blob: 2ad0077d066298921153da9a26a0be180cec3294 [file] [log] [blame]
Vasyl Saienkof9ee1582020-03-02 16:53:41 +02001#!/usr/bin/python3
2#
3# Copyright 2018 Mirantis, Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17
18"""Prepare metadata python module
19
Dennis Dmitrievb12d2342020-03-12 19:36:24 +020020The module is aimed to prepare system files (networking configs etc)
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020021based on lab metadata.
Dennis Dmitrievb12d2342020-03-12 19:36:24 +020022Shell environment variables can be used in the metadata as Jinja2 variables.
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020023
24Example:
25 python prepare-metadata --metadata-file '/etc/lab-metadata.yaml'
26
27Example of lab-metadata.yaml
28
29'52:54:00:10:94:78':
30 write_files:
31 - path: '/tmp/123.yaml'
32 content: |
33 foo: bar
Dennis Dmitrievb12d2342020-03-12 19:36:24 +020034 bee: {{ PUBLIC_INTERFACE_IP }}
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020035
36Attributes:
37 metadata-file - The file with metadata
38"""
39
40
41__version__ = '1.0'
42
43import argparse
Dennis Dmitrievb12d2342020-03-12 19:36:24 +020044import jinja2
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020045import os
46import yaml
Vasyl Saienko669dbd92020-03-13 09:32:01 +020047import logging
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020048import netifaces
49import sys
50
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020051
Vasyl Saienko669dbd92020-03-13 09:32:01 +020052LOG = logging.getLogger(__name__)
53
54
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020055def main():
Vasyl Saienko669dbd92020-03-13 09:32:01 +020056 logging.basicConfig(
57 stream=sys.stdout,
58 format='%(asctime)s - %(levelname)s - %(message)s'
59 )
60 LOG.setLevel(logging.INFO)
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020061
62 parser = argparse.ArgumentParser(
63 description=('Render system files based on metadata')
64 )
65
66 group = parser.add_argument_group()
67 group.add_argument(
68 '--metadata-file',
69 help='The path to metadata file.',
70 required=True
71 )
72 args = parser.parse_args()
73
Dennis Dmitrievb12d2342020-03-12 19:36:24 +020074 metadata = yaml.safe_load(render_template(args.metadata_file))
75
Vasyl Saienkoc719b5c2020-03-05 19:14:26 +020076 if not metadata:
Vasyl Saienko669dbd92020-03-13 09:32:01 +020077 LOG.info("The metadata is empty")
Vasyl Saienkoc719b5c2020-03-05 19:14:26 +020078 return
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020079 node_meta = get_node_metadata(metadata)
80 if node_meta is not None:
Vasyl Saienko669dbd92020-03-13 09:32:01 +020081 LOG.info(f"Processing node_metadata: {node_meta}")
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020082 create_files(node_meta.get('write_files', []))
83 else:
Vasyl Saienko669dbd92020-03-13 09:32:01 +020084 LOG.error("No matches to MACs for node_metadata found")
Vasyl Saienkof9ee1582020-03-02 16:53:41 +020085
86def get_interface_mac(iface_name):
87 mac = None
88 ifaddresses = netifaces.ifaddresses(iface_name)
89 link = ifaddresses.get(netifaces.AF_LINK, [])
90 if link:
91 return link[0]['addr']
92
93def get_node_macs():
94 ifaces = netifaces.interfaces()
95 macs = [get_interface_mac(iface_name) for iface_name in ifaces]
96 return [mac for mac in macs if mac is not None]
97
98def get_node_metadata(metadata):
99 for mac in get_node_macs():
100 if mac in metadata:
101 return metadata[mac]
102
103def create_files(files_meta):
104 for file_meta in files_meta:
105 path = file_meta['path']
106 content = file_meta['content']
107 permissions = int(str(file_meta.get('permissions', '644')), base=8)
108 with open(path, "w") as f:
109 f.write(content)
110 os.chmod(path, permissions)
111
Dennis Dmitrievb12d2342020-03-12 19:36:24 +0200112def render_template(file_path):
113 """Render a Jinja2 template file
114
115 In the template:
116 {{ SOME_ENV_NAME }} : Insert an environment variable into the template
117
118 :param file_path: str, path to the jinja2 template
119 """
Vasyl Saienko669dbd92020-03-13 09:32:01 +0200120 LOG.info("Reading template {0}".format(file_path))
Dennis Dmitrievb12d2342020-03-12 19:36:24 +0200121
122 path, filename = os.path.split(file_path)
123 environment = jinja2.Environment(
124 loader=jinja2.FileSystemLoader([path, os.path.dirname(path)],
125 followlinks=True))
126 template = environment.get_template(filename).render(os.environ)
127
128 return template
129
Vasyl Saienkof9ee1582020-03-02 16:53:41 +0200130if __name__ == '__main__':
131 try:
132 main()
133 except Exception as e:
Vasyl Saienko669dbd92020-03-13 09:32:01 +0200134 LOG.exception(f"Failed to apply image layout: {e}")
Vasyl Saienkof9ee1582020-03-02 16:53:41 +0200135 sys.exit(1)