226af1b54309ea941ea0952c62edf9e84b37d496
[apex.git] / apex / virtual / utils.py
1 ##############################################################################
2 # Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 import copy
11 import iptc
12 import logging
13 import os
14 import platform
15 import pprint
16 import subprocess
17 import xml.etree.ElementTree as ET
18
19 from apex.common import utils as common_utils
20 from apex.virtual import configure_vm as vm_lib
21 from virtualbmc import manager as vbmc_lib
22
23 DEFAULT_RAM = 8192
24 DEFAULT_PM_PORT = 6230
25 DEFAULT_USER = 'admin'
26 DEFAULT_PASS = 'password'
27 DEFAULT_VIRT_IP = '192.168.122.1'
28
29
30 def get_virt_ip():
31     try:
32         virsh_net_xml = subprocess.check_output(['virsh', 'net-dumpxml',
33                                                  'default'],
34                                                 stderr=subprocess.STDOUT)
35     except subprocess.CalledProcessError:
36         logging.warning('Unable to detect default virsh network IP.  Will '
37                         'use 192.168.122.1')
38         return DEFAULT_VIRT_IP
39
40     tree = ET.fromstring(virsh_net_xml)
41     ip_tag = tree.find('ip')
42     if ip_tag is not None:
43         virsh_ip = ip_tag.get('address')
44         if virsh_ip:
45             logging.debug("Detected virsh default network ip: "
46                           "{}".format(virsh_ip))
47             return virsh_ip
48
49     return DEFAULT_VIRT_IP
50
51
52 def generate_inventory(target_file, ha_enabled=False, num_computes=1,
53                        controller_ram=DEFAULT_RAM, arch=platform.machine(),
54                        compute_ram=DEFAULT_RAM, vcpus=4):
55     """
56     Generates inventory file for virtual deployments
57     :param target_file:
58     :param ha_enabled:
59     :param num_computes:
60     :param controller_ram:
61     :param arch:
62     :param compute_ram:
63     :param vcpus:
64     :return:
65     """
66
67     node = {'mac_address': '',
68             'ipmi_ip': get_virt_ip(),
69             'ipmi_user': DEFAULT_USER,
70             'ipmi_pass': DEFAULT_PASS,
71             'pm_type': 'pxe_ipmitool',
72             'pm_port': '',
73             'cpu': vcpus,
74             'memory': DEFAULT_RAM,
75             'disk': 41,
76             'arch': arch,
77             'capabilities': ''
78             }
79
80     inv_output = {'nodes': {}}
81     if ha_enabled:
82         num_ctrlrs = 3
83     else:
84         num_ctrlrs = 1
85
86     for idx in range(num_ctrlrs + num_computes):
87         tmp_node = copy.deepcopy(node)
88         tmp_node['mac_address'] = vm_lib.generate_baremetal_macs(1)[0]
89         tmp_node['pm_port'] = DEFAULT_PM_PORT + idx
90         if idx < num_ctrlrs:
91             tmp_node['capabilities'] = 'profile:control'
92             tmp_node['memory'] = controller_ram
93         else:
94             tmp_node['capabilities'] = 'profile:compute'
95             tmp_node['memory'] = compute_ram
96         inv_output['nodes']['node{}'.format(idx)] = copy.deepcopy(tmp_node)
97
98     common_utils.dump_yaml(inv_output, target_file)
99     logging.info('Virtual environment file created: {}'.format(target_file))
100     return inv_output
101
102
103 def host_setup(node):
104     """
105     Handles configuring vmbc and firewalld/iptables
106     :param node: dictionary of domain names and ports for ipmi
107     :return:
108     """
109     vbmc_manager = vbmc_lib.VirtualBMCManager()
110     for name, port in node.items():
111         vbmc_manager.add(username=DEFAULT_USER, password=DEFAULT_PASS,
112                          port=port, address=get_virt_ip(), domain_name=name,
113                          libvirt_uri='qemu:///system',
114                          libvirt_sasl_password=False,
115                          libvirt_sasl_username=False)
116
117         # TODO(trozet): add support for firewalld
118         try:
119             subprocess.check_call(['systemctl', 'stop', 'firewalld'])
120             subprocess.check_call(['systemctl', 'restart', 'libvirtd'])
121         except subprocess.CalledProcessError:
122             logging.warning('Failed to stop firewalld and restart libvirtd')
123         # iptables rule
124         rule = iptc.Rule()
125         rule.protocol = 'udp'
126         match = rule.create_match('udp')
127         match.dport = str(port)
128         rule.add_match(match)
129         rule.target = iptc.Target(rule, "ACCEPT")
130         chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "INPUT")
131         chain.insert_rule(rule)
132         try:
133             subprocess.check_call(['vbmc', 'start', name])
134             logging.debug("Started vbmc for domain {}".format(name))
135         except subprocess.CalledProcessError:
136             logging.error("Failed to start vbmc for {}".format(name))
137             raise
138     logging.debug('vmbcs setup: {}'.format(vbmc_manager.list()))
139
140
141 def virt_customize(ops, target):
142     """
143     Helper function to virt customize disks
144     :param ops: list of of operations and arguments
145     :param target: target disk to modify
146     :return: None
147     """
148     logging.info("Virt customizing target disk: {}".format(target))
149     virt_cmd = ['virt-customize']
150     for op in ops:
151         for op_cmd, op_arg in op.items():
152             virt_cmd.append(op_cmd)
153             virt_cmd.append(op_arg)
154     virt_cmd.append('-a')
155     virt_cmd.append(target)
156     if not os.path.isfile(target):
157         raise FileNotFoundError
158     my_env = os.environ.copy()
159     my_env['LIBGUESTFS_BACKEND'] = 'direct'
160     logging.debug("Virt-customizing with: \n{}".format(virt_cmd))
161     try:
162         logging.debug(subprocess.check_output(virt_cmd, env=my_env,
163                                               stderr=subprocess.STDOUT))
164     except subprocess.CalledProcessError as e:
165         logging.error("Error executing virt-customize: {}".format(
166                       pprint.pformat(e.output)))
167         raise