From: Eduardo Sousa Date: Wed, 20 Mar 2019 13:36:24 +0000 (+0000) Subject: Adding setup.py to charm-generator X-Git-Tag: v6.0.0~30 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=refs%2Fchanges%2F38%2F7338%2F1;p=osm%2Fdevops.git Adding setup.py to charm-generator Change-Id: I1ba69e255727703ed2f25b4f3dbabd53685573f4 Signed-off-by: Eduardo Sousa --- diff --git a/descriptor-packages/tools/charm-generator/actions/__init__.py b/descriptor-packages/tools/charm-generator/actions/__init__.py deleted file mode 100644 index e584ee55..00000000 --- a/descriptor-packages/tools/charm-generator/actions/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com -## diff --git a/descriptor-packages/tools/charm-generator/actions/templates/action.j2 b/descriptor-packages/tools/charm-generator/actions/templates/action.j2 deleted file mode 100644 index c1727c63..00000000 --- a/descriptor-packages/tools/charm-generator/actions/templates/action.j2 +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -{#- -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com --#} -{%- if license is defined %} -# Copyright {{ license.year }} {{ license.company }} -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: {{ license.email }} -{%- endif %} - -import sys -sys.path.append('lib') - -from charms.reactive import main -from charms.reactive import set_state -from charmhelpers.core.hookenv import action_fail, action_name - -""" -`set_state` only works here because it's flushed to disk inside the `main()` -loop. remove_state will need to be called inside the action method. -""" -set_state('actions.{}'.format(action_name())) - -try: - main() -except Exception as e: - action_fail(repr(e)) - diff --git a/descriptor-packages/tools/charm-generator/actions/templates/actions.yaml.j2 b/descriptor-packages/tools/charm-generator/actions/templates/actions.yaml.j2 deleted file mode 100644 index e9f29614..00000000 --- a/descriptor-packages/tools/charm-generator/actions/templates/actions.yaml.j2 +++ /dev/null @@ -1,44 +0,0 @@ -{#- -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com --#} -{%- if license is defined -%} -# Copyright {{ license.year }} {{ license.company }} -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: {{ license.email }} -{% endif %} - -{%- for act in actions %} -{{ act.action_name }}: - description: "Insert description for this playbook." - {#- TODO: Insert parameters -#} - {#- TODO: Insert required -#} -{% endfor %} - diff --git a/descriptor-packages/tools/charm-generator/ansible-charm/__init__.py b/descriptor-packages/tools/charm-generator/ansible-charm/__init__.py deleted file mode 100644 index e584ee55..00000000 --- a/descriptor-packages/tools/charm-generator/ansible-charm/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com -## diff --git a/descriptor-packages/tools/charm-generator/ansible-charm/templates/ansible_charm.py.j2 b/descriptor-packages/tools/charm-generator/ansible-charm/templates/ansible_charm.py.j2 deleted file mode 100644 index d9e2f551..00000000 --- a/descriptor-packages/tools/charm-generator/ansible-charm/templates/ansible_charm.py.j2 +++ /dev/null @@ -1,84 +0,0 @@ -{#- -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com --#} -{%- if license is defined -%} -# Copyright {{ license.year }} {{ license.company }} -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: {{ license.email }} -{%- endif %} - - -import sys -import traceback - -from charmhelpers.core.hookenv import ( - action_get, - action_fail, - action_set, - config, - status_set, -) - -import charms.libansible - -from charms.reactive import ( - remove_state as remove_flag, - set_state as set_flag, - when, -) - - -# Sets the status of the charm to show in OSM: configured -@when('config.changed') -def config_changed(): - set_flag('{{ charm_name }}.configured') - status_set('active', 'ready!') - return - - -# Edits ansible config files and executes ansible-playbook -{% for pb in playbooks -%} -@when('{{ charm_name }}.configured') -@when('actions.{{ pb.action_name }}') -def {{ pb.function_name }}(): - try: - result = charms.libansible.execute_playbook('{{ pb.file }}') - except: - exc_type, exc_value, exc_traceback = sys.exc_info() - err = traceback.format_exception(exc_type, exc_value, exc_traceback) - action_fail('{{ pb.action_name }} failed: ' + str(err)) - else: - action_set({'output': result}) - finally: - remove_flag('actions.{{ pb.action_name }}') - - -{% endfor -%} \ No newline at end of file diff --git a/descriptor-packages/tools/charm-generator/ansible-charm/templates/ansible_lib.py.j2 b/descriptor-packages/tools/charm-generator/ansible-charm/templates/ansible_lib.py.j2 deleted file mode 100644 index 109bae05..00000000 --- a/descriptor-packages/tools/charm-generator/ansible-charm/templates/ansible_lib.py.j2 +++ /dev/null @@ -1,93 +0,0 @@ -{#- -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com --#} -{%- if license is defined -%} -# Copyright {{ license.year }} {{ license.company }} -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: {{ license.email }} -{%- endif %} - -import fnmatch -import os -import yaml -import subprocess - -from charmhelpers.core.hookenv import config - - -def create_hosts(cfg, hosts): - inventory_path = '/etc/ansible/hosts' - - with open(inventory_path, 'w') as f: - f.write('[{}]\n'.format(hosts)) - h1 = '{0} ansible_connection=ssh ansible_ssh_user={1} ansible_ssh_pass={2} ' \ - 'ansible_python_interpreter=/usr/bin/python3\n'.format(cfg['ssh-hostname'], cfg['ssh-username'], - cfg['ssh-password']) - f.write(h1) - - -def create_ansible_cfg(): - ansible_config_path = '/etc/ansible/ansible.cfg' - - with open(ansible_config_path, 'w') as f: - f.write('[defaults]\n') - f.write('host_key_checking = False\n') - - -# Function to find the playbook path -def find(pattern, path): - result = '' - for root, dirs, files in os.walk(path): - for name in files: - if fnmatch.fnmatch(name, pattern): - result = os.path.join(root, name) - return result - - -def execute_playbook(playbook_file, vars=None): - playbook_path = find(playbook_file, '/var/lib/juju/agents/') - - cfg = config() - - with open(playbook_path, 'r') as f: - playbook_data = yaml.load(f) - - hosts = 'all' - if 'hosts' in playbook_data[0].keys() and playbook_data[0]['hosts']: - hosts = playbook_data[0]['hosts'] - - create_ansible_cfg() - create_hosts(cfg, hosts) - - call = ['ansible-playbook', playbook_path] - result = subprocess.check_output(call) - - return result diff --git a/descriptor-packages/tools/charm-generator/ansible-charm/templates/layer.yaml.j2 b/descriptor-packages/tools/charm-generator/ansible-charm/templates/layer.yaml.j2 deleted file mode 100644 index 3b0fa839..00000000 --- a/descriptor-packages/tools/charm-generator/ansible-charm/templates/layer.yaml.j2 +++ /dev/null @@ -1,49 +0,0 @@ -{#- -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com --#} -{%- if license is defined -%} -# Copyright {{ license.year }} {{ license.company }} -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: {{ license.email }} -{%- endif %} - -includes: -{% for lr in layers -%} -- "layer:{{ lr.name }}" -{% endfor -%} -options: -{%- for lr in layers if lr.options is defined %} - {{ lr.name }}: - {%- for op in lr.options %} - {{ op.name }}: {{ op.value }} - {%- endfor -%} -{% endfor %} - diff --git a/descriptor-packages/tools/charm-generator/generator-runner.py b/descriptor-packages/tools/charm-generator/generator-runner.py new file mode 100644 index 00000000..275156b8 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator-runner.py @@ -0,0 +1,22 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## + +from generator.generator import main + +if __name__ == '__main__': + main() diff --git a/descriptor-packages/tools/charm-generator/generator.py b/descriptor-packages/tools/charm-generator/generator.py deleted file mode 100644 index 22ce1cba..00000000 --- a/descriptor-packages/tools/charm-generator/generator.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com -## - -import argparse -import logging -import sys - -from datetime import datetime - -from generators.ansible_generator import AnsibleGenerator - - -def configure_logger(args): - global logger - logger = logging.getLogger() - - if args.verbose: - logger.setLevel(logging.DEBUG) - else: - logger.setLevel(logging.INFO) - - handler = logging.StreamHandler(sys.stdout) - - if args.verbose: - handler.setLevel(logging.DEBUG) - else: - handler.setLevel(logging.INFO) - - formatter = logging.Formatter('[%(levelname)s] %(message)s') - handler.setFormatter(formatter) - logger.addHandler(handler) - - -def verify_environment(args): - pass - - -def input_processing(): - parser = argparse.ArgumentParser(description='Charm generator for OSM VNFs') - - # Setting logger from INFO to DEBUG - parser.add_argument('-v', '--verbose', required=False, action='store_true', - help='increase output verbosity') - - # Backend selection - backend_parser = parser.add_mutually_exclusive_group(required=True) - - backend_parser.add_argument('--ansible', action='store_true', - help='generate an Ansible charm') - backend_parser.add_argument('--http', action='store_true', - help='generate a HTTP charm') - backend_parser.add_argument('--sol002', action='store_true', - help='generate a SOL002 charm') - backend_parser.add_argument('--scripts', action='store_true', - help='generate a Scripts charm') - - # Metadata inputs - metadata_parser = parser.add_argument_group('metadata') - - metadata_parser.add_argument('--summary', required=False, action='store', - help='summary to be included in the metadata.yaml') - metadata_parser.add_argument('--maintainer', required=False, action='store', - help='maintainer information to be included in the metadata.yaml') - metadata_parser.add_argument('--description', required=False, action='store', - help='description to be included in the metadata.yaml') - - # License header inputs - license_header_group = parser.add_argument_group('license_header') - - license_header_group.add_argument('--company', required=False, action='store', - help='company name to be included in the license headers') - license_header_group.add_argument('--email', required=False, action='store', - help='email to be included in the license headers') - - return parser.parse_args() - - -def process_args(args): - # Metadata information for metadata.yaml - metadata = {} - - if args.summary: - metadata['summary'] = args.summary - if args.maintainer: - metadata['maintainer'] = args.maintainer - if args.description: - metadata['description'] = args.description - - # Information for license headers - license = { - 'year': datetime.now().year - } - - if args.company: - license['company'] = args.company - if args.email: - license['email'] = args.email - - # Options to configure the backends - options = { - 'backend': None - } - - if args.ansible: - options['backend'] = 'ansible' - elif args.http: - options['backend'] = 'http' - elif args.sol002: - options['backend'] = 'sol002' - elif args.scripts: - options['backend'] = 'scripts' - - return metadata, license, options - - -def main(): - # getting the input from the user - args = input_processing() - - # configure the logger - configure_logger(args) - - logger.info('Starting generation process...') - - # verify if the environment is correct and the args are valid - verify_environment(args) - - # process data to input in generator - metadata, license, options = process_args(args) - - logger.debug('Metadata: %s', metadata) - logger.debug('License: %s', license) - logger.debug('Options: %s', options) - - if options['backend'] == 'ansible': - generator = AnsibleGenerator(metadata=metadata, license=license, options=options) - elif options['backend'] == 'http': - logger.error('HTTP backend not yet available. Available backends are: ansible') - sys.exit(-1) - elif options['backend'] == 'sol002': - logger.error('SOL002 backend not yet available. Available backends are: ansible') - sys.exit(-1) - elif options['backend'] == 'scripts': - logger.error('Scripts backend not yet available. Available backends are: ansible') - sys.exit(-1) - else: - logger.error('Undefined backend for generator. Available backends are: ansible') - sys.exit(-1) - - generator.generate() - - logger.info('Generation process complete.') - - -if __name__ == "__main__": - main() diff --git a/descriptor-packages/tools/charm-generator/generator/__init__.py b/descriptor-packages/tools/charm-generator/generator/__init__.py new file mode 100644 index 00000000..e584ee55 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## diff --git a/descriptor-packages/tools/charm-generator/generator/__main__.py b/descriptor-packages/tools/charm-generator/generator/__main__.py new file mode 100644 index 00000000..01143fbb --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/__main__.py @@ -0,0 +1,21 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## + +from .generator import main + +main() diff --git a/descriptor-packages/tools/charm-generator/generator/actions/__init__.py b/descriptor-packages/tools/charm-generator/generator/actions/__init__.py new file mode 100644 index 00000000..e584ee55 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/actions/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## diff --git a/descriptor-packages/tools/charm-generator/generator/actions/templates/action.j2 b/descriptor-packages/tools/charm-generator/generator/actions/templates/action.j2 new file mode 100644 index 00000000..c1727c63 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/actions/templates/action.j2 @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +{#- +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +-#} +{%- if license is defined %} +# Copyright {{ license.year }} {{ license.company }} +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: {{ license.email }} +{%- endif %} + +import sys +sys.path.append('lib') + +from charms.reactive import main +from charms.reactive import set_state +from charmhelpers.core.hookenv import action_fail, action_name + +""" +`set_state` only works here because it's flushed to disk inside the `main()` +loop. remove_state will need to be called inside the action method. +""" +set_state('actions.{}'.format(action_name())) + +try: + main() +except Exception as e: + action_fail(repr(e)) + diff --git a/descriptor-packages/tools/charm-generator/generator/actions/templates/actions.yaml.j2 b/descriptor-packages/tools/charm-generator/generator/actions/templates/actions.yaml.j2 new file mode 100644 index 00000000..e9f29614 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/actions/templates/actions.yaml.j2 @@ -0,0 +1,44 @@ +{#- +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +-#} +{%- if license is defined -%} +# Copyright {{ license.year }} {{ license.company }} +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: {{ license.email }} +{% endif %} + +{%- for act in actions %} +{{ act.action_name }}: + description: "Insert description for this playbook." + {#- TODO: Insert parameters -#} + {#- TODO: Insert required -#} +{% endfor %} + diff --git a/descriptor-packages/tools/charm-generator/generator/ansible-charm/__init__.py b/descriptor-packages/tools/charm-generator/generator/ansible-charm/__init__.py new file mode 100644 index 00000000..e584ee55 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/ansible-charm/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## diff --git a/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/ansible_charm.py.j2 b/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/ansible_charm.py.j2 new file mode 100644 index 00000000..d9e2f551 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/ansible_charm.py.j2 @@ -0,0 +1,84 @@ +{#- +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +-#} +{%- if license is defined -%} +# Copyright {{ license.year }} {{ license.company }} +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: {{ license.email }} +{%- endif %} + + +import sys +import traceback + +from charmhelpers.core.hookenv import ( + action_get, + action_fail, + action_set, + config, + status_set, +) + +import charms.libansible + +from charms.reactive import ( + remove_state as remove_flag, + set_state as set_flag, + when, +) + + +# Sets the status of the charm to show in OSM: configured +@when('config.changed') +def config_changed(): + set_flag('{{ charm_name }}.configured') + status_set('active', 'ready!') + return + + +# Edits ansible config files and executes ansible-playbook +{% for pb in playbooks -%} +@when('{{ charm_name }}.configured') +@when('actions.{{ pb.action_name }}') +def {{ pb.function_name }}(): + try: + result = charms.libansible.execute_playbook('{{ pb.file }}') + except: + exc_type, exc_value, exc_traceback = sys.exc_info() + err = traceback.format_exception(exc_type, exc_value, exc_traceback) + action_fail('{{ pb.action_name }} failed: ' + str(err)) + else: + action_set({'output': result}) + finally: + remove_flag('actions.{{ pb.action_name }}') + + +{% endfor -%} \ No newline at end of file diff --git a/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/ansible_lib.py.j2 b/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/ansible_lib.py.j2 new file mode 100644 index 00000000..109bae05 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/ansible_lib.py.j2 @@ -0,0 +1,93 @@ +{#- +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +-#} +{%- if license is defined -%} +# Copyright {{ license.year }} {{ license.company }} +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: {{ license.email }} +{%- endif %} + +import fnmatch +import os +import yaml +import subprocess + +from charmhelpers.core.hookenv import config + + +def create_hosts(cfg, hosts): + inventory_path = '/etc/ansible/hosts' + + with open(inventory_path, 'w') as f: + f.write('[{}]\n'.format(hosts)) + h1 = '{0} ansible_connection=ssh ansible_ssh_user={1} ansible_ssh_pass={2} ' \ + 'ansible_python_interpreter=/usr/bin/python3\n'.format(cfg['ssh-hostname'], cfg['ssh-username'], + cfg['ssh-password']) + f.write(h1) + + +def create_ansible_cfg(): + ansible_config_path = '/etc/ansible/ansible.cfg' + + with open(ansible_config_path, 'w') as f: + f.write('[defaults]\n') + f.write('host_key_checking = False\n') + + +# Function to find the playbook path +def find(pattern, path): + result = '' + for root, dirs, files in os.walk(path): + for name in files: + if fnmatch.fnmatch(name, pattern): + result = os.path.join(root, name) + return result + + +def execute_playbook(playbook_file, vars=None): + playbook_path = find(playbook_file, '/var/lib/juju/agents/') + + cfg = config() + + with open(playbook_path, 'r') as f: + playbook_data = yaml.load(f) + + hosts = 'all' + if 'hosts' in playbook_data[0].keys() and playbook_data[0]['hosts']: + hosts = playbook_data[0]['hosts'] + + create_ansible_cfg() + create_hosts(cfg, hosts) + + call = ['ansible-playbook', playbook_path] + result = subprocess.check_output(call) + + return result diff --git a/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/layer.yaml.j2 b/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/layer.yaml.j2 new file mode 100644 index 00000000..3b0fa839 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/ansible-charm/templates/layer.yaml.j2 @@ -0,0 +1,49 @@ +{#- +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +-#} +{%- if license is defined -%} +# Copyright {{ license.year }} {{ license.company }} +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: {{ license.email }} +{%- endif %} + +includes: +{% for lr in layers -%} +- "layer:{{ lr.name }}" +{% endfor -%} +options: +{%- for lr in layers if lr.options is defined %} + {{ lr.name }}: + {%- for op in lr.options %} + {{ op.name }}: {{ op.value }} + {%- endfor -%} +{% endfor %} + diff --git a/descriptor-packages/tools/charm-generator/generator/generator.py b/descriptor-packages/tools/charm-generator/generator/generator.py new file mode 100644 index 00000000..0ef9d1d1 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/generator.py @@ -0,0 +1,169 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## + +__version__ = '1.0.0' + +import argparse +import logging +import sys + +from datetime import datetime + +from .generators.ansible_generator import AnsibleGenerator + + +def configure_logger(args): + global logger + logger = logging.getLogger() + + if args.verbose: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + handler = logging.StreamHandler(sys.stdout) + + if args.verbose: + handler.setLevel(logging.DEBUG) + else: + handler.setLevel(logging.INFO) + + formatter = logging.Formatter('[%(levelname)s] %(message)s') + handler.setFormatter(formatter) + logger.addHandler(handler) + + +def verify_environment(args): + pass + + +def input_processing(): + parser = argparse.ArgumentParser(description='Charm generator for OSM VNFs') + + # Setting logger from INFO to DEBUG + parser.add_argument('-v', '--verbose', required=False, action='store_true', + help='increase output verbosity') + + # Backend selection + backend_parser = parser.add_mutually_exclusive_group(required=True) + + backend_parser.add_argument('--ansible', action='store_true', + help='generate an Ansible charm') + backend_parser.add_argument('--http', action='store_true', + help='generate a HTTP charm') + backend_parser.add_argument('--sol002', action='store_true', + help='generate a SOL002 charm') + backend_parser.add_argument('--scripts', action='store_true', + help='generate a Scripts charm') + + # Metadata inputs + metadata_parser = parser.add_argument_group('metadata') + + metadata_parser.add_argument('--summary', required=False, action='store', + help='summary to be included in the metadata.yaml') + metadata_parser.add_argument('--maintainer', required=False, action='store', + help='maintainer information to be included in the metadata.yaml') + metadata_parser.add_argument('--description', required=False, action='store', + help='description to be included in the metadata.yaml') + + # License header inputs + license_header_group = parser.add_argument_group('license_header') + + license_header_group.add_argument('--company', required=False, action='store', + help='company name to be included in the license headers') + license_header_group.add_argument('--email', required=False, action='store', + help='email to be included in the license headers') + + return parser.parse_args() + + +def process_args(args): + # Metadata information for metadata.yaml + metadata = {} + + if args.summary: + metadata['summary'] = args.summary + if args.maintainer: + metadata['maintainer'] = args.maintainer + if args.description: + metadata['description'] = args.description + + # Information for license headers + license = { + 'year': datetime.now().year + } + + if args.company: + license['company'] = args.company + if args.email: + license['email'] = args.email + + # Options to configure the backends + options = { + 'backend': None + } + + if args.ansible: + options['backend'] = 'ansible' + elif args.http: + options['backend'] = 'http' + elif args.sol002: + options['backend'] = 'sol002' + elif args.scripts: + options['backend'] = 'scripts' + + return metadata, license, options + + +def main(): + # getting the input from the user + args = input_processing() + + # configure the logger + configure_logger(args) + + logger.info('Starting generation process...') + + # verify if the environment is correct and the args are valid + verify_environment(args) + + # process data to input in generator + metadata, license, options = process_args(args) + + logger.debug('Metadata: %s', metadata) + logger.debug('License: %s', license) + logger.debug('Options: %s', options) + + if options['backend'] == 'ansible': + generator = AnsibleGenerator(metadata=metadata, license=license, options=options) + elif options['backend'] == 'http': + logger.error('HTTP backend not yet available. Available backends are: ansible') + sys.exit(-1) + elif options['backend'] == 'sol002': + logger.error('SOL002 backend not yet available. Available backends are: ansible') + sys.exit(-1) + elif options['backend'] == 'scripts': + logger.error('Scripts backend not yet available. Available backends are: ansible') + sys.exit(-1) + else: + logger.error('Undefined backend for generator. Available backends are: ansible') + sys.exit(-1) + + generator.generate() + + logger.info('Generation process complete.') diff --git a/descriptor-packages/tools/charm-generator/generator/generators/__init__.py b/descriptor-packages/tools/charm-generator/generator/generators/__init__.py new file mode 100644 index 00000000..e584ee55 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/generators/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## diff --git a/descriptor-packages/tools/charm-generator/generator/generators/actions_generator.py b/descriptor-packages/tools/charm-generator/generator/generators/actions_generator.py new file mode 100644 index 00000000..b3da7c33 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/generators/actions_generator.py @@ -0,0 +1,130 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## + +import functools +import logging +import os +import stat + +from jinja2 import Environment, PackageLoader + + +class ActionsGenerator: + LOGGER = logging.getLogger() + ENV = Environment(loader=PackageLoader('generator.actions', 'templates')) + + def __init__(self, metadata, actions, license=None, options=None): + """ + Creates the object to generate the actions folder, actions files and actions.yaml. + + Usage should be: + + 1) Create the object. + 2) Run the generate method. + + :param metadata: metadata information about the charm being generated. + :param actions: actions to be included in the charm. + :param license: information license to included in the charm being generated. + :param options: options to override the normal flow. + """ + self.path = os.getcwd() + self.metadata = metadata + self.actions = actions + self.license = license + self.options = options + + def generate(self): + """ + Generates the actions folder, actions files and actions.yaml. + """ + ActionsGenerator.LOGGER.info('Generating the actions...') + + self._create_actions_folder() + + for action in self.actions: + self._generate_action_file(action) + + self._generate_actions_yaml_file() + + ActionsGenerator.LOGGER.info('Generated the actions.') + + def _create_actions_folder(self): + """ + Creates the actions folder, where all the action files are placed. + These files are the entry point for the execution of the actions. + """ + ActionsGenerator.LOGGER.debug('Creating the actions folder...') + + actions_path = self.path + '/actions' + + if not os.path.isdir(actions_path): + os.mkdir(actions_path) + else: + ActionsGenerator.LOGGER.warning('Actions folder already exists.') + return + + ActionsGenerator.LOGGER.debug('Created actions folder.') + + def _generate_action_file(self, action): + """ + Generates the action file to act as entry point for a specific action. + + Note: the action file is made executable during this function. + + :param action: dictionary with information about the action + """ + ActionsGenerator.LOGGER.debug('Creating action file: %s...', action['action_name']) + + playbook_path = self.path + ('/actions/%s' % action['action_name']) + action_file_template = ActionsGenerator.ENV.get_template('action.j2') + + with open(playbook_path, "w") as f: + f.write(action_file_template.render(license=self.license)) + mode = os.fstat(f.fileno()).st_mode + mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + os.fchmod(f.fileno(), stat.S_IMODE(mode)) + + ActionsGenerator.LOGGER.debug('Created action file: %s.', action['action_name']) + + def _generate_actions_yaml_file(self): + """ + Generates the actions.yaml file from a template. + It takes all the playbook information and fills in the templates. + + Note: renames the old actions.yaml file with a .bkp extension, so the history is preserved. + """ + ActionsGenerator.LOGGER.debug('Creating actions.yaml...') + + actions_yaml_path = self.path + '/actions.yaml' + actions_template = ActionsGenerator.ENV.get_template('actions.yaml.j2') + + if os.path.isfile(actions_yaml_path): + ids = [int(f.split('.')[-1]) for f in os.listdir(self.path) if f.startswith('actions.yaml.bkp')] + + id = 0 + if ids: + id = functools.reduce(lambda x, y: x if (x > y) else y, ids) + id += 1 + + backup_actions_yaml_path = self.path + ('/actions.yaml.bkp.%02d' % id) + os.rename(actions_yaml_path, backup_actions_yaml_path) + + with open(actions_yaml_path, 'w') as f: + f.write(actions_template.render(actions=self.actions, license=self.license)) + + ActionsGenerator.LOGGER.debug('Created actions.yaml.') diff --git a/descriptor-packages/tools/charm-generator/generator/generators/ansible_generator.py b/descriptor-packages/tools/charm-generator/generator/generators/ansible_generator.py new file mode 100644 index 00000000..cefe4226 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/generators/ansible_generator.py @@ -0,0 +1,182 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## + +import functools +import logging +import os +import sys + +from jinja2 import Environment, PackageLoader + +from generator.generators.actions_generator import ActionsGenerator +from generator.generators.metadata_generator import MetadataGenerator + + +class AnsibleGenerator: + LOGGER = logging.getLogger() + ENV = Environment(loader=PackageLoader('generator.ansible-charm', 'templates')) + + def __init__(self, metadata, license=None, options=None): + """ + Creates the object to generate the ansible charm from templates. + + Usage should be: + + 1) Create the object. + 2) Run the generate method. + + :param metadata: metadata information about the charm being generated. + :param license: information license to included in the charm being generated. + :param options: options to override the normal flow. + """ + self.path = os.getcwd() + self.metadata = metadata + self.license = license + self.options = options + self.playbooks = AnsibleGenerator._fetch_all_playbooks(self.path) + + playbooks_name = [playbook['file'] for playbook in self.playbooks] + AnsibleGenerator.LOGGER.debug('Playbooks found: %s', playbooks_name) + + self.metadata_generator = MetadataGenerator(metadata=self.metadata, license=self.license, options=self.options) + self.actions_generator = ActionsGenerator(metadata=self.metadata, actions=self.playbooks, license=self.license, + options=self.options) + + def generate(self): + """ + Generates the Ansible Charm using templates. + """ + AnsibleGenerator.LOGGER.info('Generating the Ansible Charm...') + + # Generating metadata.yaml + self.metadata_generator.generate() + self.metadata = self.metadata_generator.get_metadata() + + # Generating actions + self.actions_generator.generate() + + self._generate_ansible_lib() + self._generate_layer_yaml_file() + self._generate_reactive_file() + + AnsibleGenerator.LOGGER.info('Generated the Ansible Charm.') + + @staticmethod + def _fetch_all_playbooks(path): + """ + Walks over the playbooks directory, fetches the playbook's name. + It takes the file name and normalizes it to be used in the ansible charm. + + :param path: path of the root of the charm. + :return: a list of dictionaries with the information about the playbooks. + """ + playbooks_path = path + '/playbooks' + + if os.path.isdir(playbooks_path) and len(os.listdir(playbooks_path)) != 0: + filenames = os.listdir(playbooks_path) + + result = [] + for file in filenames: + info = { + 'action_name': file.replace('_', '-').replace('.yaml', ''), + 'function_name': file.replace('-', '_').replace('.yaml', ''), + 'file': file + } + result.append(info) + + return result + else: + AnsibleGenerator.LOGGER.error('Playbooks directory should exist and be populated.') + sys.exit(-1) + + def _generate_ansible_lib(self): + """ + Generates the ansible lib file from a template. + """ + AnsibleGenerator.LOGGER.debug('Creating ansible.py lib...') + + lib_folder_path = self.path + '/lib/charms' + ansible_lib_path = lib_folder_path + '/libansible.py' + + if not os.path.isdir(lib_folder_path): + os.makedirs(lib_folder_path) + + ansible_lib_template = AnsibleGenerator.ENV.get_template('ansible_lib.py.j2') + + with open(ansible_lib_path, 'w') as f: + f.write(ansible_lib_template.render(license=self.license)) + + AnsibleGenerator.LOGGER.debug('Created anisble.py lib.') + + def _generate_layer_yaml_file(self): + """ + Generates the layer.yaml file from a template. + + Note: disables the venv environment. + """ + AnsibleGenerator.LOGGER.debug('Creating layer.yaml...') + + layer_yaml_path = self.path + '/layer.yaml' + + layers = [{ + 'name': 'basic', + 'options': [{ + 'name': 'use_venv', + 'value': 'false' + }]}, { + 'name': 'ansible-base' + }, { + 'name': 'vnfproxy' + }] + + layer_template = AnsibleGenerator.ENV.get_template('layer.yaml.j2') + + with open(layer_yaml_path, 'w') as f: + f.write(layer_template.render(layers=layers, license=self.license)) + + AnsibleGenerator.LOGGER.debug('Created layer.yaml.') + + def _generate_reactive_file(self): + """ + Generates the Ansible reactive file from a template. + It takes all the playbook information and fills in the templates. + + Note: renames the old charm file with a .bkp extension, so the history is preserved. + """ + AnsibleGenerator.LOGGER.debug('Creating ansible charm: %s...', self.metadata['file']) + + reactive_folder_path = self.path + '/reactive' + charm_file_path = reactive_folder_path + ('/%s' % self.metadata['file']) + ansible_charm_template = AnsibleGenerator.ENV.get_template('ansible_charm.py.j2') + + ids = [int(f.split('.')[-1]) for f in os.listdir(reactive_folder_path) + if f.startswith('%s.bkp' % self.metadata['file'])] + + id = 0 + if ids: + id = functools.reduce(lambda x, y: x if (x > y) else y, ids) + id += 1 + + backup_charm_file_path = reactive_folder_path + ('/%s.bkp.%02d' % (self.metadata['file'], id)) + os.rename(charm_file_path, backup_charm_file_path) + + with open(charm_file_path, 'w') as f: + f.write(ansible_charm_template.render(charm_name=self.metadata['name'], playbooks=self.playbooks, + license=self.license)) + + AnsibleGenerator.LOGGER.debug('Created ansible charm: %s.', self.metadata['file']) diff --git a/descriptor-packages/tools/charm-generator/generator/generators/metadata_generator.py b/descriptor-packages/tools/charm-generator/generator/generators/metadata_generator.py new file mode 100644 index 00000000..6c9b000b --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/generators/metadata_generator.py @@ -0,0 +1,152 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## + +import functools +import logging +import os +import sys + +import yaml + +from jinja2 import Environment, PackageLoader + + +class MetadataGenerator: + LOGGER = logging.getLogger() + ENV = Environment(loader=PackageLoader('generator.metadata', 'templates')) + + def __init__(self, metadata, license=None, options=None): + """ + Creates the object to generate the metadata.yaml file from a template. + + Usage should be: + + 1) Create the object. + 2) Run the generate method. + + :param metadata: metadata information about the charm being generated. + :param license: information license to included in the charm being generated. + :param options: options to override the normal flow. + """ + self.path = os.getcwd() + self.metadata = metadata + self.license = license + self.options = options + + def generate(self): + """ + Generates the metadata.yaml using templates. + """ + MetadataGenerator.LOGGER.info('Generating the metadata.yaml...') + + standard_metadata = self._read_metadata_yaml() + self._update_metadata(standard_metadata) + self._rename_metadata_yaml_as_backup() + self._write_metadata_yaml() + + MetadataGenerator.LOGGER.info('Generated the metadata.yaml.') + + def get_metadata(self): + """ + Gets the enhanced metadata. + + :return: the enhanced metadata dictionary. + """ + return self.metadata + + def _read_metadata_yaml(self): + """ + Reads the values from the old metadata.yaml and does a cleanup on undesired values. + """ + MetadataGenerator.LOGGER.debug('Reading old metadata.yaml...') + + metadata_yaml_path = self.path + '/metadata.yaml' + + if not os.path.isfile(metadata_yaml_path): + MetadataGenerator.LOGGER.error('metadata.yaml must be present. Must be run in the root of the charm') + sys.exit(-1) + + with open(metadata_yaml_path, 'r') as f: + metadata = yaml.load(f) + + if 'tags' in metadata.keys(): + del metadata['tags'] + if 'provides' in metadata.keys(): + del metadata['provides'] + if 'requires' in metadata.keys(): + del metadata['requires'] + if 'peers' in metadata.keys(): + del metadata['peers'] + + MetadataGenerator.LOGGER.debug('Read old metadata.yaml.') + + return metadata + + def _update_metadata(self, metadata): + """ + Update internal metadata before writing the new metadata.yaml. + + :param metadata: metadata values provided by the user. + """ + MetadataGenerator.LOGGER.debug('Generating the Ansible Charm...') + + if 'name' in metadata: + self.metadata['name'] = metadata['name'] + self.metadata['file'] = '%s.py' % metadata['name'].replace('-', '_') + + self.metadata['subordinate'] = False + self.metadata['tags'] = ['misc', 'osm', 'vnf'] + self.metadata['series'] = ['xenial', 'trusty'] + + MetadataGenerator.LOGGER.debug('Generating the Ansible Charm...') + + def _rename_metadata_yaml_as_backup(self): + """ + Renames the metadata.yaml to metadata.yaml.bkp.*. + Preserves the history of the charm for the user. + """ + MetadataGenerator.LOGGER.debug('Renaming the metadata.yaml to .bkp.*...') + + metadata_yaml_path = self.path + '/metadata.yaml' + + ids = [int(f.split('.')[-1]) for f in os.listdir(self.path) + if f.startswith('metadata.yaml.bkp')] + + id = 0 + if ids: + id = functools.reduce(lambda x, y: x if (x > y) else y, ids) + id += 1 + + backup_metadata_yaml_path = self.path + ('/metadata.yaml.bkp.%02d' % id) + os.rename(metadata_yaml_path, backup_metadata_yaml_path) + + MetadataGenerator.LOGGER.debug('Renamed the metadata.yaml to .bkp.%02d.', id) + + def _write_metadata_yaml(self): + """ + Writes the enriched metadata to metadata.yaml. + """ + MetadataGenerator.LOGGER.debug('Generating metadata.yaml...') + + metadata_yaml_path = self.path + '/metadata.yaml' + metadata_yaml_template = MetadataGenerator.ENV.get_template('metadata.yaml.j2') + + with open(metadata_yaml_path, 'w') as f: + f.write(metadata_yaml_template.render(metadata=self.metadata, license=self.license)) + + MetadataGenerator.LOGGER.debug('Generated metadata.yaml.') diff --git a/descriptor-packages/tools/charm-generator/generator/metadata/__init__.py b/descriptor-packages/tools/charm-generator/generator/metadata/__init__.py new file mode 100644 index 00000000..e584ee55 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/metadata/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## diff --git a/descriptor-packages/tools/charm-generator/generator/metadata/templates/metadata.yaml.j2 b/descriptor-packages/tools/charm-generator/generator/metadata/templates/metadata.yaml.j2 new file mode 100644 index 00000000..e1892444 --- /dev/null +++ b/descriptor-packages/tools/charm-generator/generator/metadata/templates/metadata.yaml.j2 @@ -0,0 +1,50 @@ +{#- +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +-#} +{%- if license is defined -%} +# Copyright {{ license.year }} {{ license.company }} +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: {{ license.email }} +{%- endif %} + +name: {{ metadata.name }} +summary: {{ metadata.summary }} +maintainer: {{ metadata.maintainer }} +description: {{ metadata.description }} +tags: +{% for tag in metadata.tags -%} +- {{ tag }} +{% endfor -%} +series: +{% for serie in metadata.series -%} +- {{ serie }} +{% endfor -%} +subordinate: {{ metadata.subordinate }} diff --git a/descriptor-packages/tools/charm-generator/generators/__init__.py b/descriptor-packages/tools/charm-generator/generators/__init__.py deleted file mode 100644 index e584ee55..00000000 --- a/descriptor-packages/tools/charm-generator/generators/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com -## diff --git a/descriptor-packages/tools/charm-generator/generators/actions_generator.py b/descriptor-packages/tools/charm-generator/generators/actions_generator.py deleted file mode 100644 index 832fd092..00000000 --- a/descriptor-packages/tools/charm-generator/generators/actions_generator.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com -## - -import functools -import logging -import os -import stat - -from jinja2 import Environment, PackageLoader - - -class ActionsGenerator: - LOGGER = logging.getLogger() - ENV = Environment(loader=PackageLoader('actions', 'templates')) - - def __init__(self, metadata, actions, license=None, options=None): - """ - Creates the object to generate the actions folder, actions files and actions.yaml. - - Usage should be: - - 1) Create the object. - 2) Run the generate method. - - :param metadata: metadata information about the charm being generated. - :param actions: actions to be included in the charm. - :param license: information license to included in the charm being generated. - :param options: options to override the normal flow. - """ - self.path = os.getcwd() - self.metadata = metadata - self.actions = actions - self.license = license - self.options = options - - def generate(self): - """ - Generates the actions folder, actions files and actions.yaml. - """ - ActionsGenerator.LOGGER.info('Generating the actions...') - - self._create_actions_folder() - - for action in self.actions: - self._generate_action_file(action) - - self._generate_actions_yaml_file() - - ActionsGenerator.LOGGER.info('Generated the actions.') - - def _create_actions_folder(self): - """ - Creates the actions folder, where all the action files are placed. - These files are the entry point for the execution of the actions. - """ - ActionsGenerator.LOGGER.debug('Creating the actions folder...') - - actions_path = self.path + '/actions' - - if not os.path.isdir(actions_path): - os.mkdir(actions_path) - else: - ActionsGenerator.LOGGER.warning('Actions folder already exists.') - return - - ActionsGenerator.LOGGER.debug('Created actions folder.') - - def _generate_action_file(self, action): - """ - Generates the action file to act as entry point for a specific action. - - Note: the action file is made executable during this function. - - :param action: dictionary with information about the action - """ - ActionsGenerator.LOGGER.debug('Creating action file: %s...', action['action_name']) - - playbook_path = self.path + ('/actions/%s' % action['action_name']) - action_file_template = ActionsGenerator.ENV.get_template('action.j2') - - with open(playbook_path, "w") as f: - f.write(action_file_template.render(license=self.license)) - mode = os.fstat(f.fileno()).st_mode - mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH - os.fchmod(f.fileno(), stat.S_IMODE(mode)) - - ActionsGenerator.LOGGER.debug('Created action file: %s.', action['action_name']) - - def _generate_actions_yaml_file(self): - """ - Generates the actions.yaml file from a template. - It takes all the playbook information and fills in the templates. - - Note: renames the old actions.yaml file with a .bkp extension, so the history is preserved. - """ - ActionsGenerator.LOGGER.debug('Creating actions.yaml...') - - actions_yaml_path = self.path + '/actions.yaml' - actions_template = ActionsGenerator.ENV.get_template('actions.yaml.j2') - - if os.path.isfile(actions_yaml_path): - ids = [int(f.split('.')[-1]) for f in os.listdir(self.path) if f.startswith('actions.yaml.bkp')] - - id = 0 - if ids: - id = functools.reduce(lambda x, y: x if (x > y) else y, ids) - id += 1 - - backup_actions_yaml_path = self.path + ('/actions.yaml.bkp.%02d' % id) - os.rename(actions_yaml_path, backup_actions_yaml_path) - - with open(actions_yaml_path, 'w') as f: - f.write(actions_template.render(actions=self.actions, license=self.license)) - - ActionsGenerator.LOGGER.debug('Created actions.yaml.') diff --git a/descriptor-packages/tools/charm-generator/generators/ansible_generator.py b/descriptor-packages/tools/charm-generator/generators/ansible_generator.py deleted file mode 100644 index 18f5a5a9..00000000 --- a/descriptor-packages/tools/charm-generator/generators/ansible_generator.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com -## - -import functools -import logging -import os -import sys - -from jinja2 import Environment, PackageLoader - -from generators.actions_generator import ActionsGenerator -from generators.metadata_generator import MetadataGenerator - - -class AnsibleGenerator: - LOGGER = logging.getLogger() - ENV = Environment(loader=PackageLoader('ansible-charm', 'templates')) - - def __init__(self, metadata, license=None, options=None): - """ - Creates the object to generate the ansible charm from templates. - - Usage should be: - - 1) Create the object. - 2) Run the generate method. - - :param metadata: metadata information about the charm being generated. - :param license: information license to included in the charm being generated. - :param options: options to override the normal flow. - """ - self.path = os.getcwd() - self.metadata = metadata - self.license = license - self.options = options - self.playbooks = AnsibleGenerator._fetch_all_playbooks(self.path) - - playbooks_name = [playbook['file'] for playbook in self.playbooks] - AnsibleGenerator.LOGGER.debug('Playbooks found: %s', playbooks_name) - - self.metadata_generator = MetadataGenerator(metadata=self.metadata, license=self.license, options=self.options) - self.actions_generator = ActionsGenerator(metadata=self.metadata, actions=self.playbooks, license=self.license, - options=self.options) - - def generate(self): - """ - Generates the Ansible Charm using templates. - """ - AnsibleGenerator.LOGGER.info('Generating the Ansible Charm...') - - # Generating metadata.yaml - self.metadata_generator.generate() - self.metadata = self.metadata_generator.get_metadata() - - # Generating actions - self.actions_generator.generate() - - self._generate_ansible_lib() - self._generate_layer_yaml_file() - self._generate_reactive_file() - - AnsibleGenerator.LOGGER.info('Generated the Ansible Charm.') - - @staticmethod - def _fetch_all_playbooks(path): - """ - Walks over the playbooks directory, fetches the playbook's name. - It takes the file name and normalizes it to be used in the ansible charm. - - :param path: path of the root of the charm. - :return: a list of dictionaries with the information about the playbooks. - """ - playbooks_path = path + '/playbooks' - - if os.path.isdir(playbooks_path) and len(os.listdir(playbooks_path)) != 0: - filenames = os.listdir(playbooks_path) - - result = [] - for file in filenames: - info = { - 'action_name': file.replace('_', '-').replace('.yaml', ''), - 'function_name': file.replace('-', '_').replace('.yaml', ''), - 'file': file - } - result.append(info) - - return result - else: - AnsibleGenerator.LOGGER.error('Playbooks directory should exist and be populated.') - sys.exit(-1) - - def _generate_ansible_lib(self): - """ - Generates the ansible lib file from a template. - """ - AnsibleGenerator.LOGGER.debug('Creating ansible.py lib...') - - lib_folder_path = self.path + '/lib/charms' - ansible_lib_path = lib_folder_path + '/libansible.py' - - if not os.path.isdir(lib_folder_path): - os.makedirs(lib_folder_path) - - ansible_lib_template = AnsibleGenerator.ENV.get_template('ansible_lib.py.j2') - - with open(ansible_lib_path, 'w') as f: - f.write(ansible_lib_template.render(license=self.license)) - - AnsibleGenerator.LOGGER.debug('Created anisble.py lib.') - - def _generate_layer_yaml_file(self): - """ - Generates the layer.yaml file from a template. - - Note: disables the venv environment. - """ - AnsibleGenerator.LOGGER.debug('Creating layer.yaml...') - - layer_yaml_path = self.path + '/layer.yaml' - - layers = [{ - 'name': 'basic', - 'options': [{ - 'name': 'use_venv', - 'value': 'false' - }]}, { - 'name': 'ansible-base' - }, { - 'name': 'vnfproxy' - }] - - layer_template = AnsibleGenerator.ENV.get_template('layer.yaml.j2') - - with open(layer_yaml_path, 'w') as f: - f.write(layer_template.render(layers=layers, license=self.license)) - - AnsibleGenerator.LOGGER.debug('Created layer.yaml.') - - def _generate_reactive_file(self): - """ - Generates the Ansible reactive file from a template. - It takes all the playbook information and fills in the templates. - - Note: renames the old charm file with a .bkp extension, so the history is preserved. - """ - AnsibleGenerator.LOGGER.debug('Creating ansible charm: %s...', self.metadata['file']) - - reactive_folder_path = self.path + '/reactive' - charm_file_path = reactive_folder_path + ('/%s' % self.metadata['file']) - ansible_charm_template = AnsibleGenerator.ENV.get_template('ansible_charm.py.j2') - - ids = [int(f.split('.')[-1]) for f in os.listdir(reactive_folder_path) - if f.startswith('%s.bkp' % self.metadata['file'])] - - id = 0 - if ids: - id = functools.reduce(lambda x, y: x if (x > y) else y, ids) - id += 1 - - backup_charm_file_path = reactive_folder_path + ('/%s.bkp.%02d' % (self.metadata['file'], id)) - os.rename(charm_file_path, backup_charm_file_path) - - with open(charm_file_path, 'w') as f: - f.write(ansible_charm_template.render(charm_name=self.metadata['name'], playbooks=self.playbooks, - license=self.license)) - - AnsibleGenerator.LOGGER.debug('Created ansible charm: %s.', self.metadata['file']) diff --git a/descriptor-packages/tools/charm-generator/generators/metadata_generator.py b/descriptor-packages/tools/charm-generator/generators/metadata_generator.py deleted file mode 100644 index 8b0d9b8d..00000000 --- a/descriptor-packages/tools/charm-generator/generators/metadata_generator.py +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com -## - -import functools -import logging -import os -import sys - -import yaml - -from jinja2 import Environment, PackageLoader - - -class MetadataGenerator: - LOGGER = logging.getLogger() - ENV = Environment(loader=PackageLoader('metadata', 'templates')) - - def __init__(self, metadata, license=None, options=None): - """ - Creates the object to generate the metadata.yaml file from a template. - - Usage should be: - - 1) Create the object. - 2) Run the generate method. - - :param metadata: metadata information about the charm being generated. - :param license: information license to included in the charm being generated. - :param options: options to override the normal flow. - """ - self.path = os.getcwd() - self.metadata = metadata - self.license = license - self.options = options - - def generate(self): - """ - Generates the metadata.yaml using templates. - """ - MetadataGenerator.LOGGER.info('Generating the metadata.yaml...') - - standard_metadata = self._read_metadata_yaml() - self._update_metadata(standard_metadata) - self._rename_metadata_yaml_as_backup() - self._write_metadata_yaml() - - MetadataGenerator.LOGGER.info('Generated the metadata.yaml.') - - def get_metadata(self): - """ - Gets the enhanced metadata. - - :return: the enhanced metadata dictionary. - """ - return self.metadata - - def _read_metadata_yaml(self): - """ - Reads the values from the old metadata.yaml and does a cleanup on undesired values. - """ - MetadataGenerator.LOGGER.debug('Reading old metadata.yaml...') - - metadata_yaml_path = self.path + '/metadata.yaml' - - if not os.path.isfile(metadata_yaml_path): - MetadataGenerator.LOGGER.error('metadata.yaml must be present. Must be run in the root of the charm') - sys.exit(-1) - - with open(metadata_yaml_path, 'r') as f: - metadata = yaml.load(f) - - if 'tags' in metadata.keys(): - del metadata['tags'] - if 'provides' in metadata.keys(): - del metadata['provides'] - if 'requires' in metadata.keys(): - del metadata['requires'] - if 'peers' in metadata.keys(): - del metadata['peers'] - - MetadataGenerator.LOGGER.debug('Read old metadata.yaml.') - - return metadata - - def _update_metadata(self, metadata): - """ - Update internal metadata before writing the new metadata.yaml. - - :param metadata: metadata values provided by the user. - """ - MetadataGenerator.LOGGER.debug('Generating the Ansible Charm...') - - if 'name' in metadata: - self.metadata['name'] = metadata['name'] - self.metadata['file'] = '%s.py' % metadata['name'].replace('-', '_') - - self.metadata['subordinate'] = False - self.metadata['tags'] = ['misc', 'osm', 'vnf'] - self.metadata['series'] = ['xenial', 'trusty'] - - MetadataGenerator.LOGGER.debug('Generating the Ansible Charm...') - - def _rename_metadata_yaml_as_backup(self): - """ - Renames the metadata.yaml to metadata.yaml.bkp.*. - Preserves the history of the charm for the user. - """ - MetadataGenerator.LOGGER.debug('Renaming the metadata.yaml to .bkp.*...') - - metadata_yaml_path = self.path + '/metadata.yaml' - - ids = [int(f.split('.')[-1]) for f in os.listdir(self.path) - if f.startswith('metadata.yaml.bkp')] - - id = 0 - if ids: - id = functools.reduce(lambda x, y: x if (x > y) else y, ids) - id += 1 - - backup_metadata_yaml_path = self.path + ('/metadata.yaml.bkp.%02d' % id) - os.rename(metadata_yaml_path, backup_metadata_yaml_path) - - MetadataGenerator.LOGGER.debug('Renamed the metadata.yaml to .bkp.%02d.', id) - - def _write_metadata_yaml(self): - """ - Writes the enriched metadata to metadata.yaml. - """ - MetadataGenerator.LOGGER.debug('Generating metadata.yaml...') - - metadata_yaml_path = self.path + '/metadata.yaml' - metadata_yaml_template = MetadataGenerator.ENV.get_template('metadata.yaml.j2') - - with open(metadata_yaml_path, 'w') as f: - f.write(metadata_yaml_template.render(metadata=self.metadata, license=self.license)) - - MetadataGenerator.LOGGER.debug('Generated metadata.yaml.') diff --git a/descriptor-packages/tools/charm-generator/metadata/__init__.py b/descriptor-packages/tools/charm-generator/metadata/__init__.py deleted file mode 100644 index e584ee55..00000000 --- a/descriptor-packages/tools/charm-generator/metadata/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com -## diff --git a/descriptor-packages/tools/charm-generator/metadata/templates/metadata.yaml.j2 b/descriptor-packages/tools/charm-generator/metadata/templates/metadata.yaml.j2 deleted file mode 100644 index e1892444..00000000 --- a/descriptor-packages/tools/charm-generator/metadata/templates/metadata.yaml.j2 +++ /dev/null @@ -1,50 +0,0 @@ -{#- -# Copyright 2019 Whitestack, LLC -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: esousa@whitestack.com or glavado@whitestack.com --#} -{%- if license is defined -%} -# Copyright {{ license.year }} {{ license.company }} -# -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: {{ license.email }} -{%- endif %} - -name: {{ metadata.name }} -summary: {{ metadata.summary }} -maintainer: {{ metadata.maintainer }} -description: {{ metadata.description }} -tags: -{% for tag in metadata.tags -%} -- {{ tag }} -{% endfor -%} -series: -{% for serie in metadata.series -%} -- {{ serie }} -{% endfor -%} -subordinate: {{ metadata.subordinate }} diff --git a/descriptor-packages/tools/charm-generator/requirements.txt b/descriptor-packages/tools/charm-generator/requirements.txt index 89ee2b0f..f0aaeda5 100644 --- a/descriptor-packages/tools/charm-generator/requirements.txt +++ b/descriptor-packages/tools/charm-generator/requirements.txt @@ -1 +1 @@ -Jinja2==2.10 +Jinja2>=2.10 diff --git a/descriptor-packages/tools/charm-generator/setup.py b/descriptor-packages/tools/charm-generator/setup.py new file mode 100644 index 00000000..5015efae --- /dev/null +++ b/descriptor-packages/tools/charm-generator/setup.py @@ -0,0 +1,55 @@ +# Copyright 2019 Whitestack, LLC +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: esousa@whitestack.com or glavado@whitestack.com +## + +import re +import setuptools + +version = re.search( + r'^__version__\s*=\s*["\'](.*)["\']', + open('generator/generator.py').read(), + re.MULTILINE).group(1) + +with open('README.md', 'r') as f: + long_description = f.read() + +setuptools.setup( + name='osm-charm-generator', + packages=setuptools.find_packages(), + entry_points={ + "console_scripts": ['osm-charm-generator = generator.generator:main'] + }, + version=version, + author='Eduardo Sousa', + author_email='esousa@whitestack.com', + description='OSM Charm Generator using Ansible', + long_description=long_description, + long_description_content_type='text/markdown', + url='https://osm.etsi.org', + install_requires=[ + 'Jinja2>=2.10' + ], + classifiers=[ + 'Programming Language :: Python :: 3 :: Only', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Environment :: Console', + 'Intended Audience :: Telecommunications Industry', + 'Natural Language :: English', + 'Topic :: Software Development :: Code Generators' + ] +)