Fixes undercloud install failure with setting hostname
[apex.git] / apex / undercloud / undercloud.py
index 013570d..5ee487c 100644 (file)
@@ -15,6 +15,7 @@ import shutil
 import subprocess
 import time
 
+from apex.builders import undercloud_builder as uc_builder
 from apex.virtual import utils as virt_utils
 from apex.virtual import configure_vm as vm_lib
 from apex.common import constants
@@ -31,8 +32,10 @@ class Undercloud:
     """
     def __init__(self, image_path, template_path,
                  root_pw=None, external_network=False,
-                 image_name='undercloud.qcow2'):
+                 image_name='undercloud.qcow2',
+                 os_version=constants.DEFAULT_OS_VERSION):
         self.ip = None
+        self.os_version = os_version
         self.root_pw = root_pw
         self.external_net = external_network
         self.volume = os.path.join(constants.LIBVIRT_VOLUME_PATH,
@@ -61,27 +64,40 @@ class Undercloud:
         if self.external_net:
             networks.append('external')
         console = 'ttyAMA0' if platform.machine() == 'aarch64' else 'ttyS0'
+        root = 'vda2' if platform.machine() == 'aarch64' else 'sda'
 
         self.vm = vm_lib.create_vm(name='undercloud',
                                    image=self.volume,
                                    baremetal_interfaces=networks,
                                    direct_boot='overcloud-full',
                                    kernel_args=['console={}'.format(console),
-                                                'root=/dev/sda'],
+                                                'root=/dev/{}'.format(root)],
                                    default_network=True,
-                                   template_dir=self.template_path)
+                                   template_dir=self.template_path,
+                                   memory=10240)
         self.setup_volumes()
         self.inject_auth()
 
-    def _set_ip(self):
-        ip_out = self.vm.interfaceAddresses(
+    @staticmethod
+    def _get_ip(vm):
+        ip_out = vm.interfaceAddresses(
             libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE, 0)
         if ip_out:
             for (name, val) in ip_out.items():
                 for ipaddr in val['addrs']:
                     if ipaddr['type'] == libvirt.VIR_IP_ADDR_TYPE_IPV4:
-                        self.ip = ipaddr['addr']
-                        return True
+                        return ipaddr['addr']
+
+    def _set_ip(self):
+        ip = self._get_ip(self.vm)
+        if ip:
+            self.ip = ip
+            return True
+
+    @staticmethod
+    def get_ip():
+        vm = Undercloud._get_vm()
+        return Undercloud._get_ip(vm)
 
     def start(self):
         """
@@ -96,7 +112,7 @@ class Undercloud:
             # give 10 seconds to come up
             time.sleep(10)
         # set IP
-        for x in range(5):
+        for x in range(10):
             if self._set_ip():
                 logging.info("Undercloud started.  IP Address: {}".format(
                     self.ip))
@@ -110,19 +126,37 @@ class Undercloud:
                 "Unable to find IP for undercloud.  Check if VM booted "
                 "correctly")
 
-    def configure(self, net_settings, playbook, apex_temp_dir):
+    def detect_nat(self, net_settings):
+        if self.external_net:
+            net = net_settings['networks'][constants.EXTERNAL_NETWORK][0]
+        else:
+            net = net_settings['networks'][constants.ADMIN_NETWORK]
+        if net['gateway'] == net['installer_vm']['ip']:
+            return True
+        else:
+            return False
+
+    def configure(self, net_settings, deploy_settings,
+                  playbook, apex_temp_dir, virtual_oc=False):
         """
         Configures undercloud VM
-        :param net_setings: Network settings for deployment
+        :param net_settings: Network settings for deployment
+        :param deploy_settings: Deployment settings for deployment
         :param playbook: playbook to use to configure undercloud
         :param apex_temp_dir: temporary apex directory to hold configs/logs
+        :param virtual_oc: Boolean to determine if overcloud is virt
         :return: None
         """
 
         logging.info("Configuring Undercloud...")
         # run ansible
-        ansible_vars = Undercloud.generate_config(net_settings)
+        ansible_vars = Undercloud.generate_config(net_settings,
+                                                  deploy_settings)
         ansible_vars['apex_temp_dir'] = apex_temp_dir
+
+        ansible_vars['nat'] = self.detect_nat(net_settings)
+        ansible_vars['container_client'] = utils.find_container_client(
+            self.os_version)
         try:
             utils.run_ansible(ansible_vars, playbook, host=self.ip,
                               user='stack')
@@ -150,11 +184,19 @@ class Undercloud:
             if os.path.exists(dest_img):
                 os.remove(dest_img)
             shutil.copyfile(src_img, dest_img)
+            if img_file == self.image_name and platform.machine() != 'aarch64':
+                uc_builder.expand_disk(dest_img)
+                self.expand_root_fs()
+
             shutil.chown(dest_img, user='qemu', group='qemu')
             os.chmod(dest_img, 0o0744)
-        # TODO(trozet):check if resize needed right now size is 50gb
+
+    def expand_root_fs(self):
         # there is a lib called vminspect which has some dependencies and is
         # not yet available in pip.  Consider switching to this lib later.
+        logging.debug("Expanding root filesystem on /dev/sda partition")
+        virt_ops = [{constants.VIRT_RUN_CMD: 'xfs_growfs /dev/sda'}]
+        virt_utils.virt_customize(virt_ops, self.volume)
 
     def inject_auth(self):
         virt_ops = list()
@@ -169,39 +211,59 @@ class Undercloud:
                          '/root/.ssh/id_rsa.pub:/root/.ssh/authorized_keys'})
         run_cmds = [
             'chmod 600 /root/.ssh/authorized_keys',
-            'restorecon /root/.ssh/authorized_keys',
+            'restorecon -R -v /root/.ssh',
+            'id -u stack || useradd -m stack',
+            'mkdir -p /home/stack/.ssh',
+            'chown stack:stack /home/stack/.ssh',
             'cp /root/.ssh/authorized_keys /home/stack/.ssh/',
             'chown stack:stack /home/stack/.ssh/authorized_keys',
-            'chmod 600 /home/stack/.ssh/authorized_keys'
+            'chmod 600 /home/stack/.ssh/authorized_keys',
+            'echo "stack       ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers',
+            'touch /etc/cloud/cloud-init.disabled'
         ]
         for cmd in run_cmds:
             virt_ops.append({constants.VIRT_RUN_CMD: cmd})
         virt_utils.virt_customize(virt_ops, self.volume)
 
     @staticmethod
-    def generate_config(ns):
+    def generate_config(ns, ds):
         """
         Generates a dictionary of settings for configuring undercloud
         :param ns: network settings to derive undercloud settings
+        :param ds: deploy settings to derive undercloud settings
         :return: dictionary of settings
         """
 
         ns_admin = ns['networks']['admin']
         intro_range = ns['apex']['networks']['admin']['introspection_range']
         config = dict()
+        # Check if this is an ARM deployment
+        config['aarch64'] = platform.machine() == 'aarch64'
+        # Configuration for undercloud.conf
         config['undercloud_config'] = [
             "enable_ui false",
             "undercloud_update_packages false",
             "undercloud_debug false",
             "inspection_extras false",
+            "ipxe_enabled {}".format(
+                str(ds['global_params'].get('ipxe', True) and
+                    not config['aarch64'])),
             "undercloud_hostname undercloud.{}".format(ns['dns-domain']),
             "local_ip {}/{}".format(str(ns_admin['installer_vm']['ip']),
                                     str(ns_admin['cidr']).split('/')[1]),
-            "network_gateway {}".format(str(ns_admin['installer_vm']['ip'])),
-            "network_cidr {}".format(str(ns_admin['cidr'])),
+            "generate_service_certificate false",
+            "undercloud_ntp_servers {}".format(str(ns['ntp'][0])),
+            "container_images_file "
+            "/home/stack/containers-prepare-parameter.yaml",
+            "undercloud_enable_selinux false"
+        ]
+
+        config['undercloud_network_config'] = [
+            "gateway {}".format(str(ns_admin['installer_vm']['ip'])),
+            "cidr {}".format(str(ns_admin['cidr'])),
             "dhcp_start {}".format(str(ns_admin['dhcp_range'][0])),
             "dhcp_end {}".format(str(ns_admin['dhcp_range'][1])),
-            "inspection_iprange {}".format(','.join(intro_range))
+            "inspection_iprange {}".format(','.join(intro_range)),
         ]
 
         config['ironic_config'] = [
@@ -225,8 +287,35 @@ class Undercloud:
             "prefix": str(ns_external['cidr']).split('/')[1],
             "enabled": ns_external['enabled']
         }
-
-        # Check if this is an ARM deployment
-        config['aarch64'] = platform.machine() == 'aarch64'
+        # We will NAT external network if it is enabled. If external network
+        # is IPv6, we will NAT admin network in case we need IPv4 connectivity
+        # for things like DNS server.
+        if 'external' in ns.enabled_network_list and \
+                ns_external['cidr'].version == 4:
+            nat_cidr = ns_external['cidr']
+        else:
+            nat_cidr = ns['networks']['admin']['cidr']
+        config['nat_cidr'] = str(nat_cidr)
+        if nat_cidr.version == 6:
+            config['nat_network_ipv6'] = True
+        else:
+            config['nat_network_ipv6'] = False
+        config['http_proxy'] = ns.get('http_proxy', '')
+        config['https_proxy'] = ns.get('https_proxy', '')
 
         return config
+
+    def _update_delorean_repo(self):
+        if utils.internet_connectivity():
+            logging.info('Updating delorean repo on Undercloud')
+            delorean_repo = (
+                "https://trunk.rdoproject.org/centos7-{}"
+                "/current-tripleo/delorean.repo".format(self.os_version))
+            cmd = ("curl -L -f -o "
+                   "/etc/yum.repos.d/deloran.repo {}".format(delorean_repo))
+            try:
+                virt_utils.virt_customize([{constants.VIRT_RUN_CMD: cmd}],
+                                          self.volume)
+            except Exception:
+                logging.warning("Failed to download and update delorean repo "
+                                "for Undercloud")