PNG  IHDR* pHYs+ IDATx]n#; cdLb Ǚ[at¤_:uP}>!Usă cag޿ ֵNu`ݼTâabO7uL&y^wFٝA"l[|ŲHLN밪4*sG3|Dv}?+y߉{OuOAt4Jj.u]Gz*҉sP'VQKbA1u\`& Af;HWj hsO;ogTu uj7S3/QzUr&wS`M$X_L7r2;aE+ώ%vikDA:dR+%KzƉo>eOth$z%: :{WwaQ:wz%4foɹE[9<]#ERINƻv溂E%P1i01 |Jvҗ&{b?9g=^wζXn/lK::90KwrюO\!ջ3uzuGv^;騢wq<Iatv09:tt~hEG`v;3@MNZD.1]L:{ծI3`L(÷ba")Y.iljCɄae#I"1 `3*Bdz>j<fU40⨬%O$3cGt]j%Fߠ_twJ;ABU8vP3uEԑwQ V:h%))LfraqX-ۿX]v-\9I gl8tzX ]ecm)-cgʒ#Uw=Wlێn(0hPP/ӨtQ“&J35 $=]r1{tLuǮ*i0_;NƝ8;-vݏr8+U-kruȕYr0RnC]*ެ(M:]gE;{]tg(#ZJ9y>utRDRMdr9㪩̞zֹb<ģ&wzJM"iI( .ꮅX)Qw:9,i좜\Ԛi7&N0:asϓc];=ΗOӣ APqz93 y $)A*kVHZwBƺnWNaby>XMN*45~ղM6Nvm;A=jֲ.~1}(9`KJ/V F9[=`~[;sRuk]rєT!)iQO)Y$V ی ۤmzWz5IM Zb )ˆC`6 rRa}qNmUfDsWuˤV{ Pݝ'=Kֳbg,UҘVz2ﴻnjNgBb{? ߮tcsͻQuxVCIY۠:(V뺕 ٥2;t`@Fo{Z9`;]wMzU~%UA蛚dI vGq\r82iu +St`cR.6U/M9IENDB`# Author: Ben Howard # # This file is part of cloud-init. See LICENSE file for license information. import json import logging import random from cloudinit import dmi from cloudinit import net as cloudnet from cloudinit import subp, url_helper, util NIC_MAP = {"public": "eth0", "private": "eth1"} LOG = logging.getLogger(__name__) def assign_ipv4_link_local(distro, nic=None): """Bring up NIC using an address using link-local (ip4LL) IPs. On DigitalOcean, the link-local domain is per-droplet routed, so there is no risk of collisions. However, to be more safe, the ip4LL address is random. """ if not nic: nic = get_link_local_nic(distro) LOG.debug("selected interface '%s' for reading metadata", nic) if not nic: raise RuntimeError( "unable to find interfaces to access the" "meta-data server. This droplet is broken." ) addr = "169.254.{0}.{1}/16".format( random.randint(1, 168), random.randint(0, 255) ) ip_addr_cmd = ["ip", "addr", "add", addr, "dev", nic] ip_link_cmd = ["ip", "link", "set", "dev", nic, "up"] if not subp.which("ip"): raise RuntimeError( "No 'ip' command available to configure ip4LL address" ) try: subp.subp(ip_addr_cmd) LOG.debug("assigned ip4LL address '%s' to '%s'", addr, nic) subp.subp(ip_link_cmd) LOG.debug("brought device '%s' up", nic) except Exception: util.logexc( LOG, "ip4LL address assignment of '%s' to '%s' failed." " Droplet networking will be broken", addr, nic, ) raise return nic def get_link_local_nic(distro): nics = [ f for f in cloudnet.get_devicelist() if distro.networking.is_physical(f) ] if not nics: return None return min(nics, key=lambda d: cloudnet.read_sys_net_int(d, "ifindex")) def del_ipv4_link_local(nic=None): """Remove the ip4LL address. While this is not necessary, the ip4LL address is extraneous and confusing to users. """ if not nic: LOG.debug( "no link_local address interface defined, skipping link " "local address cleanup" ) return LOG.debug("cleaning up ipv4LL address") ip_addr_cmd = ["ip", "addr", "flush", "dev", nic] try: subp.subp(ip_addr_cmd) LOG.debug("removed ip4LL addresses from %s", nic) except Exception as e: util.logexc(LOG, "failed to remove ip4LL address from '%s'.", nic, e) def convert_network_configuration(config, dns_servers): """Convert the DigitalOcean Network description into Cloud-init's netconfig format. Example JSON: {'public': [ {'mac': '04:01:58:27:7f:01', 'ipv4': {'gateway': '45.55.32.1', 'netmask': '255.255.224.0', 'ip_address': '45.55.50.93'}, 'anchor_ipv4': { 'gateway': '10.17.0.1', 'netmask': '255.255.0.0', 'ip_address': '10.17.0.9'}, 'type': 'public', 'ipv6': {'gateway': '....', 'ip_address': '....', 'cidr': 64}} ], 'private': [ {'mac': '04:01:58:27:7f:02', 'ipv4': {'gateway': '10.132.0.1', 'netmask': '255.255.0.0', 'ip_address': '10.132.75.35'}, 'type': 'private'} ] } """ def _get_subnet_part(pcfg): subpart = { "type": "static", "control": "auto", "address": pcfg.get("ip_address"), "gateway": pcfg.get("gateway"), } if ":" in pcfg.get("ip_address"): subpart["address"] = "{0}/{1}".format( pcfg.get("ip_address"), pcfg.get("cidr") ) else: subpart["netmask"] = pcfg.get("netmask") return subpart nic_configs = [] macs_to_nics = cloudnet.get_interfaces_by_mac() LOG.debug("nic mapping: %s", macs_to_nics) for n in config: nic = config[n][0] LOG.debug("considering %s", nic) mac_address = nic.get("mac") if mac_address not in macs_to_nics: raise RuntimeError( "Did not find network interface on system " "with mac '%s'. Cannot apply configuration: %s" % (mac_address, nic) ) sysfs_name = macs_to_nics.get(mac_address) nic_type = nic.get("type", "unknown") if_name = NIC_MAP.get(nic_type, sysfs_name) if if_name != sysfs_name: LOG.debug( "Found %s interface '%s' on '%s', assigned name of '%s'", nic_type, mac_address, sysfs_name, if_name, ) else: msg = ( "Found interface '%s' on '%s', which is not a public " "or private interface. Using default system naming." ) LOG.debug(msg, mac_address, sysfs_name) ncfg = { "type": "physical", "mac_address": mac_address, "name": if_name, } subnets = [] for netdef in ("ipv4", "ipv6", "anchor_ipv4", "anchor_ipv6"): raw_subnet = nic.get(netdef, None) if not raw_subnet: continue sub_part = _get_subnet_part(raw_subnet) if nic_type != "public" or "anchor" in netdef: del sub_part["gateway"] subnets.append(sub_part) ncfg["subnets"] = subnets nic_configs.append(ncfg) LOG.debug("nic '%s' configuration: %s", if_name, ncfg) if dns_servers: LOG.debug("added dns servers: %s", dns_servers) nic_configs.append({"type": "nameserver", "address": dns_servers}) return {"version": 1, "config": nic_configs} def read_metadata(url, timeout=2, sec_between=2, retries=30): response = url_helper.readurl( url, timeout=timeout, sec_between=sec_between, retries=retries ) if not response.ok(): raise RuntimeError("unable to read metadata at %s" % url) return json.loads(response.contents.decode()) def read_sysinfo(): # DigitalOcean embeds vendor ID and instance/droplet_id in the # SMBIOS information # Detect if we are on DigitalOcean and return the Droplet's ID vendor_name = dmi.read_dmi_data("system-manufacturer") if vendor_name != "DigitalOcean": return (False, None) droplet_id = dmi.read_dmi_data("system-serial-number") if droplet_id: LOG.debug( "system identified via SMBIOS as DigitalOcean Droplet: %s", droplet_id, ) else: msg = ( "system identified via SMBIOS as a DigitalOcean " "Droplet, but did not provide an ID. Please file a " "support ticket at: " "https://cloud.digitalocean.com/support/tickets/new" ) LOG.critical(msg) raise RuntimeError(msg) return (True, droplet_id)