]> Frank Brehm's Git Trees - pixelpark/create-vmware-tpl.git/commitdiff
Considering, that a MAC address in DHCP could have multiple leases. 2.6.1
authorFrank Brehm <frank@brehm-online.com>
Mon, 4 Jul 2022 15:21:19 +0000 (17:21 +0200)
committerFrank Brehm <frank@brehm-online.com>
Mon, 4 Jul 2022 15:21:19 +0000 (17:21 +0200)
lib/cr_vmware_tpl/__init__.py
lib/cr_vmware_tpl/cobbler.py
lib/cr_vmware_tpl/handler.py

index d35d3959934c3acc4f755f3be47dc344ed775856..a591ccd3b878af6288fb60afd378af276c6f095c 100644 (file)
@@ -3,7 +3,7 @@
 
 import time
 
-__version__ = '2.6.0'
+__version__ = '2.6.1'
 
 DEFAULT_CONFIG_DIR = 'pixelpark'
 DEFAULT_DISTRO_ARCH = 'x86_64'
index 53d7f17844f92584a462b6b2d7d587de186a52d9..ed52ac6bbdbd0075b5afec352b8c3ce635347d6a 100644 (file)
@@ -48,7 +48,7 @@ from .config import CrTplConfiguration
 
 from .xlate import XLATOR
 
-__version__ = '0.8.9'
+__version__ = '0.9.0'
 
 LOG = logging.getLogger(__name__)
 
@@ -949,6 +949,7 @@ class Cobbler(BaseHandler):
         ks_meta_list.append("SWAP_SIZE_MB={}".format(self.cfg.swap_size_mb))
         ks_meta_list.append("SYSTEM_STATUS={}".format(status))
         ks_meta_list.append("WS_REL_FILESDIR={}".format(self.cfg.cobbler_ws_rel_filesdir))
+        ks_meta_list.append("COBBLER_URL=http://{}".format(self.cfg.cobbler_host))
 
         ks_meta = None
         if ks_meta_list:
@@ -1151,6 +1152,50 @@ class Cobbler(BaseHandler):
             return assigments[mac]
         return None
 
+    # -------------------------------------------------------------------------
+    def get_dhcp_ips(self, mac_address):
+
+        mac = mac_address.lower()
+        LOG.debug(_("Trying to get IP of MAC address {!r} given by DHCP ...").format(mac))
+        all_leases = self.get_remote_filecontent(self.dhcpd_leases_file)
+
+        ips = []
+
+        cur_ip = None
+        assigments = {}
+        re_lease_start = re.compile(r'^\s*lease\s+((?:\d{1,3}\.){3}\d{1,3})\s+', re.IGNORECASE)
+        re_mac = re.compile(
+            r'^\s*hardware\s+ethernet\s+((?:[0-9a-f]{2}:){5}[0-9a-f]{2})\s*;', re.IGNORECASE)
+
+        for line in all_leases.splitlines():
+            match = re_lease_start.match(line)
+            if match:
+                try:
+                    ip = ipaddress.ip_address(match.group(1))
+                    cur_ip = str(ip)
+                except ValueError as e:
+                    msg = _("Found invalid IP address {ip!r} in leases file: {err}").format(
+                        ip=match.group(1), err=e)
+                    LOG.error(msg)
+                continue
+
+            match = re_mac.match(line)
+            if match:
+                found_mac = match.group(1).lower()
+                if cur_ip:
+                    assigments[cur_ip] = found_mac
+                continue
+
+        for ip in assigments.keys():
+            found_mac = assigments[ip]
+            if mac == found_mac:
+                ips.append(ip)
+
+        if self.verbose > 2:
+            LOG.debug(_("Found DHCP IP assignments:") + "\n" + pp(assigments))
+
+        return ips
+
     # -------------------------------------------------------------------------
     def ensure_webroot(self):
 
index adb24a968d772553932509e3b68184ad747a14cd..fc89a2235b7cd4fb899b376d3bf4705a78cfa629 100644 (file)
@@ -35,10 +35,9 @@ from ldap3.core.exceptions import LDAPException, LDAPBindError
 # Own modules
 
 from fb_tools.common import pp, to_str, is_sequence
-
 from fb_tools.errors import HandlerError, ExpectedHandlerError
-
 from fb_tools.handler import BaseHandler
+from fb_tools.xlate import format_list
 
 from fb_vmware.errors import VSphereExpectedError
 from fb_vmware.errors import VSphereDatacenterNotFoundError
@@ -57,7 +56,7 @@ from .cobbler import Cobbler
 
 from .xlate import XLATOR
 
-__version__ = '2.2.5'
+__version__ = '2.3.0'
 
 LOG = logging.getLogger(__name__)
 TZ = pytz.timezone('Europe/Berlin')
@@ -144,6 +143,7 @@ class CrTplHandler(BaseHandler):
         self.tpl_vm_hostname = None
         self.tpl_macaddress = None
         self.tpl_ip = None
+        self.tpl_ips = []
         self.ts_start_install = None
         self.ts_finish_install = None
         self.initial_sleep = 60
@@ -437,7 +437,7 @@ class CrTplHandler(BaseHandler):
 
             self.vsphere.poweron_vm(self.tpl_vm, max_wait=self.cfg.max_wait_for_poweron_vm)
             self.ts_start_install = time.time()
-            self.eval_tpl_ip()
+            self.eval_tpl_ips()
             self.wait_for_finish_install()
 
             self.show_install_log()
@@ -730,11 +730,11 @@ class CrTplHandler(BaseHandler):
             pool=self.cluster.resource_pool, max_wait=self.cfg.max_wait_for_create_vm)
 
     # -------------------------------------------------------------------------
-    def eval_tpl_ip(self):
+    def eval_tpl_ips(self):
 
         LOG.info(_("Trying to evaluate the IP address of the template VM ..."))
 
-        initial_delay = self.vm_boot_delay_secs + 10
+        initial_delay = (2 * self.vm_boot_delay_secs) + 120
 
         LOG.debug(_("Waiting initially for {} seconds:").format(initial_delay))
         print('   ==> ', end='', flush=True)
@@ -750,14 +750,14 @@ class CrTplHandler(BaseHandler):
             cur_duration = cur_time - start_time
         print('', flush=True)
 
-        self.tpl_ip = self.cobbler.get_dhcp_ip(self.tpl_macaddress)
-        if not self.tpl_ip:
+        self.tpl_ips = self.cobbler.get_dhcp_ips(self.tpl_macaddress)
+        if not self.tpl_ips:
             msg = _(
                 "Did not got the IP address of MAC address {mac!r} after "
                 "{delay} seconds.").format(mac=self.tpl_macaddress, delay=initial_delay)
             raise ExpectedHandlerError(msg)
 
-        LOG.info(_("Got IP address {!r} for template VM.").format(self.tpl_ip))
+        LOG.info(_("Got IP addresses for template VM:") + ' ' + format_list(self.tpl_ips))
 
     # -------------------------------------------------------------------------
     def wait_for_finish_install(self):
@@ -782,19 +782,21 @@ class CrTplHandler(BaseHandler):
 
         LOG.debug(_("Waiting for SSH available ..."))
 
-        addr_infos = socket.getaddrinfo(self.tpl_ip, 22, socket.AF_INET, socket.SOCK_STREAM)
-        if self.verbose > 1:
-            msg = _("Got following address_infos for {h!r}, IPv4 TCP port {p}:").format(
-                h=self.tpl_ip, p=22)
-            msg += '\n' + pp(addr_infos)
-            LOG.debug(msg)
-        if not addr_infos:
-            raise HandlerError(_("Did not get address infos for {h!r}, IPv4 TCP port {p}.").format(
-                h=self.tpl_ip, p=22))
+        addr_infos = {}
+        for ip in self.tpl_ips:
+            ai = socket.getaddrinfo(ip, 22, socket.AF_INET, socket.SOCK_STREAM)
+            if self.verbose > 1:
+                msg = _("Got following address_infos for {h!r}, IPv4 TCP port {p}:").format(
+                    h=ip, p=22)
+                msg += '\n' + pp(ai)
+                LOG.debug(msg)
+            if not ai:
+                raise HandlerError(_("Did not get address infos for {h!r}, IPv4 TCP port {p}.").format(
+                    h=ip, p=22))
 
-        addr_info = random.choice(addr_infos)
-        LOG.debug(_("Using address info: {}").format(pp(addr_info)))
-        family, socktype, proto, canonname, sockaddr = addr_info
+            addr_info = random.choice(ai)
+            LOG.debug(_("Using address info: {}").format(pp(addr_info)))
+            addr_infos[ip] = addr_info
 
         if self.verbose <= 3:
             print('   ==> ', end='', flush=True)
@@ -823,38 +825,19 @@ class CrTplHandler(BaseHandler):
                     i = 0
                 last_dot = cur_time
 
-            if self.verbose > 3:
-                LOG.debug(_("Trying to connect to {a} via TCP port {p} ...").format(
-                    a=sockaddr[0], p=sockaddr[1]))
-
-            try:
-                sock = socket.socket(family, socktype, proto)
-            except socket.error as e:
-                sock = None
-                LOG.warn(_("Error creating socket: {}").format(e))
-                continue
+            for ip in addr_infos.keys():
 
-            try:
-                sock.connect(sockaddr)
-            except socket.error as e:
-                sock.close()
-                sock = None
-                if self.verbose > 3:
-                    LOG.debug(_("Could not connect: {}").format(e))
+                addr_info = addr_infos[ip]
+                if self.check_ssh_available(addr_info):
+                    ssh_available = True
+                    self.tpl_ip = ip
+                    break
+
+            if not ssh_available:
                 continue
 
             if self.verbose <= 3:
                 print('', flush=True)
-
-            LOG.info(_("Connected to {a} via TCP port {p}.").format(
-                a=sockaddr[0], p=sockaddr[1]))
-            data = sock.recv(4096)
-            if data:
-                msg = to_str(data).strip()
-                LOG.info(_("Got SSHD banner: {}").format(msg))
-            sock.close()
-            sock = None
-            ssh_available = True
             self.ts_finish_install = time.time()
 
         self.ts_finish_install = time.time()
@@ -870,10 +853,52 @@ class CrTplHandler(BaseHandler):
                 _("SSH not available after {:0.1f} seconds, giving up.").format(duration))
 
     # -------------------------------------------------------------------------
-    def exec_remote(self, cmd):
+    def check_ssh_available(self, addr_info):
+
+        family, socktype, proto, canonname, sockaddr = addr_info
+
+        if self.verbose > 3:
+            LOG.debug(_("Trying to connect to {a} via TCP port {p} ...").format(
+                a=sockaddr[0], p=sockaddr[1]))
+
+        try:
+            sock = socket.socket(family, socktype, proto)
+        except socket.error as e:
+            sock = None
+            LOG.warn(_("Error creating socket: {}").format(e))
+            return False
+
+        try:
+            sock.connect(sockaddr)
+        except socket.error as e:
+            sock.close()
+            sock = None
+            if self.verbose > 3:
+                LOG.debug(_("Could not connect: {}").format(e))
+            return False
+
+        LOG.info(_("Connected to {a} via TCP port {p}.").format(
+            a=sockaddr[0], p=sockaddr[1]))
+
+        data = sock.recv(4096)
+        if data:
+            msg = to_str(data).strip()
+            LOG.info(_("Got SSHD banner: {}").format(msg))
+
+        sock.close()
+        sock = None
+
+        return True
+
+    # -------------------------------------------------------------------------
+    def exec_remote(self, cmd, strict_host_key_checking=False):
 
         ssh = None
         result = {'out': None, 'err': None}
+        if strict_host_key_checking:
+            policy = paramiko.client.AutoAddPolicy()
+        else:
+            policy = paramiko.client.MissingHostKeyPolicy()
 
         try:
 
@@ -881,8 +906,9 @@ class CrTplHandler(BaseHandler):
             ssh = paramiko.SSHClient()
             LOG.debug(_("Loading SSH system host keys."))
             ssh.load_system_host_keys()
-            LOG.debug(_("Setting SSH missing host key policy to {}.").format('AutoAddPolicy'))
-            ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
+            LOG.debug(_("Setting SSH missing host key policy to {}.").format(
+                policy.__class__.__name__))
+            ssh.set_missing_host_key_policy(policy)
 
             LOG.debug(_("Connecting to {h!r}, port {p} as {u!r} per SSH ...").format(
                 h=self.tpl_ip, p=self.ssh_port, u=self.ssh_user))