1 ##############################################################################
2 # Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others.
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 ##############################################################################
16 from apex.virtual import virtual_utils as virt_utils
17 from apex.virtual import configure_vm as vm_lib
18 from apex.common import constants
19 from apex.common import utils
22 class ApexUndercloudException(Exception):
28 This class represents an Apex Undercloud VM
30 def __init__(self, image_path, root_pw=None, external_network=False):
32 self.root_pw = root_pw
33 self.external_net = external_network
34 self.volume = os.path.join(constants.LIBVIRT_VOLUME_PATH,
36 self.image_path = image_path
38 if Undercloud._get_vm():
39 logging.error("Undercloud VM already exists. Please clean "
41 raise ApexUndercloudException("Undercloud VM already exists!")
46 conn = libvirt.open('qemu:///system')
48 vm = conn.lookupByName('undercloud')
50 except libvirt.libvirtError:
51 logging.debug("No undercloud VM exists")
56 networks.append('external')
57 self.vm = vm_lib.create_vm(name='undercloud',
59 baremetal_interfaces=networks,
60 direct_boot='overcloud-full',
61 kernel_args=['console=ttyS0',
68 ip_out = self.vm.interfaceAddresses(
69 libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE, 0)
71 for (name, val) in ip_out.items():
72 for ipaddr in val['addrs']:
73 if ipaddr['type'] == libvirt.VIR_IP_ADDR_TYPE_IPV4:
74 self.ip = ipaddr['addr']
82 if self.vm.isActive():
83 logging.info("Undercloud already started")
85 logging.info("Starting undercloud")
87 # give 10 seconds to come up
92 logging.info("Undercloud started. IP Address: {}".format(
95 logging.debug("Did not find undercloud IP in {} "
96 "attempts...".format(x))
99 logging.error("Cannot find IP for Undercloud")
100 raise ApexUndercloudException(
101 "Unable to find IP for undercloud. Check if VM booted "
104 def configure(self, net_settings, playbook, apex_temp_dir):
106 Configures undercloud VM
109 # TODO(trozet): If undercloud install fails we can add a retry
110 logging.info("Configuring Undercloud...")
112 ansible_vars = Undercloud.generate_config(net_settings)
113 ansible_vars['apex_temp_dir'] = apex_temp_dir
114 utils.run_ansible(ansible_vars, playbook, host=self.ip, user='stack')
115 logging.info("Undercloud installed!")
117 def setup_volumes(self):
118 for img_file in ('overcloud-full.vmlinuz', 'overcloud-full.initrd',
120 src_img = os.path.join(self.image_path, img_file)
121 dest_img = os.path.join(constants.LIBVIRT_VOLUME_PATH, img_file)
122 if not os.path.isfile(src_img):
123 raise ApexUndercloudException(
124 "Required source file does not exist:{}".format(src_img))
125 if os.path.exists(dest_img):
127 shutil.copyfile(src_img, dest_img)
129 # TODO(trozet):check if resize needed right now size is 50gb
130 # there is a lib called vminspect which has some dependencies and is
131 # not yet available in pip. Consider switching to this lib later.
132 # execute ansible playbook
134 def inject_auth(self):
136 # virt-customize keys/pws
138 pw_op = "password:{}".format(self.root_pw)
139 virt_ops.append({constants.VIRT_PW: pw_op})
141 virt_ops.append({constants.VIRT_RUN_CMD:
142 'mkdir -p /root/.ssh'})
143 virt_ops.append({constants.VIRT_UPLOAD:
144 '/root/.ssh/id_rsa.pub:/root/.ssh/authorized_keys'})
146 'chmod 600 /root/.ssh/authorized_keys',
147 'restorecon /root/.ssh/authorized_keys',
148 'cp /root/.ssh/authorized_keys /home/stack/.ssh/',
149 'chown stack:stack /home/stack/.ssh/authorized_keys',
150 'chmod 600 /home/stack/.ssh/authorized_keys'
153 virt_ops.append({constants.VIRT_RUN_CMD: cmd})
154 virt_utils.virt_customize(virt_ops, self.volume)
157 def generate_config(ns):
159 Generates a dictionary of settings for configuring undercloud
160 :param ns: network settings to derive undercloud settings
161 :return: dictionary of settings
164 ns_admin = ns['networks']['admin']
165 intro_range = ns['apex']['networks']['admin']['introspection_range']
167 config['undercloud_config'] = [
169 "undercloud_update_packages false",
170 "undercloud_debug false",
171 "undercloud_hostname undercloud.{}".format(ns['dns-domain']),
172 "local_ip {}/{}".format(str(ns_admin['installer_vm']['ip']),
173 str(ns_admin['cidr']).split('/')[1]),
174 "network_gateway {}".format(str(ns_admin['installer_vm']['ip'])),
175 "network_cidr {}".format(str(ns_admin['cidr'])),
176 "dhcp_start {}".format(str(ns_admin['dhcp_range'][0])),
177 "dhcp_end {}".format(str(ns_admin['dhcp_range'][1])),
178 "inspection_iprange {}".format(','.join(intro_range))
181 config['ironic_config'] = [
182 "disk_utils iscsi_verify_attempts 30",
183 "disk_partitioner check_device_max_retries 40"
186 config['nova_config'] = [
187 "dns_domain {}".format(ns['dns-domain']),
188 "dhcp_domain {}".format(ns['dns-domain'])
191 config['neutron_config'] = [
192 "dns_domain {}".format(ns['dns-domain']),
194 # FIXME(trozet): possible bug here with not using external network
195 ns_external = ns['networks']['external'][0]
196 config['external_network'] = {
197 "vlan": ns_external['installer_vm']['vlan'],
198 "ip": ns_external['installer_vm']['ip'],
199 "prefix": str(ns_external['cidr']).split('/')[1],
200 "enabled": ns_external['enabled']
203 # FIXME (trozet): for now hardcoding aarch64 to false
204 config['aarch64'] = False