import os import shutil from charmhelpers.core.templating import render import charmhelpers.core.host as ch_host import charmhelpers.fetch as fetch import charmhelpers.core.hookenv as hookenv _IRONIC_USER = "ironic" _IRONIC_GROUP = "ironic" class PXEBootBase(object): TFTP_ROOT = "/tftpboot" HTTP_ROOT = "/httpboot" HTTP_SERVER_CONF = "/etc/nginx/nginx.conf" GRUB_DIR = os.path.join(TFTP_ROOT, "grub") GRUB_CFG = os.path.join(GRUB_DIR, "grub.cfg") MAP_FILE = os.path.join(TFTP_ROOT, "map-file") TFTP_CONFIG = "/etc/default/tftpd-hpa" # This is a file map of source to destination. The destination is # relative to self.TFTP_ROOT FILE_MAP = { "/usr/lib/PXELINUX/pxelinux.0": "pxelinux.0", "/usr/lib/syslinux/modules/bios/chain.c32": "chain.c32", "/usr/lib/syslinux/modules/bios/ldlinux.c32": "ldlinux.c32", "/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed": "grubx64.efi", "/usr/lib/shim/shim.efi.signed": "bootx64.efi", "/usr/lib/ipxe/undionly.kpxe": "undionly.kpxe", "/usr/lib/ipxe/ipxe.efi": "ipxe.efi", } TFTP_PACKAGES = ["tftpd-hpa"] TFTPD_SERVICE = "tftpd-hpa" PACKAGES = [ 'syslinux-common', 'pxelinux', 'grub-efi-amd64-signed', 'shim-signed', 'ipxe', ] HTTPD_PACKAGES = ["nginx"] HTTPD_SERVICE_NAME = "nginx" IRONIC_USER = _IRONIC_USER IRONIC_GROUP = _IRONIC_GROUP def __init__(self, charm_config): self._config = charm_config def get_restart_map(self): return { self.TFTP_CONFIG: [self.TFTPD_SERVICE,], self.MAP_FILE: [self.TFTPD_SERVICE,], self.GRUB_CFG: [self.TFTPD_SERVICE,], } def determine_packages(self): default_packages = ( self.PACKAGES + self.TFTP_PACKAGES + self.HTTPD_PACKAGES) return default_packages def _copy_resources(self): self._ensure_folders() for f in self.FILE_MAP: if os.path.isfile(f) is False: raise ValueError( "Missing required file %s. Package not installes?" % f) shutil.copy( f, os.path.join(self.TFTP_ROOT, self.FILE_MAP[f]), follow_symlinks=True) ch_host.chownr( self.TFTP_ROOT, _IRONIC_USER, _IRONIC_GROUP, chowntopdir=True) def _ensure_folders(self): if os.path.isdir(self.TFTP_ROOT) is False: os.makedirs(self.TFTP_ROOT) if os.path.isdir(self.HTTP_ROOT) is False: os.makedirs(self.HTTP_ROOT) if os.path.isdir(self.GRUB_DIR) is False: os.makedirs(self.GRUB_DIR) ch_host.chownr( self.TFTP_ROOT, _IRONIC_USER, _IRONIC_GROUP, chowntopdir=True) ch_host.chownr( self.HTTP_ROOT, _IRONIC_USER, _IRONIC_GROUP, chowntopdir=True) # def _create_file_map(self): # self._ensure_folders() # render(source='tftp-file-map', # target=self.MAP_FILE, # owner=_IRONIC_USER, # perms=0o664, # context={}) # def _create_grub_cfg(self): # self._ensure_folders() # render(source='grub-efi', # target=os.path.join(self.GRUB_DIR, "grub.cfg"), # owner="root", # perms=0o644, # context={ # "tftpboot": self.TFTP_ROOT, # }) # def _create_tftp_config(self): # cfg_dir = os.path.dirname(self.TFTP_CONFIG) # if os.path.isdir(cfg_dir) is False: # raise Exception("Could not find %s" % cfg_dir) # render(source='tftpd-hpa', # target=self.TFTP_CONFIG, # owner="root", # perms=0o644, # context={ # "tftpboot": self.TFTP_ROOT, # "max_tftp_block_size": self._config.get( # "max_tftp_block_size", 0) # }) # def _enable_ipxe_webserver(self): # if self._config.get("use_ipxe", False) is False: # return # fetch.apt_install(self.HTTPD_PACKAGES, fatal=True) # render(source='nginx.conf', # target=self.HTTP_SERVER_CONF, # owner="root", # perms=0o644, # context={ # "ironic_user": _IRONIC_USER, # "ipxe_http_port": self._config.get( # "ipxe_http_port", 8080), # "httpboot": self.HTTP_ROOT, # }) # def configure_resources(self): # # On Ubuntu 20.04, if IPv6 is not available on the system, # # the tftp-hpa package fails to install properly. We create the # # config beforehand, forcing IPv4. # self._create_tftp_config() # self._create_file_map() # fetch.apt_install(self.TFTP_PACKAGES, fatal=True) # fetch.apt_install(self.PACKAGES, fatal=True) # self._copy_resources() # self._create_grub_cfg() class PXEBootBionic(PXEBootBase): # This is a file map of source to destination. The destination is # relative to self.TFTP_ROOT FILE_MAP = { "/usr/lib/PXELINUX/pxelinux.0": "pxelinux.0", "/usr/lib/syslinux/modules/bios/chain.c32": "chain.c32", "/usr/lib/syslinux/modules/bios/ldlinux.c32": "ldlinux.c32", "/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed": "grubx64.efi", "/usr/lib/shim/shimx64.efi.signed": "bootx64.efi", "/usr/lib/ipxe/undionly.kpxe": "undionly.kpxe", "/usr/lib/ipxe/ipxe.efi": "ipxe.efi", } def get_pxe_config_class(charm_config): # In the future, we may need to make slight adjustments to package # names and/or configuration files, based on the version of Ubuntu # we are installing on. This function serves as a factory which will # return an instance of the proper class to the charm. For now we only # have one class. series = ch_host.get_distrib_codename() if series == "bionic": return PXEBootBionic(charm_config) return PXEBootBase(charm_config)