diff options
Diffstat (limited to 'onap-client/onap_client/engine.py')
-rw-r--r-- | onap-client/onap_client/engine.py | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/onap-client/onap_client/engine.py b/onap-client/onap_client/engine.py new file mode 100644 index 0000000..7543783 --- /dev/null +++ b/onap-client/onap_client/engine.py @@ -0,0 +1,216 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import argparse +import json + +from onap_client.resource import Resource +from onap_client.config import LOG as logger +from onap_client.client.clients import import_submodules +from onap_client.exceptions import InvalidSpecException, ResourceTypeNotFoundException + + +def dumper(obj): + try: + return obj.toJSON() + except: # noqa: E722 + return str(obj) + + +def list_spec_resources(): + for subclass in Resource.__subclasses__(): + print(subclass.resource_name) + + +def show_resource_spec(resource_name): + for subclass in Resource.__subclasses__(): + if resource_name == subclass.resource_name: + print(json.dumps(subclass.spec, default=dumper, indent=4)) + return subclass.spec + + print( + "Resource {} not found. This is the list of available resources:".format( + resource_name + ) + ) + list_spec_resources() + + +def load_spec(input_spec, validate_only=False): + try: + with open(input_spec, "r") as f: + jdata = json.loads(f.read()) + except json.decoder.JSONDecodeError: + print("{} is not valid json, exiting...".format(input_spec)) + raise + + engine = SpecEngine() + return engine.load_spec(jdata, validate_only=validate_only) + + +def spec_cli(args): + parser = argparse.ArgumentParser(description="Spec Engine CLI") + + parser.add_argument( + "--load-spec", + required=False, + help="Load a local spec file into the ONAP client spec engine.", + ) + + parser.add_argument( + "--validate-spec", + required=False, + help="Validates a local spec file for the spec engine.", + ) + + parser.add_argument( + "--show-resource-spec", required=False, help="Show spec for a given resource." + ) + + parser.add_argument( + "--list-spec-resources", + action="store_true", + required=False, + help="List available spec resources.", + ) + + arguments = parser.parse_args(args) + + if arguments.list_spec_resources: + list_spec_resources() + elif arguments.show_resource_spec: + show_resource_spec(arguments.show_resource_spec) + elif arguments.validate_spec: + print(json.dumps(load_spec(arguments.validate_spec, validate_only=True), indent=4)) + elif arguments.load_spec: + load_spec(arguments.load_spec) + + +class SpecEngine: + def __init__(self): + self.initialize() + self.spec = {} + + def initialize(self): + import_submodules("onap_client") + + def load_spec(self, spec, distribute=True, validate_only=False): + # print("loading spec {}".format(spec)) + self.spec = resolve_spec(spec) + self.validate(self.spec.get("spec", {})) + + if not validate_only: + self._create(self.spec.get("spec", {}), distribute) + + return self.spec + + def validate(self, spec): + if not isinstance(spec, list): + raise InvalidSpecException( + "Input spec to spec engine must be a list, but is type {}".format( + type(spec) + ) + ) + + for item_spec in spec: + if not isinstance(item_spec, dict): + raise InvalidSpecException( + "Items in input spec to engine must be dict, but is type {}".format( + type(item_spec) + ) + ) + resource_type = item_spec.get("type") + if not resource_type: + raise InvalidSpecException( + "Items in input spec must contain key/value item for 'type:'" + ) + resource_spec = item_spec.get("resource_spec") + if not resource_spec: + raise InvalidSpecException( + "Items in input spec must contain key/value item for 'resource_spec:'" + ) + subclass = get_resource_subclass(resource_type) + if not subclass: + raise ResourceTypeNotFoundException( + "Resource type {} was not found".format(resource_type) + ) + subclass.validate(resource_spec) + + def _create(self, spec, distribute): + full_engine_spec = [] + for item_spec in spec: + resource_type = item_spec.get("type") + resource_spec = item_spec.get("resource_spec") + subclass = get_resource_subclass(resource_type) + if not subclass: + raise ResourceTypeNotFoundException( + "Resource type {} was not found".format(resource_type) + ) + full_spec = subclass.validate(resource_spec) + logger.debug(json.dumps(full_spec, indent=4)) + subclass.create_from_spec(full_spec, submit=distribute) + full_engine_spec.append({"type": resource_type, "resource_spec": full_spec}) + + logger.info(json.dumps(full_engine_spec, indent=4)) + + +def resolve_spec(spec_dict): + specs = spec_dict.get("spec") + parameters = spec_dict.get("parameters", {}) + + for param_name, param_val in parameters.items(): + specs = replace(specs, "{{{{{}}}}}".format(param_name), param_val) + + spec_dict["spec"] = specs + return spec_dict + + +def replace(data, match, repl): + if isinstance(data, dict): + return {k: replace(v, match, repl) for k, v in data.items()} + elif isinstance(data, list): + return [replace(i, match, repl) for i in data] + else: + return repl if data == match else data + + +def get_resource_subclass(subclass_name): + for subclass in Resource.__subclasses__(): + if subclass.resource_name == subclass_name: + return subclass + + return None |