summaryrefslogtreecommitdiffstats
path: root/apex/virtual/utils.py
blob: 8b24bc404682f6d57ad8eee566e2182b2239d71e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
##############################################################################
# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################

import copy
import iptc
import logging
import os
import platform
import pprint
import subprocess
import xml.etree.ElementTree as ET

from apex.common import utils as common_utils
from apex.virtual import configure_vm as vm_lib
from apex.virtual import exceptions as exc
from time import sleep
from virtualbmc import manager as vbmc_lib

DEFAULT_RAM = 8192
DEFAULT_PM_PORT = 6230
DEFAULT_USER = 'admin'
DEFAULT_PASS = 'password'
DEFAULT_VIRT_IP = '192.168.122.1'


def get_virt_ip():
    try:
        virsh_net_xml = subprocess.check_output(['virsh', 'net-dumpxml',
                                                 'default'],
                                                stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        logging.warning('Unable to detect default virsh network IP.  Will '
                        'use 192.168.122.1')
        return DEFAULT_VIRT_IP

    tree = ET.fromstring(virsh_net_xml)
    ip_tag = tree.find('ip')
    if ip_tag is not None:
        virsh_ip = ip_tag.get('address')
        if virsh_ip:
            logging.debug("Detected virsh default network ip: "
                          "{}".format(virsh_ip))
            return virsh_ip

    return DEFAULT_VIRT_IP


def generate_inventory(target_file, ha_enabled=False, num_computes=1,
                       controller_ram=DEFAULT_RAM, arch=platform.machine(),
                       compute_ram=DEFAULT_RAM, vcpus=4):
    """
    Generates inventory file for virtual deployments
    :param target_file:
    :param ha_enabled:
    :param num_computes:
    :param controller_ram:
    :param arch:
    :param compute_ram:
    :param vcpus:
    :return:
    """

    node = {'mac_address': '',
            'ipmi_ip': get_virt_ip(),
            'ipmi_user': DEFAULT_USER,
            'ipmi_pass': DEFAULT_PASS,
            'pm_type': 'pxe_ipmitool',
            'pm_port': '',
            'cpu': vcpus,
            'memory': DEFAULT_RAM,
            'disk': 41,
            'arch': arch,
            'capabilities': ''
            }

    inv_output = {'nodes': {}}
    if ha_enabled:
        num_ctrlrs = 3
    else:
        num_ctrlrs = 1

    for idx in range(num_ctrlrs + num_computes):
        tmp_node = copy.deepcopy(node)
        tmp_node['mac_address'] = vm_lib.generate_baremetal_macs(1)[0]
        tmp_node['pm_port'] = DEFAULT_PM_PORT + idx
        if idx < num_ctrlrs:
            tmp_node['capabilities'] = 'profile:control'
            tmp_node['memory'] = controller_ram
        else:
            tmp_node['capabilities'] = 'profile:compute'
            tmp_node['memory'] = compute_ram
        inv_output['nodes']['node{}'.format(idx)] = copy.deepcopy(tmp_node)

    common_utils.dump_yaml(inv_output, target_file)
    logging.info('Virtual environment file created: {}'.format(target_file))
    return inv_output


def host_setup(node):
    """
    Handles configuring vmbc and firewalld/iptables
    :param node: dictionary of domain names and ports for ipmi
    :return:
    """
    vbmc_manager = vbmc_lib.VirtualBMCManager()
    for name, port in node.items():
        vbmc_manager.add(username=DEFAULT_USER, password=DEFAULT_PASS,
                         port=port, address=get_virt_ip(), domain_name=name,
                         libvirt_uri='qemu:///system',
                         libvirt_sasl_password=False,
                         libvirt_sasl_username=False)

        # TODO(trozet): add support for firewalld
        try:
            subprocess.check_call(['systemctl', 'stop', 'firewalld'])
            subprocess.check_call(['systemctl', 'restart', 'libvirtd'])
        except subprocess.CalledProcessError:
            logging.warning('Failed to stop firewalld and restart libvirtd')
        # iptables rule
        rule = iptc.Rule()
        rule.protocol = 'udp'
        match = rule.create_match('udp')
        match.dport = str(port)
        rule.add_match(match)
        rule.target = iptc.Target(rule, "ACCEPT")
        chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "INPUT")
        chain.insert_rule(rule)
        try:
            subprocess.check_call(['vbmc', 'start', name])
            logging.debug("Started VBMC for domain {}".format(name))
        except subprocess.CalledProcessError:
            logging.error("Failed to start VBMC for {}".format(name))
            raise

        logging.info("Checking VBMC {} is up".format(name))
        is_running = False
        for x in range(0, 4):
            logging.debug("Polling to see if VBMC is up, attempt {}".format(x))
            try:
                output = subprocess.check_output(['vbmc', 'show', name],
                                                 stderr=subprocess.STDOUT)
            except subprocess.CalledProcessError:
                logging.warning('Unable to issue "vbmc show" cmd')
                continue
            for line in output.decode('utf-8').split('\n'):
                if 'status' in line:
                    if 'running' in line:
                        is_running = True
                        break
                    else:
                        logging.debug('VBMC status is not "running"')
                    break
            if is_running:
                break
            sleep(1)
        if is_running:
            logging.info("VBMC {} is up and running".format(name))
        else:
            logging.error("Failed to verify VBMC is running")
            raise exc.ApexVirtualException("Failed to bring up vbmc "
                                           "{}".format(name))
    logging.debug('VBMCs setup: {}'.format(vbmc_manager.list()))


def virt_customize(ops, target):
    """
    Helper function to virt customize disks
    :param ops: list of of operations and arguments
    :param target: target disk to modify
    :return: None
    """
    logging.info("Virt customizing target disk: {}".format(target))
    virt_cmd = ['virt-customize']
    for op in ops:
        for op_cmd, op_arg in op.items():
            virt_cmd.append(op_cmd)
            virt_cmd.append(op_arg)
    virt_cmd.append('-a')
    virt_cmd.append(target)
    if not os.path.isfile(target):
        raise FileNotFoundError
    my_env = os.environ.copy()
    my_env['LIBGUESTFS_BACKEND'] = 'direct'
    logging.debug("Virt-customizing with: \n{}".format(virt_cmd))
    try:
        logging.debug(subprocess.check_output(virt_cmd, env=my_env,
                                              stderr=subprocess.STDOUT))
    except subprocess.CalledProcessError as e:
        logging.error("Error executing virt-customize: {}".format(
                      pprint.pformat(e.output)))
        raise