def _maybe_remove_legacy_eth0(target, path="etc/network/interfaces.d/eth0.cfg"): """Ubuntu cloud images previously included a 'eth0.cfg' that had hard coded content. That file would interfere with the rendered configuration if it was present. if the file does not exist do nothing. If the file exists: - with known content, remove it and warn - with unknown content, leave it and warn """ cfg = util.target_path(target, path=path) if not os.path.exists(cfg): LOG.warn('Failed to find legacy network conf file %s', cfg) return bmsg = "Dynamic networking config may not apply." try: contents = util.load_file(cfg) known_contents = ["auto eth0", "iface eth0 inet dhcp"] lines = [ f.strip() for f in contents.splitlines() if not f.startswith("#") ] if lines == known_contents: util.del_file(cfg) msg = "removed %s with known contents" % cfg else: msg = (bmsg + " '%s' exists with user configured content." % cfg) except Exception: msg = bmsg + " %s exists, but could not be read." % cfg LOG.exception(msg) raise LOG.warn(msg)
def force_devmapper_symlinks(): """Check if /dev/mapper/mpath* files are symlinks, if not trigger udev.""" LOG.debug('Verifying /dev/mapper/mpath* files are symlinks') needs_trigger = [] for mp_id, dm_dev in dmname_to_blkdev_mapping().items(): if mp_id.startswith('mpath'): mapper_path = '/dev/mapper/' + mp_id if not os.path.islink(mapper_path): LOG.warning( 'Found invalid device mapper mp path: %s, removing', mapper_path) util.del_file(mapper_path) needs_trigger.append((mapper_path, dm_dev)) if len(needs_trigger): for (mapper_path, dm_dev) in needs_trigger: LOG.debug('multipath: regenerating symlink for %s (%s)', mapper_path, dm_dev) util.subp([ 'udevadm', 'trigger', '--subsystem-match=block', '--action=add', '/sys/class/block/' + os.path.basename(dm_dev) ]) udev.udevadm_settle(exists=mapper_path) if not os.path.islink(mapper_path): LOG.error('Failed to regenerate udev symlink %s', mapper_path)
def ubuntu_core_curthooks(cfg, target=None): """ Ubuntu-Core 16 images cannot execute standard curthooks Instead we copy in any cloud-init configuration to the 'LABEL=writable' partition mounted at target. """ ubuntu_core_target = os.path.join(target, "system-data") cc_target = os.path.join(ubuntu_core_target, 'etc/cloud/cloud.cfg.d') cloudconfig = cfg.get('cloudconfig', None) if cloudconfig: # remove cloud-init.disabled, if found cloudinit_disable = os.path.join(ubuntu_core_target, 'etc/cloud/cloud-init.disabled') if os.path.exists(cloudinit_disable): util.del_file(cloudinit_disable) handle_cloudconfig(cloudconfig, base_dir=cc_target) netconfig = cfg.get('network', None) if netconfig: LOG.info('Writing network configuration') ubuntu_core_netconfig = os.path.join(cc_target, "50-curtin-networking.cfg") util.write_file(ubuntu_core_netconfig, content=config.dump_config({'network': netconfig}))
def load_products(self, path=None, content_id=None, remove=True): # overridden from ObjectStoreMirrorWriter # the reason is that we have copied here from trunk # is bug 1511364 which is not fixed in all ubuntu versions if content_id: try: dpath = self.products_data_path(content_id) return sutil.load_content(self.source(dpath).read()) except IOError as e: if e.errno != errno.ENOENT: raise except JSONDecodeError: jsonfile = os.path.join(self.out_d, dpath) sys.stderr.write("Decode error in:\n " "content_id=%s\n " "JSON filepath=%s\n" % (content_id, jsonfile)) if remove is True: sys.stderr.write("Removing offending file: %s\n" % jsonfile) util.del_file(jsonfile) sys.stderr.write("Trying to load products again...\n") sys.stderr.flush() return self.load_products(path=path, content_id=content_id, remove=False) raise if path: return {} raise TypeError("unable to load_products with no path")
def _disable_ipv6_privacy_extensions(target, path="etc/sysctl.d/10-ipv6-privacy.conf"): """Ubuntu server image sets a preference to use IPv6 privacy extensions by default; this races with the cloud-image desire to disable them. Resolve this by allowing the cloud-image setting to win. """ LOG.debug('Attempting to remove ipv6 privacy extensions') cfg = util.target_path(target, path=path) if not os.path.exists(cfg): LOG.warn('Failed to find ipv6 privacy conf file %s', cfg) return bmsg = "Disabling IPv6 privacy extensions config may not apply." try: contents = util.load_file(cfg) known_contents = [ "net.ipv6.conf.all.use_tempaddr = 2", "net.ipv6.conf.default.use_tempaddr = 2" ] lines = [ f.strip() for f in contents.splitlines() if not f.startswith("#") ] if lines == known_contents: LOG.info('Removing ipv6 privacy extension config file: %s', cfg) util.del_file(cfg) msg = "removed %s with known contents" % cfg curtin_contents = '\n'.join([ "# IPv6 Privacy Extensions (RFC 4941)", "# Disabled by curtin", "# net.ipv6.conf.all.use_tempaddr = 2", "# net.ipv6.conf.default.use_tempaddr = 2" ]) util.write_file(cfg, curtin_contents) else: LOG.debug('skipping removal of %s, expected content not found', cfg) LOG.debug("Found content in file %s:\n%s", cfg, lines) LOG.debug("Expected contents in file %s:\n%s", cfg, known_contents) msg = (bmsg + " '%s' exists with user configured content." % cfg) except Exception as e: msg = bmsg + " %s exists, but could not be read. %s" % (cfg, e) LOG.exception(msg) raise
def apply_apt_proxy_config(cfg, proxy_fname, config_fname): """apply_apt_proxy_config Applies any apt*proxy config from if specified """ # Set up any apt proxy cfgs = (('proxy', 'Acquire::http::Proxy "%s";'), ('http_proxy', 'Acquire::http::Proxy "%s";'), ('ftp_proxy', 'Acquire::ftp::Proxy "%s";'), ('https_proxy', 'Acquire::https::Proxy "%s";')) proxies = [fmt % cfg.get(name) for (name, fmt) in cfgs if cfg.get(name)] if len(proxies): LOG.debug("write apt proxy info to %s", proxy_fname) util.write_file(proxy_fname, '\n'.join(proxies) + '\n') elif os.path.isfile(proxy_fname): util.del_file(proxy_fname) LOG.debug("no apt proxy configured, removed %s", proxy_fname) if cfg.get('conf', None): LOG.debug("write apt config info to %s", config_fname) util.write_file(config_fname, cfg.get('conf')) elif os.path.isfile(config_fname): util.del_file(config_fname) LOG.debug("no apt config configured, removed %s", config_fname)
def centos_apply_network_config(netcfg, target=None): """ CentOS images execute built-in curthooks which only supports simple networking configuration. This hook enables advanced network configuration via config passthrough to the target. """ def cloud_init_repo(version): if not version: raise ValueError('Missing required version parameter') return CLOUD_INIT_YUM_REPO_TEMPLATE % version if netcfg: LOG.info('Removing embedded network configuration (if present)') ifcfgs = glob.glob( util.target_path(target, 'etc/sysconfig/network-scripts') + '/ifcfg-*') # remove ifcfg-* (except ifcfg-lo) for ifcfg in ifcfgs: if os.path.basename(ifcfg) != "ifcfg-lo": util.del_file(ifcfg) LOG.info( 'Checking cloud-init in target [%s] for network ' 'configuration passthrough support.', target) passthrough = net.netconfig_passthrough_available(target) LOG.debug('passthrough available via in-target: %s', passthrough) # if in-target cloud-init is not updated, upgrade via cloud-init repo if not passthrough: cloud_init_yum_repo = (util.target_path( target, 'etc/yum.repos.d/curtin-cloud-init.repo')) # Inject cloud-init daily yum repo util.write_file(cloud_init_yum_repo, content=cloud_init_repo(rpm_get_dist_id(target))) # we separate the installation of repository packages (epel, # cloud-init-el-release) as we need a new invocation of yum # to read the newly installed repo files. YUM_CMD = ['yum', '-y', '--noplugins', 'install'] retries = [1] * 30 with util.ChrootableTarget(target) as in_chroot: # ensure up-to-date ca-certificates to handle https mirror # connections in_chroot.subp(YUM_CMD + ['ca-certificates'], capture=True, log_captured=True, retries=retries) in_chroot.subp(YUM_CMD + ['epel-release'], capture=True, log_captured=True, retries=retries) in_chroot.subp(YUM_CMD + ['cloud-init-el-release'], log_captured=True, capture=True, retries=retries) in_chroot.subp(YUM_CMD + ['cloud-init'], capture=True, log_captured=True, retries=retries) # remove cloud-init el-stable bootstrap repo config as the # cloud-init-el-release package points to the correct repo util.del_file(cloud_init_yum_repo) # install bridge-utils if needed with util.ChrootableTarget(target) as in_chroot: try: in_chroot.subp(['rpm', '-q', 'bridge-utils'], capture=False, rcs=[0]) except util.ProcessExecutionError: LOG.debug('Image missing bridge-utils package, installing') in_chroot.subp(YUM_CMD + ['bridge-utils'], capture=True, log_captured=True, retries=retries) LOG.info('Passing network configuration through to target') net.render_netconfig_passthrough(target, netconfig={'network': netcfg})