X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FUI.git;a=blobdiff_plain;f=skyquake%2Fplugins%2Fcomposer%2Fsrc%2Fschemas%2Fyang%2Fconfd2model.js;fp=skyquake%2Fplugins%2Fcomposer%2Fsrc%2Fschemas%2Fyang%2Fconfd2model.js;h=ecaddf45eb06ad9a93e212abd390abe29597d082;hp=0000000000000000000000000000000000000000;hb=e29efc315df33d546237e270470916e26df391d6;hpb=9c5e457509ba5a1822c316635c6308874e61b4b9 diff --git a/skyquake/plugins/composer/src/schemas/yang/confd2model.js b/skyquake/plugins/composer/src/schemas/yang/confd2model.js new file mode 100644 index 000000000..ecaddf45e --- /dev/null +++ b/skyquake/plugins/composer/src/schemas/yang/confd2model.js @@ -0,0 +1,245 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +'use strict'; + +// the models to be transformed into the output DSL JSON meta file +var yang = [require('./json-nsd.json'), require('./json-vnfd.json')]; + +var _ = require('lodash'); +var inet = require('./ietf-inet-types.yang.json'); + +var utils = { + resolvePath(obj, path) { + // supports a.b, a[1] and foo[bar], etc. + // where obj is ['nope', 'yes', {a: {b: 1}, foo: 2}] + // then [1] returns 'yes'; [2].a.b returns 1; [2].a[foo] returns 2; + path = path.split(/[\.\[\]]/).filter(d => d); + return path.reduce((r, p) => { + if (r) { + return r[p]; + } + }, obj); + }, + assignPathValue(obj, path, value) { + path = path.split(/[\.\[\]]/).filter(d => d); + // enable look-ahead to determine if type is array or object + const pathCopy = path.slice(); + // last item in path used to assign value on the resolved object + const name = path.pop(); + const resolvedObj = path.reduce((r, p, i) => { + if (typeof(r[p]) !== 'object') { + // look-ahead to see if next path item is a number + const isArray = !isNaN(parseInt(pathCopy[i + 1], 10)); + r[p] = isArray ? [] : {} + } + return r[p]; + }, obj); + resolvedObj[name] = value; + } +}; + +var isType = d => /^(leaf|leaf-list|list|container|choice|case|uses)$/.test(d); + +function deriveCardinalityFromProperty(property, typeName) { + if (String(property.mandatory) === 'true') { + return '1'; + } + let min = 0, max = Infinity; + if (property.hasOwnProperty('min-elements')) { + min = parseInt(property['min-elements'], 10) || 0; + } + if (property.hasOwnProperty('max-elements')) { + max = parseInt(property['max-elements'], 10) || Infinity; + } else { + if (!/^(list|leaf-list)$/.test(typeName)) { + max = '1'; + } + } + if (min > max) { + return String(min); + } + if (min === max) { + return String(min); + } + return String(min) + '..' + (max === Infinity ? 'N' : max); +} + +function cleanWhitespace(text) { + if (typeof text === 'string') { + return text.replace(/\s+/g, ' '); + } + return text; +} + +function buildProperties(typeData, typeName) { + var properties = []; + Object.keys(typeData).forEach(name => { + var property = typeData[name]; + var listKey = typeName === 'list' ? String(property.key).split(/\s/).filter(k => k && k !== 'undefined') : false; + var meta = { + name: name, + type: typeName, + description: cleanWhitespace(property.description), + cardinality: deriveCardinalityFromProperty(property, typeName), + 'data-type': property.type, + properties: Object.keys(property).filter(isType).reduce((r, childType) => { + return r.concat(buildProperties(property[childType], childType)); + }, []) + }; + if (listKey) { + meta.key = listKey; + } + properties.push(meta); + }); + return properties; +} + +function lookupUses(uses, yang) { + function doLookup(lookupTypeName) { + var key; + // warn: hardcoded prefix support for mano-types - other prefixes will be ignored + if (/^manotypes:/.test(lookupTypeName)) { + var moduleName = lookupTypeName.split(':')[1]; + key = ['dependencies.mano-types.module.mano-types.grouping', moduleName].join('.'); + } else { + var name = yang.name.replace(/^rw-/, ''); + key = ['dependencies', name, 'module', name, 'grouping', lookupTypeName].join('.'); + } + return utils.resolvePath(yang, key); + } + if (typeof uses === 'object') { + return Object.keys(uses).reduce((result, key) => { + var found = doLookup(key); + Object.keys(found).filter(isType).forEach(type => { + var property = result[type] || (result[type] = {}); + Object.assign(property, found[type]); + }); + return result; + }, {}); + } else if (typeof uses === 'string') { + return doLookup(uses); + } + return {}; +} + +function lookupTypedef(property, yang) { + var key; + var lookupTypeName = property.type; + // warn: hardcoded prefix support - other prefixes will be ignored + if (/^manotypes:/.test(lookupTypeName)) { + var lookupName = lookupTypeName.split(':')[1]; + key = ['dependencies.mano-types.module.mano-types.typedef', lookupName].join('.'); + } else if (/^inet:/.test(lookupTypeName)) { + var lookupName = lookupTypeName.split(':')[1]; + yang = inet; + key = ['schema.module.ietf-inet-types.typedef', lookupName].join('.'); + } + if (key) { + return utils.resolvePath(yang, key); + } +} + +function resolveUses(property, yang) { + var childData = property.uses; + var resolved = lookupUses(childData, yang); + //console.log('uses', childData, 'found', resolved); + Object.keys(resolved).forEach(type => { + var parentTypes = property[type] || (property[type] = {}); + // copy types into the parent types bucket + Object.assign(parentTypes, resolveReferences(yang, resolved[type])); + }); + delete property.uses; +} + +function resolveTypedef(property, yang) { + if (/:/.test(property.type)) { + var found = lookupTypedef(property, yang); + if (found) { + Object.assign(property, found); + } + } +} + +function resolveReferences(yang, data) { + var dataClone = _.cloneDeep(data); + function doResolve(typeData) { + Object.keys(typeData).forEach(name => { + var property = typeData[name]; + resolveTypedef(property, yang); + Object.keys(property).filter(isType).forEach(childType => { + if (childType === 'uses') { + resolveUses(property, yang); + } else { + doResolve(property[childType]); + } + }); + }); + } + doResolve(dataClone); + return dataClone; +} + +function module(yang) { + let module; + var name = yang.name.replace(/^rw-/, ''); + if (!name) { + throw 'no name given in json yang'; + } + const path = ['container', name + '-catalog'].join('.'); + module = utils.resolvePath(yang, path); + + if (!module) { + module = utils.resolvePath(yang, ['schema', 'module', name, path].join('.')); + } + if (!module) { + module = utils.resolvePath(yang, ['dependencies', name, 'module', name, path].join('.')); + } + if (!module) { + throw 'cannot find the module' + name; + } + + // module/agument/nsd:nsd-catalog/nsd:nsd/meta + const augLeafPath = ['schema.module', 'rw-' + name, 'augment', '/' + name + ':' + name + '-catalog/' + name + ':' + name, 'leaf']; + const meta = utils.resolvePath(yang, augLeafPath.concat('meta').join('.')); + + const putLeafPath = ['dependencies', name, 'module', name, path, 'list', name, 'leaf']; + + if (meta) { + utils.assignPathValue(yang, putLeafPath.concat(['meta']).join('.'), meta); + } + + // module/agument/nsd:nsd-catalog/nsd:nsd/logo + const logo = utils.resolvePath(yang, augLeafPath.concat('logo').join('.')); + if (logo) { + utils.assignPathValue(yang, putLeafPath.concat(['logo']).join('.'), logo); + } + var data = module.list; + + return {name: name, data: resolveReferences(yang, data)}; + +} + +function reduceModule(result, module) { + result[module.name] = buildProperties(module.data, 'list')[0]; + return result; +} + +var result = yang.map(module).reduce(reduceModule, {}); + +console.log(JSON.stringify(result, null, 5));