| #!/usr/bin/python3 |
| # |
| # Copyright 2018 Mirantis, Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| # not use this file except in compliance with the License. You may obtain |
| # a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| |
| |
| """Prepare metadata python module |
| |
| The module is aimed to prepare system files (networking configs etc) |
| based on lab metadata. |
| Shell environment variables can be used in the metadata as Jinja2 variables. |
| |
| Example: |
| python prepare-metadata --metadata-file '/etc/lab-metadata.yaml' |
| |
| Example of lab-metadata.yaml |
| |
| '52:54:00:10:94:78': |
| write_files: |
| - path: '/tmp/123.yaml' |
| content: | |
| foo: bar |
| bee: {{ PUBLIC_INTERFACE_IP }} |
| |
| Attributes: |
| metadata-file - The file with metadata |
| """ |
| |
| |
| __version__ = '1.0' |
| |
| import argparse |
| import jinja2 |
| import os |
| import yaml |
| import logging |
| import netifaces |
| import sys |
| |
| |
| LOG = logging.getLogger(__name__) |
| |
| |
| def main(): |
| logging.basicConfig( |
| stream=sys.stdout, |
| format='%(asctime)s - %(levelname)s - %(message)s' |
| ) |
| LOG.setLevel(logging.INFO) |
| |
| parser = argparse.ArgumentParser( |
| description=('Render system files based on metadata') |
| ) |
| |
| group = parser.add_argument_group() |
| group.add_argument( |
| '--metadata-file', |
| help='The path to metadata file.', |
| required=True |
| ) |
| args = parser.parse_args() |
| |
| metadata = yaml.safe_load(render_template(args.metadata_file)) |
| |
| if not metadata: |
| LOG.info("The metadata is empty") |
| return |
| node_meta = get_node_metadata(metadata) |
| if node_meta is not None: |
| LOG.info(f"Processing node_metadata: {node_meta}") |
| create_files(node_meta.get('write_files', [])) |
| else: |
| LOG.error("No matches to MACs for node_metadata found") |
| |
| def get_interface_mac(iface_name): |
| mac = None |
| ifaddresses = netifaces.ifaddresses(iface_name) |
| link = ifaddresses.get(netifaces.AF_LINK, []) |
| if link: |
| return link[0]['addr'] |
| |
| def get_node_macs(): |
| ifaces = netifaces.interfaces() |
| macs = [get_interface_mac(iface_name) for iface_name in ifaces] |
| return [mac for mac in macs if mac is not None] |
| |
| def get_node_metadata(metadata): |
| for mac in get_node_macs(): |
| if mac in metadata: |
| return metadata[mac] |
| |
| def create_files(files_meta): |
| for file_meta in files_meta: |
| path = file_meta['path'] |
| content = file_meta['content'] |
| permissions = int(str(file_meta.get('permissions', '644')), base=8) |
| with open(path, "w") as f: |
| f.write(content) |
| os.chmod(path, permissions) |
| |
| def render_template(file_path): |
| """Render a Jinja2 template file |
| |
| In the template: |
| {{ SOME_ENV_NAME }} : Insert an environment variable into the template |
| |
| :param file_path: str, path to the jinja2 template |
| """ |
| LOG.info("Reading template {0}".format(file_path)) |
| |
| path, filename = os.path.split(file_path) |
| environment = jinja2.Environment( |
| loader=jinja2.FileSystemLoader([path, os.path.dirname(path)], |
| followlinks=True)) |
| template = environment.get_template(filename).render(os.environ) |
| |
| return template |
| |
| if __name__ == '__main__': |
| try: |
| main() |
| except Exception as e: |
| LOG.exception(f"Failed to apply image layout: {e}") |
| sys.exit(1) |