Fuel Config Reap + Additional Refactoring for Autodeployment
[genesis.git] / fuel / deploy / reap.py
diff --git a/fuel/deploy/reap.py b/fuel/deploy/reap.py
new file mode 100644 (file)
index 0000000..8a8681a
--- /dev/null
@@ -0,0 +1,330 @@
+import common
+import time
+import os
+import yaml
+import glob
+import shutil
+
+N = common.N
+E = common.E
+R = common.R
+ArgParser = common.ArgParser
+exec_cmd = common.exec_cmd
+parse = common.parse
+err = common.err
+log = common.log
+delete_file = common.delete_file
+commafy = common.commafy
+
+DEA_1 = '''
+title: Deployment Environment Adapter (DEA)
+# DEA API version supported
+version: 1.1
+created: {date}
+comment: {comment}
+'''
+
+DHA_1 = '''
+title: Deployment Hardware Adapter (DHA)
+# DHA API version supported
+version: 1.1
+created: {date}
+comment: {comment}
+
+# Adapter to use for this definition
+# adapter: [ipmi|libvirt]
+adapter:
+
+# Node list.
+# Mandatory properties are id and role.
+# All other properties are adapter specific.
+# For Non-Fuel nodes controlled by:
+#   - ipmi adapter you need to provide:
+#       pxeMac
+#       ipmiIp
+#       ipmiUser
+#       ipmiPass
+#   - libvirt adapter you need to provide:
+#       libvirtName: <whatever>
+#       libvirtTemplate: [libvirt/vms/controller.xml | libvirt/vms/compute.xml]
+#
+# For the Fuel Node you need to provide:
+#       libvirtName: <whatever>
+#       libvirtTemplate: libvirt/vms/fuel.xml
+#       isFuel: yes
+#       username: root
+#       password: r00tme
+'''
+
+DHA_2 = '''
+# Adding the Fuel node as node id {node_id}
+# which may not be correct - please adjust as needed.
+'''
+
+DISKS = {'fuel': '30G',
+         'controller': '30G',
+         'compute': '30G'}
+
+class Reap(object):
+
+    def __init__(self, dea_file, dha_file, comment):
+        self.dea_file = dea_file
+        self.dha_file = dha_file
+        self.comment = comment
+        self.temp_dir = None
+        self.env = None
+        self.env_id = None
+        self.last_node = None
+
+    def get_env(self):
+        env_list = parse(exec_cmd('fuel env'))
+        if len(env_list) > 1:
+            err('Not exactly one environment')
+        self.env = env_list[0]
+        self.env_id = self.env[E['id']]
+
+    def download_config(self, config_type):
+        log('Download %s config for environment %s'
+            % (config_type, self.env_id))
+        exec_cmd('fuel %s --env %s --download --dir %s'
+                 % (config_type, self.env_id, self.temp_dir))
+
+    def write(self, file, text, newline=True):
+        mode = 'a' if os.path.isfile(file) else 'w'
+        with open(file, mode) as f:
+            f.write('%s%s' % (text, ('\n' if newline else '')))
+
+    def write_yaml(self, file, data, newline=True):
+        self.write(file, yaml.dump(data, default_flow_style=False).strip(),
+                   newline)
+
+    def get_node_by_id(self, node_list, node_id):
+        for node in node_list:
+            if node[N['id']] == node_id:
+                return node
+
+    def reap_interface(self, node_id, interfaces):
+        interface, mac = self.get_interface(node_id)
+        if_name = None
+        if interfaces:
+            if_name = self.check_dict_exists(interfaces, interface)
+        if not if_name:
+            if_name = 'interfaces_%s' % str(len(interfaces) + 1)
+            interfaces[if_name] = interface
+        return if_name, mac
+
+    def reap_transformation(self, node_id, roles, transformations):
+        main_role = 'controller' if 'controller' in roles else 'compute'
+        node_file = glob.glob('%s/deployment_%s/*%s_%s.yaml'
+                              % (self.temp_dir, self.env_id,
+                                 main_role, node_id))
+        tr_name = None
+        with open(node_file[0]) as f:
+            node_config = yaml.load(f)
+        transformation = node_config['network_scheme']['transformations']
+        if transformations:
+            tr_name = self.check_dict_exists(transformations, transformation)
+        if not tr_name:
+            tr_name = 'transformations_%s' % str(len(transformations) + 1)
+            transformations[tr_name] = transformation
+        return tr_name
+
+    def check_dict_exists(self, main_dict, dict):
+        for key, val in main_dict.iteritems():
+            if cmp(dict, val) == 0:
+                return key
+
+    def reap_nodes_interfaces_transformations(self):
+        node_list = parse(exec_cmd('fuel node'))
+        real_node_ids = [node[N['id']] for node in node_list]
+        real_node_ids.sort()
+        min_node = real_node_ids[0]
+
+        interfaces = {}
+        transformations = {}
+        dea_nodes = []
+        dha_nodes = []
+
+        for real_node_id in real_node_ids:
+            node_id = int(real_node_id) - int(min_node) + 1
+            self.last_node = node_id
+            node = self.get_node_by_id(node_list, real_node_id)
+            roles = commafy(node[N['roles']])
+            if not roles:
+                err('Fuel Node %s has no role' % real_node_id)
+            dea_node = {'id': node_id,
+                        'role': roles}
+            dha_node = {'id': node_id}
+            if_name, mac = self.reap_interface(real_node_id, interfaces)
+            tr_name = self.reap_transformation(real_node_id, roles,
+                                               transformations)
+            dea_node.update(
+                {'interfaces': if_name,
+                 'transformations': tr_name})
+
+            dha_node.update(
+                {'pxeMac': mac if mac else None,
+                 'ipmiIp': None,
+                 'ipmiUser': None,
+                 'ipmiPass': None,
+                 'libvirtName': None,
+                 'libvirtTemplate': None})
+
+            dea_nodes.append(dea_node)
+            dha_nodes.append(dha_node)
+
+        self.write_yaml(self.dha_file, {'nodes': dha_nodes}, False)
+        self.write_yaml(self.dea_file, {'nodes': dea_nodes})
+        self.write_yaml(self.dea_file, {'interfaces': interfaces})
+        self.write_yaml(self.dea_file, {'transformations': transformations})
+        self.reap_fuel_node_info()
+        self.write_yaml(self.dha_file, {'disks': DISKS})
+
+    def reap_fuel_node_info(self):
+        dha_nodes = []
+        dha_node = {
+            'id': self.last_node + 1,
+            'libvirtName': None,
+            'libvirtTemplate': None,
+            'isFuel': True,
+            'username': 'root',
+            'password': 'r00tme'}
+
+        dha_nodes.append(dha_node)
+
+        self.write(self.dha_file, DHA_2.format(node_id=dha_node['id']), False)
+        self.write_yaml(self.dha_file, dha_nodes)
+
+    def reap_environment_info(self):
+        self.write_yaml(self.dea_file,
+                        {'environment_name': self.env[E['name']]})
+        self.write_yaml(self.dea_file,
+                        {'environment_mode': self.env[E['mode']]})
+        wanted_release = None
+        rel_list = parse(exec_cmd('fuel release'))
+        for rel in rel_list:
+            if rel[R['id']] == self.env[E['release_id']]:
+                wanted_release = rel[R['name']]
+        self.write_yaml(self.dea_file, {'wanted_release': wanted_release})
+
+    def reap_fuel_settings(self):
+        data = self.read_yaml('/etc/fuel/astute.yaml')
+        fuel = {}
+        del(data['ADMIN_NETWORK']['mac'])
+        del(data['ADMIN_NETWORK']['interface'])
+        for key in ['ADMIN_NETWORK', 'HOSTNAME', 'DNS_DOMAIN', 'DNS_SEARCH',
+                    'DNS_UPSTREAM', 'NTP1', 'NTP2', 'NTP3', 'FUEL_ACCESS']:
+            fuel[key] = data[key]
+        self.write_yaml(self.dea_file, {'fuel': fuel})
+
+    def reap_network_settings(self):
+        network_file = ('%s/network_%s.yaml'
+                          % (self.temp_dir, self.env_id))
+        data = self.read_yaml(network_file)
+        network = {}
+        network['networking_parameters'] = data['networking_parameters']
+        network['networks'] = data['networks']
+        for net in network['networks']:
+            del net['id']
+            del net['group_id']
+        self.write_yaml(self.dea_file, {'network': network})
+
+    def reap_settings(self):
+        settings_file  = '%s/settings_%s.yaml' % (self.temp_dir, self.env_id)
+        settings = self.read_yaml(settings_file)
+        self.write_yaml(self.dea_file, {'settings': settings})
+
+    def get_opnfv_astute(self, role):
+        node_files = glob.glob('%s/deployment_%s/*%s*.yaml'
+                               % (self.temp_dir, self.env_id, role))
+        node_config = self.read_yaml(node_files[0])
+        return node_config['opnfv'] if 'opnfv' in node_config else {}
+
+    def reap_opnfv_astute(self):
+        controller_opnfv_astute = self.get_opnfv_astute('controller')
+        compute_opnfv_astute = self.get_opnfv_astute('compute')
+        opnfv = {}
+        opnfv['opnfv'] = {
+            'controller': controller_opnfv_astute,
+            'compute': compute_opnfv_astute}
+        self.write_yaml(self.dea_file, opnfv)
+
+    def get_interface(self, real_node_id):
+        exec_cmd('fuel node --node-id %s --network --download --dir %s'
+                 % (real_node_id, self.temp_dir))
+        interface_file = ('%s/node_%s/interfaces.yaml'
+                          % (self.temp_dir, real_node_id))
+        interfaces = self.read_yaml(interface_file)
+        interface_config = {}
+        pxe_mac = None
+        for interface in interfaces:
+            networks = []
+            for network in interface['assigned_networks']:
+                networks.append(network['name'])
+                if network['name'] == 'fuelweb_admin':
+                    pxe_mac = interface['mac']
+            if networks:
+                interface_config[interface['name']] = networks
+        return interface_config, pxe_mac
+
+    def read_yaml(self, yaml_file):
+        with open(yaml_file) as f:
+            data = yaml.load(f)
+            return data
+
+    def intro(self):
+        delete_file(self.dea_file)
+        delete_file(self.dha_file)
+        self.temp_dir = exec_cmd('mktemp -d')
+        date = time.strftime('%c')
+        self.write(self.dea_file,
+                   DEA_1.format(date=date, comment=self.comment), False)
+        self.write(self.dha_file,
+                   DHA_1.format(date=date, comment=self.comment))
+        self.get_env()
+        self.download_config('deployment')
+        self.download_config('settings')
+        self.download_config('network')
+
+    def finale(self):
+        log('DEA file is available at %s' % self.dea_file)
+        log('DHA file is available at %s (this is just a template)'
+            % self.dha_file)
+        shutil.rmtree(self.temp_dir)
+
+    def reap(self):
+        self.intro()
+        self.reap_environment_info()
+        self.reap_nodes_interfaces_transformations()
+        self.reap_fuel_settings()
+        self.reap_opnfv_astute()
+        self.reap_network_settings()
+        self.reap_settings()
+        self.finale()
+
+def usage():
+    print '''
+    Usage:
+    python reap.py <dea_file> <dha_file> <comment>
+    '''
+
+def parse_arguments():
+    parser = ArgParser(prog='python %s' % __file__)
+    parser.add_argument('dea_file', nargs='?', action='store',
+                        default='dea.yaml',
+                        help='Deployment Environment Adapter: dea.yaml')
+    parser.add_argument('dha_file', nargs='?', action='store',
+                        default='dha.yaml',
+                        help='Deployment Hardware Adapter: dha.yaml')
+    parser.add_argument('comment', nargs='?', action='store', help='Comment')
+    args = parser.parse_args()
+    return (args.dea_file, args.dha_file, args.comment)
+
+def main():
+    dea_file, dha_file, comment = parse_arguments()
+
+    r = Reap(dea_file, dha_file, comment)
+    r.reap()
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file