diff options
Diffstat (limited to 'cloudinit/sources/DataSourceSmartOS.py')
-rw-r--r-- | cloudinit/sources/DataSourceSmartOS.py | 555 |
1 files changed, 313 insertions, 242 deletions
diff --git a/cloudinit/sources/DataSourceSmartOS.py b/cloudinit/sources/DataSourceSmartOS.py index 9b16bf8d..40f915fa 100644 --- a/cloudinit/sources/DataSourceSmartOS.py +++ b/cloudinit/sources/DataSourceSmartOS.py @@ -32,55 +32,51 @@ import socket from cloudinit import dmi from cloudinit import log as logging -from cloudinit import serial -from cloudinit import sources -from cloudinit import subp -from cloudinit import util +from cloudinit import serial, sources, subp, util from cloudinit.event import EventScope, EventType LOG = logging.getLogger(__name__) SMARTOS_ATTRIB_MAP = { # Cloud-init Key : (SmartOS Key, Strip line endings) - 'instance-id': ('sdc:uuid', True), - 'local-hostname': ('hostname', True), - 'public-keys': ('root_authorized_keys', True), - 'user-script': ('user-script', False), - 'legacy-user-data': ('user-data', False), - 'user-data': ('cloud-init:user-data', False), - 'iptables_disable': ('iptables_disable', True), - 'motd_sys_info': ('motd_sys_info', True), - 'availability_zone': ('sdc:datacenter_name', True), - 'vendor-data': ('sdc:vendor-data', False), - 'operator-script': ('sdc:operator-script', False), - 'hostname': ('sdc:hostname', True), - 'dns_domain': ('sdc:dns_domain', True), + "instance-id": ("sdc:uuid", True), + "local-hostname": ("hostname", True), + "public-keys": ("root_authorized_keys", True), + "user-script": ("user-script", False), + "legacy-user-data": ("user-data", False), + "user-data": ("cloud-init:user-data", False), + "iptables_disable": ("iptables_disable", True), + "motd_sys_info": ("motd_sys_info", True), + "availability_zone": ("sdc:datacenter_name", True), + "vendor-data": ("sdc:vendor-data", False), + "operator-script": ("sdc:operator-script", False), + "hostname": ("sdc:hostname", True), + "dns_domain": ("sdc:dns_domain", True), } SMARTOS_ATTRIB_JSON = { # Cloud-init Key : (SmartOS Key known JSON) - 'network-data': 'sdc:nics', - 'dns_servers': 'sdc:resolvers', - 'routes': 'sdc:routes', + "network-data": "sdc:nics", + "dns_servers": "sdc:resolvers", + "routes": "sdc:routes", } SMARTOS_ENV_LX_BRAND = "lx-brand" SMARTOS_ENV_KVM = "kvm" -DS_NAME = 'SmartOS' -DS_CFG_PATH = ['datasource', DS_NAME] +DS_NAME = "SmartOS" +DS_CFG_PATH = ["datasource", DS_NAME] NO_BASE64_DECODE = [ - 'iptables_disable', - 'motd_sys_info', - 'root_authorized_keys', - 'sdc:datacenter_name', - 'sdc:uuid' - 'user-data', - 'user-script', + "iptables_disable", + "motd_sys_info", + "root_authorized_keys", + "sdc:datacenter_name", + "sdc:uuiduser-data", + "user-script", ] -METADATA_SOCKFILE = '/native/.zonecontrol/metadata.sock' -SERIAL_DEVICE = '/dev/ttyS1' +METADATA_SOCKFILE = "/native/.zonecontrol/metadata.sock" +SERIAL_DEVICE = "/dev/ttyS1" SERIAL_TIMEOUT = 60 # BUILT-IN DATASOURCE CONFIGURATION @@ -98,24 +94,26 @@ SERIAL_TIMEOUT = 60 # fs_setup: describes how to format the ephemeral drive # BUILTIN_DS_CONFIG = { - 'serial_device': SERIAL_DEVICE, - 'serial_timeout': SERIAL_TIMEOUT, - 'metadata_sockfile': METADATA_SOCKFILE, - 'no_base64_decode': NO_BASE64_DECODE, - 'base64_keys': [], - 'base64_all': False, - 'disk_aliases': {'ephemeral0': '/dev/vdb'}, + "serial_device": SERIAL_DEVICE, + "serial_timeout": SERIAL_TIMEOUT, + "metadata_sockfile": METADATA_SOCKFILE, + "no_base64_decode": NO_BASE64_DECODE, + "base64_keys": [], + "base64_all": False, + "disk_aliases": {"ephemeral0": "/dev/vdb"}, } BUILTIN_CLOUD_CONFIG = { - 'disk_setup': { - 'ephemeral0': {'table_type': 'mbr', - 'layout': False, - 'overwrite': False} + "disk_setup": { + "ephemeral0": { + "table_type": "mbr", + "layout": False, + "overwrite": False, + } }, - 'fs_setup': [{'label': 'ephemeral0', - 'filesystem': 'ext4', - 'device': 'ephemeral0'}], + "fs_setup": [ + {"label": "ephemeral0", "filesystem": "ext4", "device": "ephemeral0"} + ], } # builtin vendor-data is a boothook that writes a script into @@ -170,18 +168,23 @@ class DataSourceSmartOS(sources.DataSource): smartos_type = sources.UNSET md_client = sources.UNSET - default_update_events = {EventScope.NETWORK: { - EventType.BOOT_NEW_INSTANCE, - EventType.BOOT, - EventType.BOOT_LEGACY - }} + default_update_events = { + EventScope.NETWORK: { + EventType.BOOT_NEW_INSTANCE, + EventType.BOOT, + EventType.BOOT_LEGACY, + } + } def __init__(self, sys_cfg, distro, paths): sources.DataSource.__init__(self, sys_cfg, distro, paths) - self.ds_cfg = util.mergemanydict([ - self.ds_cfg, - util.get_cfg_by_path(sys_cfg, DS_CFG_PATH, {}), - BUILTIN_DS_CONFIG]) + self.ds_cfg = util.mergemanydict( + [ + self.ds_cfg, + util.get_cfg_by_path(sys_cfg, DS_CFG_PATH, {}), + BUILTIN_DS_CONFIG, + ] + ) self.metadata = {} self.network_data = None @@ -204,25 +207,28 @@ class DataSourceSmartOS(sources.DataSource): if self.md_client == sources.UNSET: self.md_client = jmc_client_factory( smartos_type=self.smartos_type, - metadata_sockfile=self.ds_cfg['metadata_sockfile'], - serial_device=self.ds_cfg['serial_device'], - serial_timeout=self.ds_cfg['serial_timeout']) + metadata_sockfile=self.ds_cfg["metadata_sockfile"], + serial_device=self.ds_cfg["serial_device"], + serial_timeout=self.ds_cfg["serial_timeout"], + ) def _set_provisioned(self): - '''Mark the instance provisioning state as successful. + """Mark the instance provisioning state as successful. When run in a zone, the host OS will look for /var/svc/provisioning to be renamed as /var/svc/provision_success. This should be done after meta-data is successfully retrieved and from this point the host considers the provision of the zone to be a success and keeps the zone running. - ''' + """ - LOG.debug('Instance provisioning state set as successful') - svc_path = '/var/svc' - if os.path.exists('/'.join([svc_path, 'provisioning'])): - os.rename('/'.join([svc_path, 'provisioning']), - '/'.join([svc_path, 'provision_success'])) + LOG.debug("Instance provisioning state set as successful") + svc_path = "/var/svc" + if os.path.exists("/".join([svc_path, "provisioning"])): + os.rename( + "/".join([svc_path, "provisioning"]), + "/".join([svc_path, "provision_success"]), + ) def _get_data(self): self._init() @@ -235,8 +241,10 @@ class DataSourceSmartOS(sources.DataSource): return False if not self.md_client.exists(): - LOG.debug("No metadata device '%r' found for SmartOS datasource", - self.md_client) + LOG.debug( + "No metadata device '%r' found for SmartOS datasource", + self.md_client, + ) return False # Open once for many requests, rather than once for each request @@ -259,24 +267,33 @@ class DataSourceSmartOS(sources.DataSource): # We write 'user-script' and 'operator-script' into the # instance/data directory. The default vendor-data then handles # executing them later. - data_d = os.path.join(self.paths.get_cpath(), 'instances', - md['instance-id'], 'data') - user_script = os.path.join(data_d, 'user-script') + data_d = os.path.join( + self.paths.get_cpath(), "instances", md["instance-id"], "data" + ) + user_script = os.path.join(data_d, "user-script") u_script_l = "%s/user-script" % LEGACY_USER_D - write_boot_content(md.get('user-script'), content_f=user_script, - link=u_script_l, shebang=True, mode=0o700) - - operator_script = os.path.join(data_d, 'operator-script') - write_boot_content(md.get('operator-script'), - content_f=operator_script, shebang=False, - mode=0o700) + write_boot_content( + md.get("user-script"), + content_f=user_script, + link=u_script_l, + shebang=True, + mode=0o700, + ) + + operator_script = os.path.join(data_d, "operator-script") + write_boot_content( + md.get("operator-script"), + content_f=operator_script, + shebang=False, + mode=0o700, + ) # @datadictionary: This key has no defined format, but its value # is written to the file /var/db/mdata-user-data on each boot prior # to the phase that runs user-script. This file is not to be executed. # This allows a configuration file of some kind to be injected into # the machine to be consumed by the user-script when it runs. - u_data = md.get('legacy-user-data') + u_data = md.get("legacy-user-data") u_data_f = "%s/mdata-user-data" % LEGACY_USER_D write_boot_content(u_data, u_data_f) @@ -284,38 +301,39 @@ class DataSourceSmartOS(sources.DataSource): # The hostname may or may not be qualified with the local domain name. # This follows section 3.14 of RFC 2132. - if not md['local-hostname']: - if md['hostname']: - md['local-hostname'] = md['hostname'] + if not md["local-hostname"]: + if md["hostname"]: + md["local-hostname"] = md["hostname"] else: - md['local-hostname'] = md['instance-id'] + md["local-hostname"] = md["instance-id"] ud = None - if md['user-data']: - ud = md['user-data'] - - if not md['vendor-data']: - md['vendor-data'] = BUILTIN_VENDOR_DATA % { - 'user_script': user_script, - 'operator_script': operator_script, - 'per_boot_d': os.path.join(self.paths.get_cpath("scripts"), - 'per-boot'), + if md["user-data"]: + ud = md["user-data"] + + if not md["vendor-data"]: + md["vendor-data"] = BUILTIN_VENDOR_DATA % { + "user_script": user_script, + "operator_script": operator_script, + "per_boot_d": os.path.join( + self.paths.get_cpath("scripts"), "per-boot" + ), } self.metadata = util.mergemanydict([md, self.metadata]) self.userdata_raw = ud - self.vendordata_raw = md['vendor-data'] - self.network_data = md['network-data'] - self.routes_data = md['routes'] + self.vendordata_raw = md["vendor-data"] + self.network_data = md["network-data"] + self.routes_data = md["routes"] self._set_provisioned() return True def _get_subplatform(self): - return 'serial (%s)' % SERIAL_DEVICE + return "serial (%s)" % SERIAL_DEVICE def device_name_to_device(self, name): - return self.ds_cfg['disk_aliases'].get(name) + return self.ds_cfg["disk_aliases"].get(name) def get_config_obj(self): if self.smartos_type == SMARTOS_ENV_KVM: @@ -323,7 +341,7 @@ class DataSourceSmartOS(sources.DataSource): return {} def get_instance_id(self): - return self.metadata['instance-id'] + return self.metadata["instance-id"] @property def network_config(self): @@ -333,12 +351,12 @@ class DataSourceSmartOS(sources.DataSource): if self._network_config is None: if self.network_data is not None: - self._network_config = ( - convert_smartos_network_data( - network_data=self.network_data, - dns_servers=self.metadata['dns_servers'], - dns_domain=self.metadata['dns_domain'], - routes=self.routes_data)) + self._network_config = convert_smartos_network_data( + network_data=self.network_data, + dns_servers=self.metadata["dns_servers"], + dns_domain=self.metadata["dns_domain"], + routes=self.routes_data, + ) return self._network_config @@ -357,10 +375,12 @@ class JoyentMetadataClient(object): The full specification can be found at http://eng.joyent.com/mdata/protocol.html """ + line_regex = re.compile( - r'V2 (?P<length>\d+) (?P<checksum>[0-9a-f]+)' - r' (?P<body>(?P<request_id>[0-9a-f]+) (?P<status>SUCCESS|NOTFOUND)' - r'( (?P<payload>.+))?)') + r"V2 (?P<length>\d+) (?P<checksum>[0-9a-f]+)" + r" (?P<body>(?P<request_id>[0-9a-f]+) (?P<status>SUCCESS|NOTFOUND)" + r"( (?P<payload>.+))?)" + ) def __init__(self, smartos_type=None, fp=None): if smartos_type is None: @@ -369,43 +389,50 @@ class JoyentMetadataClient(object): self.fp = fp def _checksum(self, body): - return '{0:08x}'.format( - binascii.crc32(body.encode('utf-8')) & 0xffffffff) + return "{0:08x}".format( + binascii.crc32(body.encode("utf-8")) & 0xFFFFFFFF + ) def _get_value_from_frame(self, expected_request_id, frame): frame_data = self.line_regex.match(frame).groupdict() - if int(frame_data['length']) != len(frame_data['body']): + if int(frame_data["length"]) != len(frame_data["body"]): raise JoyentMetadataFetchException( - 'Incorrect frame length given ({0} != {1}).'.format( - frame_data['length'], len(frame_data['body']))) - expected_checksum = self._checksum(frame_data['body']) - if frame_data['checksum'] != expected_checksum: + "Incorrect frame length given ({0} != {1}).".format( + frame_data["length"], len(frame_data["body"]) + ) + ) + expected_checksum = self._checksum(frame_data["body"]) + if frame_data["checksum"] != expected_checksum: raise JoyentMetadataFetchException( - 'Invalid checksum (expected: {0}; got {1}).'.format( - expected_checksum, frame_data['checksum'])) - if frame_data['request_id'] != expected_request_id: + "Invalid checksum (expected: {0}; got {1}).".format( + expected_checksum, frame_data["checksum"] + ) + ) + if frame_data["request_id"] != expected_request_id: raise JoyentMetadataFetchException( - 'Request ID mismatch (expected: {0}; got {1}).'.format( - expected_request_id, frame_data['request_id'])) - if not frame_data.get('payload', None): - LOG.debug('No value found.') + "Request ID mismatch (expected: {0}; got {1}).".format( + expected_request_id, frame_data["request_id"] + ) + ) + if not frame_data.get("payload", None): + LOG.debug("No value found.") return None - value = util.b64d(frame_data['payload']) + value = util.b64d(frame_data["payload"]) LOG.debug('Value "%s" found.', value) return value def _readline(self): """ - Reads a line a byte at a time until \n is encountered. Returns an - ascii string with the trailing newline removed. + Reads a line a byte at a time until \n is encountered. Returns an + ascii string with the trailing newline removed. - If a timeout (per-byte) is set and it expires, a - JoyentMetadataFetchException will be thrown. + If a timeout (per-byte) is set and it expires, a + JoyentMetadataFetchException will be thrown. """ response = [] def as_ascii(): - return b''.join(response).decode('ascii') + return b"".join(response).decode("ascii") msg = "Partial response: '%s'" while True: @@ -413,7 +440,7 @@ class JoyentMetadataClient(object): byte = self.fp.read(1) if len(byte) == 0: raise JoyentMetadataTimeoutException(msg % as_ascii()) - if byte == b'\n': + if byte == b"\n": return as_ascii() response.append(byte) except OSError as exc: @@ -424,26 +451,33 @@ class JoyentMetadataClient(object): raise def _write(self, msg): - self.fp.write(msg.encode('ascii')) + self.fp.write(msg.encode("ascii")) self.fp.flush() def _negotiate(self): - LOG.debug('Negotiating protocol V2') - self._write('NEGOTIATE V2\n') + LOG.debug("Negotiating protocol V2") + self._write("NEGOTIATE V2\n") response = self._readline() LOG.debug('read "%s"', response) - if response != 'V2_OK': + if response != "V2_OK": raise JoyentMetadataFetchException( - 'Invalid response "%s" to "NEGOTIATE V2"' % response) - LOG.debug('Negotiation complete') + 'Invalid response "%s" to "NEGOTIATE V2"' % response + ) + LOG.debug("Negotiation complete") def request(self, rtype, param=None): - request_id = '{0:08x}'.format(random.randint(0, 0xffffffff)) - message_body = ' '.join((request_id, rtype,)) + request_id = "{0:08x}".format(random.randint(0, 0xFFFFFFFF)) + message_body = " ".join( + ( + request_id, + rtype, + ) + ) if param: - message_body += ' ' + base64.b64encode(param.encode()).decode() - msg = 'V2 {0} {1} {2}\n'.format( - len(message_body), self._checksum(message_body), message_body) + message_body += " " + base64.b64encode(param.encode()).decode() + msg = "V2 {0} {1} {2}\n".format( + len(message_body), self._checksum(message_body), message_body + ) LOG.debug('Writing "%s" to metadata transport.', msg) need_close = False @@ -458,14 +492,14 @@ class JoyentMetadataClient(object): LOG.debug('Read "%s" from metadata transport.', response) - if 'SUCCESS' not in response: + if "SUCCESS" not in response: return None value = self._get_value_from_frame(request_id, response) return value def get(self, key, default=None, strip=False): - result = self.request(rtype='GET', param=key) + result = self.request(rtype="GET", param=key) if result is None: return default if result and strip: @@ -479,18 +513,19 @@ class JoyentMetadataClient(object): return json.loads(result) def list(self): - result = self.request(rtype='KEYS') + result = self.request(rtype="KEYS") if not result: return [] - return result.split('\n') + return result.split("\n") def put(self, key, val): - param = b' '.join([base64.b64encode(i.encode()) - for i in (key, val)]).decode() - return self.request(rtype='PUT', param=param) + param = b" ".join( + [base64.b64encode(i.encode()) for i in (key, val)] + ).decode() + return self.request(rtype="PUT", param=param) def delete(self, key): - return self.request(rtype='DELETE', param=key) + return self.request(rtype="DELETE", param=key) def close_transport(self): if self.fp: @@ -519,7 +554,7 @@ class JoyentMetadataSocketClient(JoyentMetadataClient): def open_transport(self): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(self.socketpath) - self.fp = sock.makefile('rwb') + self.fp = sock.makefile("rwb") self._negotiate() def exists(self): @@ -530,8 +565,9 @@ class JoyentMetadataSocketClient(JoyentMetadataClient): class JoyentMetadataSerialClient(JoyentMetadataClient): - def __init__(self, device, timeout=10, smartos_type=SMARTOS_ENV_KVM, - fp=None): + def __init__( + self, device, timeout=10, smartos_type=SMARTOS_ENV_KVM, fp=None + ): super(JoyentMetadataSerialClient, self).__init__(smartos_type, fp) self.device = device self.timeout = timeout @@ -550,7 +586,7 @@ class JoyentMetadataSerialClient(JoyentMetadataClient): self._negotiate() def _flush(self): - LOG.debug('Flushing input') + LOG.debug("Flushing input") # Read any pending data timeout = self.fp.timeout self.fp.timeout = 0.1 @@ -559,7 +595,7 @@ class JoyentMetadataSerialClient(JoyentMetadataClient): self._readline() except JoyentMetadataTimeoutException: break - LOG.debug('Input empty') + LOG.debug("Input empty") # Send a newline and expect "invalid command". Keep trying until # successful. Retry rather frequently so that the "Is the host @@ -571,24 +607,29 @@ class JoyentMetadataSerialClient(JoyentMetadataClient): self.fp.timeout = timeout while True: LOG.debug('Writing newline, expecting "invalid command"') - self._write('\n') + self._write("\n") try: response = self._readline() - if response == 'invalid command': + if response == "invalid command": break - if response == 'FAILURE': + if response == "FAILURE": LOG.debug('Got "FAILURE". Retrying.') continue LOG.warning('Unexpected response "%s" during flush', response) except JoyentMetadataTimeoutException: - LOG.warning('Timeout while initializing metadata client. ' - 'Is the host metadata service running?') + LOG.warning( + "Timeout while initializing metadata client. " + "Is the host metadata service running?" + ) LOG.debug('Got "invalid command". Flush complete.') self.fp.timeout = timeout def __repr__(self): return "%s(device=%s, timeout=%s)" % ( - self.__class__.__name__, self.device, self.timeout) + self.__class__.__name__, + self.device, + self.timeout, + ) class JoyentMetadataLegacySerialClient(JoyentMetadataSerialClient): @@ -620,7 +661,7 @@ class JoyentMetadataLegacySerialClient(JoyentMetadataSerialClient): keys = None if self.base64_all is None: keys = self.list() - if 'base64_all' in keys: + if "base64_all" in keys: self.base64_all = util.is_true(self._get("base64_all")) else: self.base64_all = False @@ -633,7 +674,7 @@ class JoyentMetadataLegacySerialClient(JoyentMetadataSerialClient): if keys is None: keys = self.list() b64_keys = set() - if 'base64_keys' in keys: + if "base64_keys" in keys: b64_keys = set(self._get("base64_keys").split(",")) # now add any b64-<keyname> that has a true value @@ -647,8 +688,9 @@ class JoyentMetadataLegacySerialClient(JoyentMetadataSerialClient): self.base64_keys = b64_keys def _get(self, key, default=None, strip=False): - return (super(JoyentMetadataLegacySerialClient, self). - get(key, default=default, strip=strip)) + return super(JoyentMetadataLegacySerialClient, self).get( + key, default=default, strip=strip + ) def is_b64_encoded(self, key, reset=False): if key in NO_BASE64_DECODE: @@ -680,9 +722,12 @@ class JoyentMetadataLegacySerialClient(JoyentMetadataSerialClient): def jmc_client_factory( - smartos_type=None, metadata_sockfile=METADATA_SOCKFILE, - serial_device=SERIAL_DEVICE, serial_timeout=SERIAL_TIMEOUT, - uname_version=None): + smartos_type=None, + metadata_sockfile=METADATA_SOCKFILE, + serial_device=SERIAL_DEVICE, + serial_timeout=SERIAL_TIMEOUT, + uname_version=None, +): if smartos_type is None: smartos_type = get_smartos_environ(uname_version) @@ -691,11 +736,14 @@ def jmc_client_factory( return None elif smartos_type == SMARTOS_ENV_KVM: return JoyentMetadataLegacySerialClient( - device=serial_device, timeout=serial_timeout, - smartos_type=smartos_type) + device=serial_device, + timeout=serial_timeout, + smartos_type=smartos_type, + ) elif smartos_type == SMARTOS_ENV_LX_BRAND: - return JoyentMetadataSocketClient(socketpath=metadata_sockfile, - smartos_type=smartos_type) + return JoyentMetadataSocketClient( + socketpath=metadata_sockfile, smartos_type=smartos_type + ) raise ValueError("Unknown value for smartos_type: %s" % smartos_type) @@ -708,12 +756,14 @@ def identify_file(content_f): LOG.debug("script %s mime type is %s", content_f, f_type) except subp.ProcessExecutionError as e: util.logexc( - LOG, ("Failed to identify script type for %s" % content_f, e)) + LOG, ("Failed to identify script type for %s" % content_f, e) + ) return None if f_type is None else f_type.strip() -def write_boot_content(content, content_f, link=None, shebang=False, - mode=0o400): +def write_boot_content( + content, content_f, link=None, shebang=False, mode=0o400 +): """ Write the content to content_f. Under the following rules: 1. If no content, remove the file @@ -747,7 +797,8 @@ def write_boot_content(content, content_f, link=None, shebang=False, f_type = identify_file(content_f) if f_type == "text/plain": util.write_file( - content_f, "\n".join(["#!/bin/bash", content]), mode=mode) + content_f, "\n".join(["#!/bin/bash", content]), mode=mode + ) LOG.debug("added shebang to file %s", content_f) if link: @@ -768,7 +819,7 @@ def get_smartos_environ(uname_version=None, product_name=None): # report 'BrandZ virtual linux' as the kernel version if uname_version is None: uname_version = uname[3] - if uname_version == 'BrandZ virtual linux': + if uname_version == "BrandZ virtual linux": return SMARTOS_ENV_LX_BRAND if product_name is None: @@ -776,16 +827,16 @@ def get_smartos_environ(uname_version=None, product_name=None): else: system_type = product_name - if system_type and system_type.startswith('SmartDC'): + if system_type and system_type.startswith("SmartDC"): return SMARTOS_ENV_KVM return None # Convert SMARTOS 'sdc:nics' data to network_config yaml -def convert_smartos_network_data(network_data=None, - dns_servers=None, dns_domain=None, - routes=None): +def convert_smartos_network_data( + network_data=None, dns_servers=None, dns_domain=None, routes=None +): """Return a dictionary of network_config by parsing provided SMARTOS sdc:nics configuration data @@ -810,28 +861,28 @@ def convert_smartos_network_data(network_data=None, """ valid_keys = { - 'physical': [ - 'mac_address', - 'mtu', - 'name', - 'params', - 'subnets', - 'type', + "physical": [ + "mac_address", + "mtu", + "name", + "params", + "subnets", + "type", ], - 'subnet': [ - 'address', - 'broadcast', - 'dns_nameservers', - 'dns_search', - 'metric', - 'pointopoint', - 'routes', - 'scope', - 'type', + "subnet": [ + "address", + "broadcast", + "dns_nameservers", + "dns_search", + "metric", + "pointopoint", + "routes", + "scope", + "type", ], - 'route': [ - 'network', - 'gateway', + "route": [ + "network", + "gateway", ], } @@ -851,56 +902,64 @@ def convert_smartos_network_data(network_data=None, routes = [] def is_valid_ipv4(addr): - return '.' in addr + return "." in addr def is_valid_ipv6(addr): - return ':' in addr + return ":" in addr pgws = { - 'ipv4': {'match': is_valid_ipv4, 'gw': None}, - 'ipv6': {'match': is_valid_ipv6, 'gw': None}, + "ipv4": {"match": is_valid_ipv4, "gw": None}, + "ipv6": {"match": is_valid_ipv6, "gw": None}, } config = [] for nic in network_data: - cfg = dict((k, v) for k, v in nic.items() - if k in valid_keys['physical']) - cfg.update({ - 'type': 'physical', - 'name': nic['interface']}) - if 'mac' in nic: - cfg.update({'mac_address': nic['mac']}) + cfg = dict( + (k, v) for k, v in nic.items() if k in valid_keys["physical"] + ) + cfg.update({"type": "physical", "name": nic["interface"]}) + if "mac" in nic: + cfg.update({"mac_address": nic["mac"]}) subnets = [] - for ip in nic.get('ips', []): + for ip in nic.get("ips", []): if ip == "dhcp": - subnet = {'type': 'dhcp4'} + subnet = {"type": "dhcp4"} else: routeents = [] - subnet = dict((k, v) for k, v in nic.items() - if k in valid_keys['subnet']) - subnet.update({ - 'type': 'static', - 'address': ip, - }) - - proto = 'ipv4' if is_valid_ipv4(ip) else 'ipv6' + subnet = dict( + (k, v) for k, v in nic.items() if k in valid_keys["subnet"] + ) + subnet.update( + { + "type": "static", + "address": ip, + } + ) + + proto = "ipv4" if is_valid_ipv4(ip) else "ipv6" # Only use gateways for 'primary' nics - if 'primary' in nic and nic.get('primary', False): + if "primary" in nic and nic.get("primary", False): # the ips and gateways list may be N to M, here # we map the ip index into the gateways list, # and handle the case that we could have more ips # than gateways. we only consume the first gateway - if not pgws[proto]['gw']: - gateways = [gw for gw in nic.get('gateways', []) - if pgws[proto]['match'](gw)] + if not pgws[proto]["gw"]: + gateways = [ + gw + for gw in nic.get("gateways", []) + if pgws[proto]["match"](gw) + ] if len(gateways): - pgws[proto]['gw'] = gateways[0] - subnet.update({'gateway': pgws[proto]['gw']}) + pgws[proto]["gw"] = gateways[0] + subnet.update({"gateway": pgws[proto]["gw"]}) for route in routes: - rcfg = dict((k, v) for k, v in route.items() - if k in valid_keys['route']) + rcfg = dict( + (k, v) + for k, v in route.items() + if k in valid_keys["route"] + ) # Linux uses the value of 'gateway' to determine # automatically if the route is a forward/next-hop # (non-local IP for gateway) or an interface/resolver @@ -913,25 +972,29 @@ def convert_smartos_network_data(network_data=None, # to see if it's in the prefix. We can then smartly # add or not-add this route. But for now, # when in doubt, use brute force! Routes for everyone! - rcfg.update({'network': route['dst']}) + rcfg.update({"network": route["dst"]}) routeents.append(rcfg) - subnet.update({'routes': routeents}) + subnet.update({"routes": routeents}) subnets.append(subnet) - cfg.update({'subnets': subnets}) + cfg.update({"subnets": subnets}) config.append(cfg) if dns_servers: config.append( - {'type': 'nameserver', 'address': dns_servers, - 'search': dns_domain}) + { + "type": "nameserver", + "address": dns_servers, + "search": dns_domain, + } + ) - return {'version': 1, 'config': config} + return {"version": 1, "config": config} # Used to match classes to dependencies datasources = [ - (DataSourceSmartOS, (sources.DEP_FILESYSTEM, )), + (DataSourceSmartOS, (sources.DEP_FILESYSTEM,)), ] @@ -942,13 +1005,17 @@ def get_datasource_list(depends): if __name__ == "__main__": import sys + jmc = jmc_client_factory() if jmc is None: print("Do not appear to be on smartos.") sys.exit(1) if len(sys.argv) == 1: - keys = (list(SMARTOS_ATTRIB_JSON.keys()) + - list(SMARTOS_ATTRIB_MAP.keys()) + ['network_config']) + keys = ( + list(SMARTOS_ATTRIB_JSON.keys()) + + list(SMARTOS_ATTRIB_MAP.keys()) + + ["network_config"] + ) else: keys = sys.argv[1:] @@ -960,14 +1027,19 @@ if __name__ == "__main__": keyname = SMARTOS_ATTRIB_JSON[key] data[key] = client.get_json(keyname) elif key == "network_config": - for depkey in ('network-data', 'dns_servers', 'dns_domain', - 'routes'): + for depkey in ( + "network-data", + "dns_servers", + "dns_domain", + "routes", + ): load_key(client, depkey, data) data[key] = convert_smartos_network_data( - network_data=data['network-data'], - dns_servers=data['dns_servers'], - dns_domain=data['dns_domain'], - routes=data['routes']) + network_data=data["network-data"], + dns_servers=data["dns_servers"], + dns_domain=data["dns_domain"], + routes=data["routes"], + ) else: if key in SMARTOS_ATTRIB_MAP: keyname, strip = SMARTOS_ATTRIB_MAP[key] @@ -981,7 +1053,6 @@ if __name__ == "__main__": for key in keys: load_key(client=jmc, key=key, data=data) - print(json.dumps(data, indent=1, sort_keys=True, - separators=(',', ': '))) + print(json.dumps(data, indent=1, sort_keys=True, separators=(",", ": "))) # vi: ts=4 expandtab |