def _read_content_path(self, item, decode=False): path = item.get('content_path', '').lstrip("/") path_pieces = path.split("/") valid_pieces = [p for p in path_pieces if len(p)] if not valid_pieces: raise BrokenMetadata("Item %s has no valid content path" % (item)) path = self._path_join(self.base_path, "openstack", *path_pieces) return self._path_read(path, decode=decode)
def _read_ec2_metadata(self): path = self._path_join(self.base_path, 'ec2', 'latest', 'meta-data.json') if not os.path.exists(path): return {} else: try: return util.load_json(self._path_read(path)) except Exception as e: raise BrokenMetadata("Failed to process " "path %s: %s" % (path, e))
def read_v1(self): """Reads a version 1 formatted location. Return a dict with metadata, userdata, dsmode, files and version (1). If not a valid path, raise a NonReadable exception. """ found = {} for name in FILES_V1.keys(): path = self._path_join(self.base_path, name) if os.path.exists(path): found[name] = path if len(found) == 0: raise NonReadable("%s: no files found" % (self.base_path)) md = {} for (name, (key, translator, default)) in FILES_V1.items(): if name in found: path = found[name] try: contents = self._path_read(path) except IOError: raise BrokenMetadata("Failed to read: %s" % path) try: md[key] = translator(contents) except Exception as e: raise BrokenMetadata("Failed to process " "path %s: %s" % (path, e)) else: md[key] = copy.deepcopy(default) keydata = md['authorized_keys'] meta_js = md['meta_js'] # keydata in meta_js is preferred over "injected" keydata = meta_js.get('public-keys', keydata) if keydata: lines = keydata.splitlines() md['public-keys'] = [ l for l in lines if len(l) and not l.startswith("#") ] # config-drive-v1 has no way for openstack to provide the instance-id # so we copy that into metadata from the user input if 'instance-id' in meta_js: md['instance-id'] = meta_js['instance-id'] results = { 'version': 1, 'metadata': md, } # allow the user to specify 'dsmode' in a meta tag if 'dsmode' in meta_js: results['dsmode'] = meta_js['dsmode'] # config-drive-v1 has no way of specifying user-data, so the user has # to cheat and stuff it in a meta tag also. results['userdata'] = meta_js.get('user-data', '') # this implementation does not support files other than # network/interfaces and authorized_keys... results['files'] = {} return results
def read_v2(self): """Reads a version 2 formatted location. Return a dict with metadata, userdata, ec2-metadata, dsmode, network_config, files and version (2). If not a valid location, raise a NonReadable exception. """ load_json_anytype = functools.partial(util.load_json, root_types=(dict, list) + six.string_types) def datafiles(version): files = {} files['metadata'] = ( # File path to read self._path_join("openstack", version, 'meta_data.json'), # Is it required? True, # Translator function (applied after loading) util.load_json, ) files['userdata'] = ( self._path_join("openstack", version, 'user_data'), False, lambda x: x, ) files['vendordata'] = ( self._path_join("openstack", version, 'vendor_data.json'), False, load_json_anytype, ) files['networkdata'] = ( self._path_join("openstack", version, 'network_data.json'), False, load_json_anytype, ) return files results = { 'userdata': '', 'version': 2, } data = datafiles(self._find_working_version()) for (name, (path, required, translator)) in data.items(): path = self._path_join(self.base_path, path) data = None found = False try: data = self._path_read(path) except IOError as e: if not required: LOG.debug("Failed reading optional path %s due" " to: %s", path, e) else: LOG.debug("Failed reading mandatory path %s due" " to: %s", path, e) else: found = True if required and not found: raise NonReadable("Missing mandatory path: %s" % path) if found and translator: try: data = translator(data) except Exception as e: raise BrokenMetadata("Failed to process " "path %s: %s" % (path, e)) if found: results[name] = data metadata = results['metadata'] if 'random_seed' in metadata: random_seed = metadata['random_seed'] try: metadata['random_seed'] = base64.b64decode(random_seed) except (ValueError, TypeError) as e: raise BrokenMetadata("Badly formatted metadata" " random_seed entry: %s" % e) # load any files that were provided files = {} metadata_files = metadata.get('files', []) for item in metadata_files: if 'path' not in item: continue path = item['path'] try: files[path] = self._read_content_path(item) except Exception as e: raise BrokenMetadata("Failed to read provided " "file %s: %s" % (path, e)) results['files'] = files # The 'network_config' item in metadata is a content pointer # to the network config that should be applied. It is just a # ubuntu/debian '/etc/network/interfaces' file. net_item = metadata.get("network_config", None) if net_item: try: content = self._read_content_path(net_item, decode=True) results['network_config'] = content except IOError as e: raise BrokenMetadata("Failed to read network" " configuration: %s" % (e)) # To openstack, user can specify meta ('nova boot --meta=key=value') # and those will appear under metadata['meta']. # if they specify 'dsmode' they're indicating the mode that they intend # for this datasource to operate in. try: results['dsmode'] = metadata['meta']['dsmode'] except KeyError: pass # Read any ec2-metadata (if applicable) results['ec2-metadata'] = self._read_ec2_metadata() # Perform some misc. metadata key renames... for (target_key, source_key, is_required) in KEY_COPIES: if is_required and source_key not in metadata: raise BrokenMetadata("No '%s' entry in metadata" % source_key) if source_key in metadata: metadata[target_key] = metadata.get(source_key) return results
def read_v1(self): """Reads a version 1 formatted location. Return a dict with metadata, userdata, dsmode, files and version (1). If not a valid path, raise a NonReadable exception. """ found = {} for name in FILES_V1.keys(): path = self._path_join(self.base_path, name) if os.path.exists(path): found[name] = path if len(found) == 0: raise NonReadable("%s: no files found" % (self.base_path)) md = {} for (name, (key, translator, default)) in FILES_V1.items(): if name in found: path = found[name] try: contents = self._path_read(path) except IOError as e: raise BrokenMetadata("Failed to read: %s" % path) from e try: # Disable not-callable pylint check; pylint isn't able to # determine that every member of FILES_V1 has a callable in # the appropriate position md[key] = translator(contents) # pylint: disable=E1102 except Exception as e: raise BrokenMetadata("Failed to process path %s: %s" % (path, e)) from e else: md[key] = copy.deepcopy(default) keydata = md["authorized_keys"] meta_js = md["meta_js"] # keydata in meta_js is preferred over "injected" keydata = meta_js.get("public-keys", keydata) if keydata: lines = keydata.splitlines() md["public-keys"] = [ line for line in lines if len(line) and not line.startswith("#") ] # config-drive-v1 has no way for openstack to provide the instance-id # so we copy that into metadata from the user input if "instance-id" in meta_js: md["instance-id"] = meta_js["instance-id"] results = { "version": 1, "metadata": md, } # allow the user to specify 'dsmode' in a meta tag if "dsmode" in meta_js: results["dsmode"] = meta_js["dsmode"] # config-drive-v1 has no way of specifying user-data, so the user has # to cheat and stuff it in a meta tag also. results["userdata"] = meta_js.get("user-data", "") # this implementation does not support files other than # network/interfaces and authorized_keys... results["files"] = {} return results