Commit 9608771e authored by Malte Höck's avatar Malte Höck
Browse files

UPD: merge new_intreface into DHCPVS

parents e6aa137e d5c7bbe0
default:
image: debian-buster:latest
before_script:
## dependencies
- curl -sL https://deb.nodesource.com/setup_11.x | bash -
- apt-get install -y nodejs python3-requests
- apt-get update -y
- test -e /etc/apt/sources.list.d/nodesource.list || curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
- test -e /etc/apt/sources.list.d/nodesource.list || echo 'deb https://deb.nodesource.com/node_14.x buster main' > /etc/apt/sources.list.d/nodesource.list
- 'which npm || (apt-get update -y && apt-get install -y nodejs)'
- 'which pip3 || (apt-get update -y && apt-get install -y python3-pip)'
- pip3 install git+https://git.scc.kit.edu/scc-net/net-suite/api-generator.git#egg=net-api-generator
- cd frontend/
- cp netvs.config.js.example netvs.config.js
- npm install
- cd ..
## Deployment
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
stages:
- build
- lint
......@@ -27,7 +23,8 @@ frontend-build:
stage: build
script:
- cd frontend/
- python3 ./generate_base_model.py
- net-api-generator openapi > public/api.json
- net-api-generator es-webpack
- npm run build
artifacts:
expire_in: 7 days
......@@ -51,6 +48,13 @@ frontend-audit:
deploy_devel_devel:
stage: deploy
script:
- 'which ssh-agent || ( apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
- ssh net-suite-devel@net-web09.scc.kit.edu "${CI_PIPELINE_ID}"
environment:
name: devel-devel
......
[submodule "net_suite/modules/dhcp_leases"]
path = net_suite/modules/dhcp_leases
url = git@git.scc.kit.edu:scc-net/net-suite/net-suite-dhcp_leases.git
[submodule "net_suite/modules/dnsvs"]
path = net_suite/modules/dnsvs
url = git@git.scc.kit.edu:scc-net/net-suite/net-suite-dnsvs.git
[submodule "net_suite/modules/macfinder"]
path = net_suite/modules/macfinder
url = git@git.scc.kit.edu:scc-net/net-suite/net-suite-macfinder.git
......
import json
import re
import requests
import os
AUTH = os.getenv('NETDB_AUTH')
API_SCHEME = 'https'
API_HOST = 'www-net-devel.scc.kit.edu'
API_VERSION = '3.0'
API_BASE_URL = f'/api/{API_VERSION}'
BASE_DIR = 'src/api-services.gen/'
SWAGGER_TARGET_FILE = 'public/api.json'
swagger = {'swagger': '2.0',
'info': {
'description': """
Die Schnittstelle WebAPI bietet IT-Betreuern am KIT die Möglichkeit, eigene, speziell zugeschnittene Anwendungsprogramme für die Pflege ihrer netzwerk-spezifischen Anwendungsdaten (wie z.B. DNS) aufzubauen.
Die Schnittstelle setzt auf der Netzdatenbank des SCC (NetDB) auf und ist für eine vollautomatisierte Nutzung auf Basis des heutzutage üblichen textbasierten Datenaustauschformates JSON (JavaScript Object Notation) vorgesehen (s.a. http://de.wikipedia.org/wiki/JavaScript_Object_Notation).
Sie ist über HTTPS erreichbar und kann dadurch mit jeder beliebigen HTTP-Programmbibliothek benutzt werden. Im Rahmen der implementierten Systeme (Anwendungsbereiche), Objekttypen und Funktionen lassen sich sämtliche Daten durch entsprechende Dienstanforderungen (Requests) sowohl ausgeben bzw. abfragen als auch manipulieren (im Standardfall Eintragen, Ändern, Löschen). Die konkreten, jeweils versionsspezifischen Informationen und Parameter zu diesen Systemen, Objekttypen und Funktionen sind nicht Bestandteil dieser Dokumentation, sondern über selbstdokumentierende Indexabfragen der WebAPI erreichbar.""",
'version': API_VERSION,
'title': 'SCC NETDB-API'},
'externalDocs': {
'description': 'Weitere Dokumentation',
'url': f'https://www-net-devel-doku.scc.kit.edu/webapi/{API_VERSION}/intro'
},
'host': '',
'basePath': API_BASE_URL,
# 'schemes': [API_SCHEME],
'tags': [],
'paths': []
}
keyword_replacements = {'class': 'cls'}
files = {}
sess = requests.Session()
sess.headers.update({'Authorization': AUTH})
request = sess.get("{scheme}://{base_host}{base_url}/wapi/function/list".format(scheme=API_SCHEME, base_host=API_HOST,
base_url=API_BASE_URL))
if not request.status_code == 200:
print("Fail! Unexpected status_code", request.status_code)
print("Is the NETDB_AUTH environment variable correct?")
exit(1)
func_index = request.json()[0]
data_types = {k['name']: k for k in sess.get(
"{scheme}://{base_host}{base_url}/cntl/data_type/list".format(scheme=API_SCHEME, base_host=API_HOST,
base_url=API_BASE_URL)).json()[0]}
for f in func_index:
fname = '{system}.{object_type}.js'.format(system=f['system'], object_type=f['object_type'])
f_handle = files.get(fname, None)
if fname not in files:
files[fname] = open(os.path.join(BASE_DIR, fname), 'w')
f_handle = files[fname]
f_handle.write("""// -- AUTOGENERATED --
// eslint-disable-next-line no-unused-vars
import APIUtils from '@/util/apiutil'
import Axios from 'axios';
import NETVSConfig from "@/../netvs.config";
export default {
""")
params_str = ''
params_dict = '{'
params_dict_new = '{'
params_dict_old = '{'
for (p, v) in f['parameters'].items():
old_def_def = False
new_def_def = False
old_default = None
new_default = None
p_esc = p
for (unesc, esc) in keyword_replacements.items():
p_esc = p_esc.replace(unesc, esc)
if 'old' in v:
old_def_def = 'dataDefault' in v['old']
if old_def_def:
old_default = v['old']['dataDefault']
if 'new' in v:
new_def_def = 'dataDefault' in v['new']
if new_def_def:
new_default = v['new']['dataDefault']
v['dataType'] = data_types[v['dataType']]
if v['dataType']['json_name'] == 'string':
if old_default is not None:
old_default = '"{}"'.format(old_default)
if new_default is not None:
new_default = '"{}"'.format(new_default)
if 'old' in v:
if not f['is_data_manipulating'] and v['dataType']['json_name'] == 'array':
params_dict_old += '\'{p}\': ({p_esc}_old===null)?null:JSON.stringify({p}_old), '.format(p_esc=p, p=p)
else:
params_dict_old += '\'{p}\': {p_esc}_old, '.format(p=p, p_esc=p_esc)
if old_def_def:
params_str += '{p_esc}_old={d}, '.format(p_esc=p_esc, p=p, d=old_default)
else:
params_str += '{p_esc}_old, '.format(p_esc=p_esc, p=p)
if 'new' in v:
if new_def_def:
params_str += '{p}_new={d}, '.format(p=p, d=new_default)
else:
params_str += '{p}_new, '.format(p=p)
params_dict_new += '\'{p}\': {p}_new, '.format(p=p)
params_dict_new += '}'
params_dict_old += '}'
if not f['is_data_manipulating']:
params_dict_old = params_dict_old.replace('_old', '')
params_str = params_str.replace('_old', '')
params_dict += '"new": '
params_dict += params_dict_new
params_dict += ', '
params_dict += '"old": '
params_dict += params_dict_old
params_dict += '}'
params_str = params_str[:-2]
params_str = params_str.replace('None', 'null')
func_str = None
if f['is_data_manipulating']:
func_str = """
{name}(config, {{{params}}}) {{
let params = {params_dict}
// TODO: Return ta-object instead
return Axios.post(`${{NETVSConfig.NETDB_API_BASE_URL}}/{fq_name_slash}`, params, config)
}},
"""
else:
func_str = """
{name}(config, {{{params}}}) {{
let params = APIUtils.denullify_dict({params_dict_old})
let cnf = {{}}
Object.assign(cnf, config)
cnf['params'] = params
return Axios.get(`${{NETVSConfig.NETDB_API_BASE_URL}}/{fq_name_slash}`, cnf)
}},
"""
f_handle.write(
func_str.format(params_dict_old=params_dict_old, params_dict=params_dict, name=f['name'], params=params_str,
fq_name_slash=f['fq_name'].replace('.', '/')))
for f in files.values():
f.write("""}
""")
f.close()
def setDef(tmp):
res = []
for r in tmp:
if 'schema' in r:
if not r['schema']['has_def']:
del (r['schema']['default'])
del (r['schema']['has_def'])
res.append(r)
else:
if not r['has_def']:
del (r['default'])
del (r['has_def'])
res.append(r)
return res
def gen_param_defs(d, scope):
defs = {
'type': d['dataType']['json_name'],
'description': d['description'],
'required': d[scope]['isRequired'],
'nullable': d[scope]['isNullable'],
'has_def': 'dataDefault' in d[scope],
'default': d[scope].get('dataDefault', None)
}
if d['dataType']['json_name'] == 'array':
defs['items'] = {'type': 'string'}
return defs
def generateParamters(f):
if not f['is_data_manipulating']:
tmp = [
{
'name': p, 'description': d['description'],
'required': d['old']['isRequired'] if 'old' in d else d['new']['isRequired'],
'schema': {
'type': d['dataType']['json_name'],
'nullable': d['old']['isNullable'],
'has_def': 'dataDefault' in d['old'],
'default': d['old'].get('dataDefault', None)
},
'in': 'query'
}
for p, d in f['parameters'].items()]
return setDef(tmp)
new_params = {
p: gen_param_defs(d, 'new')
for p, d in f['parameters'].items() if 'new' in d
}
old_params = {
p: gen_param_defs(d, 'old')
for p, d in f['parameters'].items() if 'old' in d
}
old_params = {k: setDef([v])[0] for k, v in old_params.items()}
new_params = {k: setDef([v])[0] for k, v in new_params.items()}
props = {}
if len(old_params) > 0:
props['old'] = {
'description': 'Alte Attribute zur einduetigen Identifizierung des Objekts',
'type': 'object',
'properties': old_params,
}
if len(new_params) > 0:
props['new'] = {
'description': 'Neue Angaben',
'type': 'object',
'properties': new_params,
}
return [{
'name': 'body',
'in': 'body',
'required': True,
'schema': {
'type': 'object',
'properties': props
}
}]
systems = sess.get(
"{scheme}://{base_host}{base_url}/".format(scheme=API_SCHEME, base_host=API_HOST, base_url=API_BASE_URL))
objects = sess.get(
"{scheme}://{base_host}{base_url}/wapi/object_type/list".format(scheme=API_SCHEME, base_host=API_HOST,
base_url=API_BASE_URL)).json()[0]
swagger['tags'] = systems.json()[0]
swagger['securityDefinitions'] = {'api_key': {'type': 'apiKey', 'name': 'Authorization', 'in': 'header'}}
swagger['paths'] = {f"/{f['system']}/{f['object_type']}/{f['name']}":
{
'post' if f['is_data_manipulating'] else 'get': {
'parameters': generateParamters(f),
'produces': ['application/json'] if f['is_returning'] else [],
'tags': [f['system']],
'security': [{'api_key': []}],
'responses': {
200: {
'description': 'Request erfolgreich',
'schema': {
'type': 'array',
'items': {
'$ref': '#/definitions/' + f['system'] + '.' + f['object_type']
}
} if f['is_returning'] else None
},
400: {
'description': 'Eingabefehler',
},
401: {
'description': 'Unautorisiert',
},
500: {
'description': 'Interner Serverfehler',
},
}}}
for f in func_index
}
def renderDataType(d):
dat = {'type': d['dataType']['json_name'],
'description': d['description'],
}
if d['dataType']['format_literal'] is not None and d['dataType']['json_name'] == 'string':
dat['format'] = d['dataType']['format_literal']
if d['dataType']['name'] == 'text_array':
dat['items'] = {
'type': 'string'
}
if re.match(r'^integer.*_array$', d['dataType']['name']) is not None:
dat['items'] = {
'type': 'integer'
}
return dat
for o in objects:
for a,d in o['attributes'].items():
d['dataType'] = data_types[d['dataType']]
swagger['definitions'] = {o['fq_name']:
{
'type': 'object',
'description': o['description'],
'properties': {
a: renderDataType(d)
for a, d in o['attributes'].items()
}
}
for o in objects
}
with open(SWAGGER_TARGET_FILE, 'w') as f:
f.write(json.dumps(swagger))
export default {
NETDB_API_BASE_URL: "/api/3.0/"
NETDB_API_BASE_URL: "/api/3.0/",
ENABLE_OIC: true,
}
This diff is collapsed.
......@@ -8,42 +8,48 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.14",
"@fortawesome/free-regular-svg-icons": "^5.7.1",
"@fortawesome/free-solid-svg-icons": "^5.7.1",
"@fortawesome/vue-fontawesome": "^0.1.5",
"ajv": "^6.8.1",
"axios": "^0.19.0",
"bootstrap": "^4.3.1",
"bootstrap-vue": "^2.1.0",
"http-proxy-middleware": "^0.19.1",
"@fortawesome/fontawesome-svg-core": "^1.2.30",
"@fortawesome/free-brands-svg-icons": "^5.14.0",
"@fortawesome/free-regular-svg-icons": "^5.14.0",
"@fortawesome/free-solid-svg-icons": "^5.14.0",
"@fortawesome/vue-fontawesome": "^0.1.10",
"ajv": "^6.12.4",
"axios": "^0.19.2",
"bootstrap": "^4.5.2",
"bootstrap-vue": "^2.16.0",
"http-proxy-middleware": "^0.19.2",
"i": "^0.3.6",
"node-sass": "^4.13.1",
"npm": "^6.13.6",
"popper.js": "^1.14.7",
"sass-loader": "^7.1.0",
"swagger-ui": "^3.24.3",
"leaflet": "^1.6.0",
"node-sass": "^4.14.1",
"npm": "^6.14.8",
"popper.js": "^1.16.1",
"sass-loader": "^7.3.1",
"swagger-ui": "^3.32.4",
"uuid": "^8.3.0",
"v-debounce": "^0.1.2",
"vue": "^2.6.0",
"vue-awesome": "^3.4.0",
"vue-axios": "^2.1.4",
"vue-ctk-date-time-picker": "^2.4.0",
"vue-router": "^3.0.1",
"vue-virtual-scroller": "^1.0.0-beta.4",
"vuex": "^3.1.1"
"vue": "^2.6.12",
"vue-awesome": "^3.5.4",
"vue-axios": "^2.1.5",
"vue-ctk-date-time-picker": "^2.5.0",
"vue-router": "^3.4.3",
"vue-virtual-scroller": "^1.0.10",
"vue2-leaflet": "^2.5.2",
"vuedraggable": "^2.24.1",
"vuex": "^3.5.1",
"vuex-shared-mutations": "^1.0.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.2.0",
"@vue/cli-plugin-eslint": "^3.2.0",
"@vue/cli-service": "^3.7.0",
"babel-eslint": "^10.0.1",
"css-loader": "^2.1.0",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0-0",
"@vue/cli-plugin-babel": "^3.12.1",
"@vue/cli-plugin-eslint": "^4.5.4",
"@vue/cli-service": "^4.5.4",
"babel-eslint": "^10.1.0",
"css-loader": "^2.1.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.2.3",
"style-loader": "^0.23.1",
"vue-loader": "^15.6.2",
"vue-template-compiler": "^2.6.0",
"webpack-cli": "^3.2.3"
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"webpack-cli": "^3.3.12"
},
"eslintConfig": {
"root": true,
......
<template>
<div id="app">
<b-navbar toggleable="lg" fixed="top" :type="$sysinfo.host_oper_mode.is_prod?'light':'dark'" :variant="$sysinfo.host_oper_mode.is_prod?'light':'dark'" :class="`bg-${$sysinfo.host_oper_mode.mode}`">
<!-- Brand and toggle get grouped for better mobile display -->
<b-navbar-brand to="/">
<img class="d-inline-block align-top scc-img" alt="SCC" src="@/assets/img/scc_logo_small.png"/>
<span v-if="!$sysinfo.host_oper_mode.is_prod"> Instanz: {{$sysinfo.host_oper_mode.mode}}</span>
</b-navbar-brand>
<b-navbar-toggle target="navbarNavDropdown">
<font-awesome-icon icon="bars"></font-awesome-icon>
</b-navbar-toggle>
<!-- Collect the nav links, forms, and other content for toggling -->
<b-collapse is-nav id="navbarNavDropdown">
<b-navbar-nav>
<b-nav-item v-if="$sysinfo_mods_by_name['dnsvs']" to="/dnsvs/" :active="$route.path.startsWith('/dnsvs/')">DNSVS</b-nav-item>
<b-nav-item to="/dhcpvs/" :active="$route.path.startsWith('/dhcpvs/')">DHCPVS</b-nav-item>
<b-nav-item v-if="$sysinfo_mods_by_name['dhcp_leases']" to="/dhcp-leases/" :active="$route.path.startsWith('/dhcp-leases/')">DHCP-Leases</b-nav-item>
<b-nav-item href="https://www-net.scc.kit.edu/~netadmin/natvs/user/wrapper.cgi/" target="_blank">NATVS+</b-nav-item>
<b-nav-item href="https://www-net.scc.kit.edu/~netadmin/netdoc/user/wrapper.cgi/" target="_blank">NETDOC</b-nav-item>
</b-navbar-nav>
<b-navbar-nav class="ml-auto">
<b-nav-form>
<div class="input-group mr-sm-2">
<div id="scope_filter_display" class="input-group-prepend">
<span class="input-group-text">DNSVS</span>
</div>
<input class="form-control" type="search" id="nav-search" name="search"
placeholder="Suchbegriff"
aria-label="Search">
<div class="input-group-append">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Suchen</button>
</div>
</div>
</b-nav-form>
<b-nav-item><span class="d-none d-xl-inline">Erweiterte </span>Suche</b-nav-item>
<b-nav-item>
<i class="fas fa-tasks" aria-hidden="true"></i> Transaktion <span
class="badge badge-pill badge-success"
id="trans_count">0</span>
</b-nav-item>
<b-nav-item-dropdown v-if="$store.state.user" right :toggle-class="$route.path.startsWith('/user/')?'active':''">
<template slot="button-content">
<font-awesome-icon :icon="['far', 'user']"></font-awesome-icon>
<span v-if="$store.state.user"> {{$store.state.user.login_name}}</span>
</template>
<template v-if="$store.state.user">
<b-dropdown-item to="/user/tokens" :active="$route.path === '/user/tokens'"><font-awesome-icon icon="code"></font-awesome-icon> API-Tokens</b-dropdown-item>
<b-dropdown-divider></b-dropdown-divider>
<b-dropdown-item @click="logout()">Abmelden</b-dropdown-item>
</template>
</b-nav-item-dropdown>
<b-nav-item v-else>
<font-awesome-icon :icon="['far', 'user']"></font-awesome-icon>
<span> Anmelden</span>
</b-nav-item>
</b-navbar-nav>
</b-collapse><!-- /.navbar-collapse -->
</b-navbar>
<div id="wrapper">
<Navbar/>
<div id="wrapper" :class="$store.state.show_sidebar ? 'squished' : null">
<div id="page-content-wrapper">
<div class="container">
<b-breadcrumb :items="breadcrumbs"/>
<router-view/>
<footer>
<hr>
<p class="pull-right d-print-none">
<a target="_blank" href="https://www-net-doku.scc.kit.edu/webapi/release/dnscfg-perms/"><font-awesome-icon :icon="['far', 'life-ring']"></font-awesome-icon> Hilfe/Dokumentation</a> |
<b-link to="/swagger"><font-awesome-icon icon="code"></font-awesome-icon> API</b-link> |
<a target="_blank" href="https://git.scc.kit.edu/scc-net/net-suite/net-suite/issues"><font-awesome-icon icon="bug"></font-awesome-icon> Fehler melden</a> |
Kontakt: <a href="mailto:dns-betrieb@scc.kit.edu">dns-betrieb∂scc.kit.edu</a>
</p>
<p class="pull-left">Instanz: {{$sysinfo.host_oper_mode.mode}} | <b-link v-b-modal.net-suite-version>Version</b-link></p>
<b-modal class="text-center" id="net-suite-version" hide-footer hide-header>
<img alt="SCC" src="@/assets/img/scc_logo_small.png"/>
<h3>NET-Suite</h3>
<p>
Version <a :href="`https://git.scc.kit.edu/scc-net/net-suite/net-suite/commit/${$sysinfo.version}`" target="_blank">{{$sysinfo.version}}</a>
</p>
Module:
<ul>
<li v-for="m in $sysinfo.mods" :key="m.mod_path">MOD_{{m.mod_path}}:
<a target="_blank" :href="`${m.gitlab_url}/commit/${m.version}`">{{m.version}}</a></li>
</ul>
</b-modal>
</footer>
<router-view v-bind:key="$store.state.reload_count"/>
<Footer/>
</div>
</div>
<b-card id="sidebar-wrapper" no-body :class="$store.state.show_sidebar ? 'hide' : null">
<Sidebar/>
</b-card>
</div>
</div>
</template>
<style lang="scss">
@import '../node_modules/bootstrap/scss/bootstrap.scss';
@import '../node_modules/bootstrap-vue/dist/bootstrap-vue.css';
@import 'assets/css/net-suite.css';
@import 'assets/css/net-suite-devel.css';
@import 'assets/css/net-suite-test.css';
</style>
<script>
import LoginService from '@/api-services/login.service'
import Navbar from "./components/Navbar";
import Footer from "./components/Footer";
import Sidebar from "./components/Sidebar";
export default {
components: {Sidebar, Footer, Navbar},
data() {
return {
breadcrumbs: [],
breadcrumbs: []
}
},
watch: {
$route() {
var m = this.$router.currentRoute.matched
let m = this.$router.currentRoute.matched
m = m[m.length - 1]
this.breadcrumbs = this.gen_bread(m)
this.breadcrumbs = this.getBreadcrumbs(m)
}
},
methods: {
gen_bread(m) {
var res = [{text: m.meta.resolveName(this.$route.params), path: m.path}]
getBreadcrumbs(m) {