Fixes ensuring VBMCs are actually running
[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 apex.virtual import exceptions as exc
22 from time import sleep
23 from virtualbmc import manager as vbmc_lib
24
25 DEFAULT_RAM = 8192
26 DEFAULT_PM_PORT = 6230
27 DEFAULT_USER = 'admin'
28 DEFAULT_PASS = 'password'
29 DEFAULT_VIRT_IP = '192.168.122.1'
30
31
32 def get_virt_ip():
33     try:
34         virsh_net_xml = subprocess.check_output(['virsh', 'net-dumpxml',
35                                                  'default'],
36                                                 stderr=subprocess.STDOUT)
37     except subprocess.CalledProcessError:
38         logging.warning('Unable to detect default virsh network IP.  Will '
39                         'use 192.168.122.1')
40         return DEFAULT_VIRT_IP
41
42     tree = ET.fromstring(virsh_net_xml)
43     ip_tag = tree.find('ip')
44     if ip_tag is not None:
45         virsh_ip = ip_tag.get('address')
46         if virsh_ip:
47             logging.debug("Detected virsh default network ip: "
48                           "{}".format(virsh_ip))
49             return virsh_ip
50
51     return DEFAULT_VIRT_IP
52
53
54 def generate_inventory(target_file, ha_enabled=False, num_computes=1,
55                        controller_ram=DEFAULT_RAM, arch=platform.machine(),
56                        compute_ram=DEFAULT_RAM, vcpus=4):
57     """
58     Generates inventory file for virtual deployments
59     :param target_file:
60     :param ha_enabled:
61     :param num_computes:
62     :param controller_ram:
63     :param arch:
64     :param compute_ram:
65     :param vcpus:
66     :return:
67     """
68
69     node = {'mac_address': '',
70             'ipmi_ip': get_virt_ip(),
71             'ipmi_user': DEFAULT_USER,
72             'ipmi_pass': DEFAULT_PASS,
73             'pm_type': 'pxe_ipmitool',
74             'pm_port': '',
75             'cpu': vcpus,
76             'memory': DEFAULT_RAM,
77             'disk': 41,
78             'arch': arch,
79             'capabilities': ''
80             }
81
82     inv_output = {'nodes': {}}
83     if ha_enabled:
84         num_ctrlrs = 3
85     else:
86         num_ctrlrs = 1
87
88     for idx in range(num_ctrlrs + num_computes):
89         tmp_node = copy.deepcopy(node)
90         tmp_node['mac_address'] = vm_lib.generate_baremetal_macs(1)[0]
91         tmp_node['pm_port'] = DEFAULT_PM_PORT + idx
92         if idx < num_ctrlrs:
93             tmp_node['capabilities'] = 'profile:control'
94             tmp_node['memory'] = controller_ram
95         else:
96             tmp_node['capabilities'] = 'profile:compute'
97             tmp_node['memory'] = compute_ram
98         inv_output['nodes']['node{}'.format(idx)] = copy.deepcopy(tmp_node)
99
100     common_utils.dump_yaml(inv_output, target_file)
101     logging.info('Virtual environment file created: {}'.format(target_file))
102     return inv_output
103
104
105 def host_setup(node):
106     """
107     Handles configuring vmbc and firewalld/iptables
108     :param node: dictionary of domain names and ports for ipmi
109     :return:
110     """
111     vbmc_manager = vbmc_lib.VirtualBMCManager()
112     for name, port in node.items():
113         vbmc_manager.add(username=DEFAULT_USER, password=DEFAULT_PASS,
114                          port=port, address=get_virt_ip(), domain_name=name,
115                          libvirt_uri='qemu:///system',
116                          libvirt_sasl_password=False,
117                          libvirt_sasl_username=False)
118
119         # TODO(trozet): add support for firewalld
120         try:
121             subprocess.check_call(['systemctl', 'stop', 'firewalld'])
122             subprocess.check_call(['systemctl', 'restart', 'libvirtd'])
123         except subprocess.CalledProcessError:
124             logging.warning('Failed to stop firewalld and restart libvirtd')
125         # iptables rule
126         rule = iptc.Rule()
127         rule.protocol = 'udp'
128         match = rule.create_match('udp')
129         match.dport = str(port)
130         rule.add_match(match)
131         rule.target = iptc.Target(rule, "ACCEPT")
132         chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "INPUT")
133         chain.insert_rule(rule)
134         try:
135             subprocess.check_call(['vbmc', 'start', name])
136             logging.debug("Started VBMC for domain {}".format(name))
137         except subprocess.CalledProcessError:
138             logging.error("Failed to start VBMC for {}".format(name))
139             raise
140
141         logging.info("Checking VBMC {} is up".format(name))
142         is_running = False
143         for x in range(0, 4):
144             logging.debug("Polling to see if VBMC is up, attempt {}".format(x))
145             try:
146                 output = subprocess.check_output(['vbmc', 'show', name],
147                                                  stderr=subprocess.STDOUT)
148             except subprocess.CalledProcessError:
149                 logging.warning('Unable to issue "vbmc show" cmd')
150                 continue
151             for line in output.decode('utf-8').split('\n'):
152                 if 'status' in line:
153                     if 'running' in line:
154                         is_running = True
155                         break
156                     else:
157                         logging.debug('VBMC status is not "running"')
158                     break
159             if is_running:
160                 break
161             sleep(1)
162         if is_running:
163             logging.info("VBMC {} is up and running".format(name))
164         else:
165             logging.error("Failed to verify VBMC is running")
166             raise exc.ApexVirtualException("Failed to bring up vbmc "
167                                            "{}".format(name))
168     logging.debug('VBMCs setup: {}'.format(vbmc_manager.list()))
169
170
171 def virt_customize(ops, target):
172     """
173     Helper function to virt customize disks
174     :param ops: list of of operations and arguments
175     :param target: target disk to modify
176     :return: None
177     """
178     logging.info("Virt customizing target disk: {}".format(target))
179     virt_cmd = ['virt-customize']
180     for op in ops:
181         for op_cmd, op_arg in op.items():
182             virt_cmd.append(op_cmd)
183             virt_cmd.append(op_arg)
184     virt_cmd.append('-a')
185     virt_cmd.append(target)
186     if not os.path.isfile(target):
187         raise FileNotFoundError
188     my_env = os.environ.copy()
189     my_env['LIBGUESTFS_BACKEND'] = 'direct'
190     logging.debug("Virt-customizing with: \n{}".format(virt_cmd))
191     try:
192         logging.debug(subprocess.check_output(virt_cmd, env=my_env,
193                                               stderr=subprocess.STDOUT))
194     except subprocess.CalledProcessError as e:
195         logging.error("Error executing virt-customize: {}".format(
196                       pprint.pformat(e.output)))
197         raise