Просмотр исходного кода

Improves Red Hat network config

Alessandro Pilotti 10 лет назад
Родитель
Сommit
8201dbba85
4 измененных файлов с 107 добавлено и 13 удалено
  1. 12 0
      coriolis/osmorphing/base.py
  2. 7 4
      coriolis/osmorphing/manager.py
  3. 73 9
      coriolis/osmorphing/redhat.py
  4. 15 0
      coriolis/utils.py

+ 12 - 0
coriolis/osmorphing/base.py

@@ -1,6 +1,7 @@
 import abc
 import itertools
 import os
+import uuid
 
 from coriolis import utils
 
@@ -26,6 +27,10 @@ class BaseOSMorphingTools(object):
         path = os.path.join(self._os_root_dir, chroot_path)
         utils.write_ssh_file(self._ssh, path, content)
 
+    def _list_dir(self, chroot_path):
+        path = os.path.join(self._os_root_dir, chroot_path)
+        return utils.list_ssh_dir(self._ssh, path)
+
     def _exec_cmd(self, cmd):
         return utils.exec_ssh_cmd(self._ssh, cmd)
 
@@ -39,6 +44,13 @@ class BaseOSMorphingTools(object):
         except:
             return False
 
+    def _write_file_sudo(self, chroot_path, content):
+        # NOTE: writes the file to a temp location due to permission issues
+        tmp_file = 'tmp/%s' % str(uuid.uuid4())
+        self._write_file(tmp_file, content)
+        self._exec_cmd_chroot("cp /%s /%s" % (tmp_file, chroot_path))
+        self._exec_cmd_chroot("rm /%s" % tmp_file)
+
     @abc.abstractmethod
     def check_os(self):
         pass

+ 7 - 4
coriolis/osmorphing/manager.py

@@ -12,15 +12,17 @@ def morph_image(connection_info, target_hypervisor, target_platform,
                 volume_devs):
     (ip, port, username, pkey) = connection_info
 
-    LOG.info("Waiting for connectivity on host: %s:%s", (ip, port))
+    LOG.info("Waiting for connectivity on host: %(ip)s:%(port)s",
+             {"ip": ip, "port": port})
     utils.wait_for_port_connectivity(ip, port)
 
-    LOG.info("Connecting to host: %s:%s", (ip, port))
+    LOG.info("Connecting to host: %(ip)s:%(port)s", {"ip": ip, "port": port})
     ssh = paramiko.SSHClient()
     ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
     ssh.connect(hostname=ip, port=port, username=username, pkey=pkey)
 
     os_mount_tools = osmount_factory.get_os_mount_tools(ssh)
+    LOG.info("Discovering and mounting OS partitions")
     os_root_dir, other_mounted_dirs = os_mount_tools.mount_os(ssh, volume_devs)
     os_morphing_tools, os_info = osmorphing_factory.get_os_morphing_tools(
         ssh, os_root_dir, target_hypervisor, target_platform)
@@ -35,14 +37,15 @@ def morph_image(connection_info, target_hypervisor, target_platform,
      packages_remove) = os_morphing_tools.get_packages()
 
     if packages_add:
-        LOG.info("Adding packages: %s" % str(packages_add))
+        LOG.info("Adding packages: %s", str(packages_add))
         os_morphing_tools.install_packages(packages_add)
 
     if packages_remove:
-        LOG.info("Removing packages: %s" % str(packages_remove))
+        LOG.info("Removing packages: %s", str(packages_remove))
         os_morphing_tools.uninstall_packages(packages_remove)
 
     LOG.info("Post packages")
     os_morphing_tools.post_packages_install()
 
+    LOG.info("Dismounting OS partitions")
     os_mount_tools.dismount_os(ssh, other_mounted_dirs + [os_root_dir])

+ 73 - 9
coriolis/osmorphing/redhat.py

@@ -1,12 +1,19 @@
+import os
 import re
+import uuid
 
 from oslo_log import log as logging
 
 from coriolis import constants
 from coriolis.osmorphing import base
+from coriolis import utils
 
 LOG = logging.getLogger(__name__)
 
+RELEASE_RHEL = "Red Hat Enterprise Linux Server"
+RELEASE_CENTOS = "CentOS Linux"
+RELEASE_FEDORA = "Fedora"
+
 
 class RedHatMorphingTools(base.BaseOSMorphingTools):
     _packages = {
@@ -18,6 +25,7 @@ class RedHatMorphingTools(base.BaseOSMorphingTools):
             ("cloud-utils-growpart", False)],
     }
     _CLOUD_INIT_USER = 'centos'
+    _NETWORK_SCRIPTS_PATH = "etc/sysconfig/network-scripts"
 
     def check_os(self):
         redhat_release_path = "etc/redhat-release"
@@ -30,8 +38,25 @@ class RedHatMorphingTools(base.BaseOSMorphingTools):
                 return (distro, version)
 
     def set_dhcp(self):
-        # TODO: set BOOTPROTO="dhcp" in all relevant ifcfg-* configurations
-        pass
+        for ifcfg_file in self._get_net_config_files():
+            ifcfg_content = self._read_file(ifcfg_file).decode()
+            ifcfg = self._get_config(ifcfg_content)
+            if (ifcfg.get("TYPE") == "Ethernet" and
+                    ifcfg.get("BOOTPROTO") == "none"):
+                ifcfg["BOOTPROTO"] = "dhcp"
+                ifcfg["UUID"] = str(uuid.uuid4())
+
+                if 'IPADDR' in ifcfg:
+                    del ifcfg['IPADDR']
+                if 'GATEWAY' in ifcfg:
+                    del ifcfg['GATEWAY']
+                if 'NETMASK' in ifcfg:
+                    del ifcfg['NETMASK']
+                if 'NETWORK' in ifcfg:
+                    del ifcfg['NETWORK']
+
+                ifcfg_updated_content = self._get_config_file_content(ifcfg)
+                self._write_file_sudo(ifcfg_file, ifcfg_updated_content)
 
     def install_packages(self, package_names):
         apt_get_cmd = 'yum install %s -y' % " ".join(package_names)
@@ -57,19 +82,58 @@ class RedHatMorphingTools(base.BaseOSMorphingTools):
                 self._check_user_exists(self._CLOUD_INIT_USER)):
             self._exec_cmd_chroot("useradd %s" % self._CLOUD_INIT_USER)
 
-    def _add_hyperv_ballooning_rule(self):
+    def _add_hyperv_ballooning_udev_rules(self):
         udev_file = "etc/udev/rules.d/100-balloon.rules"
         content = 'SUBSYSTEM=="memory", ACTION=="add", ATTR{state}="online"\n'
 
         if (self._hypervisor == constants.HYPERVISOR_HYPERV and
                 not self._test_path(udev_file)):
-            # NOTE: writes the file to a temp location due to permission issues
-            tmp_file = 'tmp/100-balloon.rules'
-            self._write_file(tmp_file, content)
-            self._exec_cmd_chroot("cp /%s /%s" % (tmp_file, udev_file))
-            self._exec_cmd_chroot("rm /%s" % tmp_file)
+            self._write_file_sudo(udev_file, content)
+
+    def _add_net_udev_rules(self):
+        udev_file = "etc/udev/rules.d/70-persistent-net.rules"
+        if not self._test_path(udev_file):
+            net_ifaces_info = self._get_net_ifaces_info()
+            content = utils.get_udev_net_rules(net_ifaces_info)
+            self._write_file_sudo(udev_file, content)
+
+    def _get_config_file_content(self, config):
+        return "%s\n" % "\n".join(
+            ['%s="%s"' % (k, v) for k, v in config.items()])
+
+    def _get_config(self, config_content):
+        config = {}
+        for config_line in config_content.split('\n'):
+            m = re.match('(.*)="?([^"]*)"?', config_line)
+            if m:
+                name, value = m.groups()
+                config[name] = value
+        return config
+
+    def _get_net_config_files(self):
+        dir_content = self._list_dir(self._NETWORK_SCRIPTS_PATH)
+        return [os.path.join(self._NETWORK_SCRIPTS_PATH, f) for f in
+                dir_content if re.match("^ifcfg-(.*)", f)]
+
+    def _get_net_ifaces_info(self):
+        net_ifaces_info = []
+        for ifcfg_file in self._get_net_config_files():
+            ifcfg_content = self._read_file(ifcfg_file).decode()
+            ifcfg = self._get_config(ifcfg_content)
+            if ifcfg.get("TYPE") == "Ethernet":
+                mac_address = ifcfg.get("HWADDR")
+                if not mac_address:
+                    LOG.warn("HWADDR not defined in: %s", ifcfg_file)
+                    continue
+                name = ifcfg.get("NAME")
+                if not name:
+                    # Get the name from the config file
+                    name = re.match("^.*/ifcfg-(.*)", ifcfg_file).groups()[0]
+                net_ifaces_info.append((name, mac_address))
+        return net_ifaces_info
 
     def post_packages_install(self):
+        self._add_net_udev_rules()
+        self._add_hyperv_ballooning_udev_rules()
         self._run_dracut()
         self._add_cloud_init_user()
-        self._add_hyperv_ballooning_rule()

+ 15 - 0
coriolis/utils.py

@@ -42,6 +42,15 @@ def retry_on_error(max_attempts=5, sleep_seconds=0):
     return _retry_on_error
 
 
+def get_udev_net_rules(net_ifaces_info):
+    content = ""
+    for name, mac_address in net_ifaces_info:
+        content += ('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
+                    'ATTR{address}=="%(mac_address)s", NAME="%(name)s"\n' %
+                    {"name": name, "mac_address": mac_address.lower()})
+    return content
+
+
 def get_linux_os_info(ssh):
     out = exec_ssh_cmd(ssh, "lsb_release -a || true").decode()
     dist_id = re.findall('^Distributor ID:\s(.*)$', out, re.MULTILINE)
@@ -74,6 +83,12 @@ def write_ssh_file(ssh, remote_path, content):
     sftp.open(remote_path, 'wb').write(content)
 
 
+@retry_on_error()
+def list_ssh_dir(ssh, remote_path):
+    sftp = ssh.open_sftp()
+    return sftp.listdir(remote_path)
+
+
 @retry_on_error()
 def exec_ssh_cmd(ssh, cmd):
     stdin, stdout, stderr = ssh.exec_command(cmd)