class Site(base.DrydockPersistentObject, base.DrydockObject): VERSION = '1.0' fields = { 'name': ovo_fields.StringField(), 'status': hd_fields.SiteStatusField(default=hd_fields.SiteStatus.Unknown), 'source': hd_fields.ModelSourceField(), 'tag_definitions': ovo_fields.ObjectField('NodeTagDefinitionList', nullable=True), 'repositories': ovo_fields.ObjectField('RepositoryList', nullable=True), 'authorized_keys': ovo_fields.ListOfStringsField(nullable=True), } def __init__(self, **kwargs): super(Site, self).__init__(**kwargs) def get_id(self): return self.name def get_name(self): return self.name def add_tag_definition(self, tag_definition): self.tag_definitions.append(tag_definition) def add_key(self, key_string): self.authorized_keys.append(key_string)
class BootAction(base.DrydockPersistentObject, base.DrydockObject): VERSION = '1.0' fields = { 'name': ovo_fields.StringField(), 'source': hd_fields.ModelSourceField(nullable=False), 'asset_list': ovo_fields.ObjectField('BootActionAssetList', nullable=False), 'node_filter': ovo_fields.ObjectField('NodeFilterSet', nullable=True), 'target_nodes': ovo_fields.ListOfStringsField(nullable=True), 'signaling': ovo_fields.BooleanField(default=True), } def __init__(self, **kwargs): super().__init__(**kwargs) # NetworkLink keyed by name def get_id(self): return self.get_name() def get_name(self): return self.name def render_assets(self, nodename, site_design, action_id, design_ref, type_filter=None): """Render all of the assets in this bootaction. Render the assets of this bootaction and return them in a list. The ``nodename`` and ``action_id`` will be used to build the context for any assets utilizing the ``template`` pipeline segment. :param nodename: name of the node the assets are destined for :param site_design: a objects.SiteDesign instance holding the design sets :param action_id: a 128-bit ULID action_id of the boot action the assets are part of :param design_ref: the design ref this boot action was initiated under :param type_filter: optional filter of the types of assets to render """ assets = list() for a in self.asset_list: if type_filter is None or (type_filter is not None and a.type == type_filter): a.render(nodename, site_design, action_id, design_ref) assets.append(a) return assets
class HardwareProfile(base.DrydockPersistentObject, base.DrydockObject): VERSION = '1.0' fields = { 'name': ovo_fields.StringField(), 'source': hd_fields.ModelSourceField(), 'site': ovo_fields.StringField(), 'vendor': ovo_fields.StringField(nullable=True), 'generation': ovo_fields.StringField(nullable=True), 'hw_version': ovo_fields.StringField(nullable=True), 'bios_version': ovo_fields.StringField(nullable=True), 'boot_mode': ovo_fields.StringField(nullable=True), 'bootstrap_protocol': ovo_fields.StringField(nullable=True), 'pxe_interface': ovo_fields.StringField(nullable=True), 'devices': ovo_fields.ObjectField('HardwareDeviceAliasList', nullable=True), } def __init__(self, **kwargs): super(HardwareProfile, self).__init__(**kwargs) return # HardwareProfile keyed on name def get_id(self): return self.get_name() def get_name(self): return self.name def resolve_alias(self, alias_type, alias): for d in self.devices: if d.alias == alias and d.bus_type == alias_type: selector = objects.HardwareDeviceSelector() selector.selector_type = "address" selector.address = d.address selector.device_type = d.dev_type return selector return None
class NodeTagDefinition(base.DrydockObject): VERSION = '1.0' fields = { 'tag': ovo_fields.StringField(), 'type': ovo_fields.StringField(), 'definition': ovo_fields.StringField(), 'source': hd_fields.ModelSourceField(), } def __init__(self, **kwargs): super(NodeTagDefinition, self).__init__(**kwargs) # TagDefinition keyed by tag def get_id(self): return self.tag
class HardwareDeviceAlias(base.DrydockObject): VERSION = '1.0' fields = { 'alias': ovo_fields.StringField(), 'source': hd_fields.ModelSourceField(), 'address': ovo_fields.StringField(), 'bus_type': ovo_fields.StringField(), 'dev_type': ovo_fields.StringField(nullable=True), } def __init__(self, **kwargs): super(HardwareDeviceAlias, self).__init__(**kwargs) # HardwareDeviceAlias keyed on alias def get_id(self): return self.alias
class Rack(base.DrydockPersistentObject, base.DrydockObject): VERSION = '1.0' fields = { 'name': obj_fields.StringField(nullable=False), 'site': obj_fields.StringField(nullable=False), 'source': hd_fields.ModelSourceField(nullable=False), 'tor_switches': obj_fields.ObjectField('TorSwitchList', nullable=False), 'location': obj_fields.DictOfStringsField(nullable=False), 'local_networks': obj_fields.ListOfStringsField(nullable=True), } def __init__(self, **kwargs): super().__init__(**kwargs) def get_id(self): return self.get_name() def get_name(self): return self.name
class HostVolume(base.DrydockObject): """Model representing a host logical volume.""" VERSION = '1.0' fields = { 'name': obj_fields.StringField(), 'source': hd_fields.ModelSourceField(), 'lv_uuid': obj_fields.UUIDField(nullable=True), 'size': obj_fields.StringField(nullable=True), 'mountpoint': obj_fields.StringField(nullable=True), 'fstype': obj_fields.StringField(nullable=True, default='ext4'), 'mount_options': obj_fields.StringField(nullable=True, default='defaults'), 'fs_uuid': obj_fields.UUIDField(nullable=True), 'fs_label': obj_fields.StringField(nullable=True), } def __init__(self, **kwargs): super().__init__(**kwargs) # HostVolume keyed by name def get_id(self): return self.get_name() def get_name(self): return self.name def is_sys(self): """Is this LV for root and/or boot?""" if self.mountpoint is not None and self.mountpoint in ['/', '/boot']: return True return False """ Merge two lists of HostVolume models with child_list taking priority when conflicts. If a member of child_list has a name beginning with '!' it indicates that HostPartition should be removed from the merged list """ @staticmethod def merge_lists(child_list, parent_list): if child_list is None: return parent_list if parent_list is None: return child_list effective_list = [] if len(child_list) == 0 and len(parent_list) > 0: for p in parent_list: pp = deepcopy(p) pp.source = hd_fields.ModelSource.Compiled effective_list.append(pp) elif len(parent_list) == 0 and len(child_list) > 0: for i in child_list: if i.get_name().startswith('!'): continue else: ii = deepcopy(i) ii.source = hd_fields.ModelSource.Compiled effective_list.append(ii) elif len(parent_list) > 0 and len(child_list) > 0: inherit_field_list = [ "lv_uuid", "size", "mountpoint", "fstype", "mount_options", "fs_uuid", "fs_label", ] parent_volumes = [] for i in parent_list: parent_name = i.get_name() parent_volumes.append(parent_name) add = True for j in child_list: if j.get_name() == ("!" + parent_name): add = False break elif j.get_name() == parent_name: p = objects.HostPartition() p.name = j.get_name() for f in inherit_field_list: setattr(p, f, objects.Utils.apply_field_inheritance( getattr(j, f, None), getattr(i, f, None))) add = False p.source = hd_fields.ModelSource.Compiled effective_list.append(p) if add: ii = deepcopy(i) ii.source = hd_fields.ModelSource.Compiled effective_list.append(ii) for j in child_list: if (j.get_name() not in parent_volumes and not j.get_name().startswith("!")): jj = deepcopy(j) jj.source = hd_fields.ModelSource.Compiled effective_list.append(jj) return effective_list
class HostProfile(base.DrydockPersistentObject, base.DrydockObject): VERSION = '1.0' fields = { 'name': obj_fields.StringField(nullable=False), 'site': obj_fields.StringField(nullable=False), 'source': hd_fields.ModelSourceField(nullable=False), 'parent_profile': obj_fields.StringField(nullable=True), 'hardware_profile': obj_fields.StringField(nullable=True), 'oob_type': obj_fields.StringField(nullable=True), 'oob_parameters': obj_fields.DictOfStringsField(nullable=True), 'storage_devices': obj_fields.ObjectField('HostStorageDeviceList', nullable=True), 'volume_groups': obj_fields.ObjectField('HostVolumeGroupList', nullable=True), 'interfaces': obj_fields.ObjectField('HostInterfaceList', nullable=True), 'tags': obj_fields.ListOfStringsField(nullable=True), 'owner_data': obj_fields.DictOfStringsField(nullable=True), 'rack': obj_fields.StringField(nullable=True), 'base_os': obj_fields.StringField(nullable=True), 'image': obj_fields.StringField(nullable=True), 'kernel': obj_fields.StringField(nullable=True), 'kernel_params': obj_fields.DictOfStringsField(nullable=True), 'primary_network': obj_fields.StringField(nullable=True), } def __init__(self, **kwargs): super(HostProfile, self).__init__(**kwargs) def get_rack(self): return self.rack # HostProfile is keyed by name def get_id(self): return self.get_name() def get_name(self): return self.name def has_tag(self, tag): if tag in self.tags: return True return False def apply_inheritance(self, site_design): # No parent to inherit from, just apply design values # and return if self.parent_profile is None: self.source = hd_fields.ModelSource.Compiled return parent = site_design.get_host_profile(self.parent_profile) if parent is None: raise NameError("Cannot find parent profile %s for %s" % (self.design['parent_profile'], self.name)) parent.apply_inheritance(site_design) # First compute inheritance for simple fields inheritable_field_list = [ 'hardware_profile', 'oob_type', 'storage_layout', 'bootdisk_device', 'bootdisk_root_size', 'bootdisk_boot_size', 'rack', 'base_os', 'image', 'kernel', 'primary_network' ] # Create applied data from self design values and parent # applied values for f in inheritable_field_list: setattr(self, f, objects.Utils.apply_field_inheritance( getattr(self, f, None), getattr(parent, f, None))) # Now compute inheritance for complex types self.oob_parameters = objects.Utils.merge_dicts( self.oob_parameters, parent.oob_parameters) self.tags = objects.Utils.merge_lists(self.tags, parent.tags) self.owner_data = objects.Utils.merge_dicts(self.owner_data, parent.owner_data) self.kernel_params = objects.Utils.merge_dicts(self.kernel_params, parent.kernel_params) self.storage_devices = HostStorageDeviceList.from_basic_list( HostStorageDevice.merge_lists(self.storage_devices, parent.storage_devices)) self.volume_groups = HostVolumeGroupList.from_basic_list( HostVolumeGroup.merge_lists(self.volume_groups, parent.volume_groups)) self.interfaces = HostInterfaceList.from_basic_list( HostInterface.merge_lists(self.interfaces, parent.interfaces)) self.source = hd_fields.ModelSource.Compiled return
class HostInterface(base.DrydockObject): VERSION = '1.0' fields = { 'device_name': obj_fields.StringField(), 'source': hd_fields.ModelSourceField(), 'network_link': obj_fields.StringField(nullable=True), 'hardware_slaves': obj_fields.ListOfStringsField(nullable=True), 'slave_selectors': obj_fields.ObjectField('HardwareDeviceSelectorList', nullable=True), 'networks': obj_fields.ListOfStringsField(nullable=True), } def __init__(self, **kwargs): super(HostInterface, self).__init__(**kwargs) # HostInterface is keyed by device_name def get_id(self): return self.get_name() def get_name(self): return self.device_name def get_hw_slaves(self): return self.hardware_slaves def get_slave_selectors(self): return self.slave_selectors # Return number of slaves for this interface def get_slave_count(self): return len(self.hardware_slaves) # The device attribute may be hardware alias that translates to a # physical device address. If the device attribute does not match an # alias, we assume it directly identifies a OS device name. When the # apply_hardware_profile method is called on the parent Node of this # device, the selector will be decided and applied def add_selector(self, slave_selector): if self.slave_selectors is None: self.slave_selectors = objects.HardwareDeviceSelectorList() self.slave_selectors.append(slave_selector) """ Merge two lists of HostInterface models with child_list taking priority when conflicts. If a member of child_list has a device_name beginning with '!' it indicates that HostInterface should be removed from the merged list """ @staticmethod def merge_lists(child_list, parent_list): if child_list is None: return parent_list if parent_list is None: return child_list effective_list = [] if len(child_list) == 0 and len(parent_list) > 0: for p in parent_list: pp = deepcopy(p) pp.source = hd_fields.ModelSource.Compiled effective_list.append(pp) elif len(parent_list) == 0 and len(child_list) > 0: for i in child_list: if i.get_name().startswith('!'): continue else: ii = deepcopy(i) ii.source = hd_fields.ModelSource.Compiled effective_list.append(ii) elif len(parent_list) > 0 and len(child_list) > 0: parent_interfaces = [] for i in parent_list: parent_name = i.get_name() parent_interfaces.append(parent_name) add = True for j in child_list: if j.get_name() == ("!" + parent_name): add = False break elif j.get_name() == parent_name: m = objects.HostInterface() m.device_name = j.get_name() m.network_link = \ objects.Utils.apply_field_inheritance( getattr(j, 'network_link', None), getattr(i, 'network_link', None)) s = [ x for x in getattr(i, 'hardware_slaves', []) if ("!" + x ) not in getattr(j, 'hardware_slaves', []) ] s.extend([ x for x in getattr(j, 'hardware_slaves', []) if not x.startswith("!") ]) m.hardware_slaves = s n = [ x for x in getattr(i, 'networks', []) if ("!" + x) not in getattr(j, 'networks', []) ] n.extend([ x for x in getattr(j, 'networks', []) if not x.startswith("!") ]) m.networks = n m.source = hd_fields.ModelSource.Compiled effective_list.append(m) add = False break if add: ii = deepcopy(i) ii.source = hd_fields.ModelSource.Compiled effective_list.append(ii) for j in child_list: if (j.device_name not in parent_interfaces and not j.get_name().startswith("!")): jj = deepcopy(j) jj.source = hd_fields.ModelSource.Compiled effective_list.append(jj) return effective_list
class HardwareProfile(base.DrydockPersistentObject, base.DrydockObject): VERSION = '1.0' fields = { 'name': ovo_fields.StringField(), 'source': hd_fields.ModelSourceField(), 'site': ovo_fields.StringField(), 'vendor': ovo_fields.StringField(nullable=True), 'generation': ovo_fields.StringField(nullable=True), 'hw_version': ovo_fields.StringField(nullable=True), 'bios_version': ovo_fields.StringField(nullable=True), 'boot_mode': ovo_fields.StringField(nullable=True), 'bootstrap_protocol': ovo_fields.StringField(nullable=True), 'pxe_interface': ovo_fields.StringField(nullable=True), 'devices': ovo_fields.ObjectField('HardwareDeviceAliasList', nullable=True), 'cpu_sets': ovo_fields.DictOfStringsField(nullable=True), 'hugepages_confs': ovo_fields.ObjectField('HugepagesConfList', nullable=True), } def __init__(self, **kwargs): super(HardwareProfile, self).__init__(**kwargs) return # HardwareProfile keyed on name def get_id(self): return self.get_name() def get_name(self): return self.name def get_hugepage_conf(self, conf_name): """Return the hugepages conf matching ``conf_name``""" if not self.hugepages_confs: raise errors.HugepageConfNotFound( "Hugepage configuration %s not found." % conf_name) for c in self.hugepages_confs: if c.name == conf_name: return c raise errors.HugepageConfNotFound( "Hugepage configuration %s not found." % conf_name) def get_cpu_set(self, set_name): """Return the cpu set matching ``set_name``""" if not self.cpu_sets: raise errors.CpuSetNotFound("CPU set %s not found." % set_name) if set_name in self.cpu_sets: return self.cpu_sets[set_name] raise errors.CpuSetNotFound("CPU set %s not found." % set_name) def resolve_alias(self, alias_type, alias): for d in self.devices: if d.alias == alias and d.bus_type == alias_type: selector = objects.HardwareDeviceSelector() selector.selector_type = "address" selector.address = d.address selector.device_type = d.dev_type return selector return None
class SiteDesign(base.DrydockPersistentObject, base.DrydockObject): VERSION = '1.0' fields = { 'id': ovo_fields.UUIDField(), # if null, indicates this is the site base design 'base_design_id': ovo_fields.UUIDField(nullable=True), 'source': hd_fields.ModelSourceField(), 'site': ovo_fields.ObjectField('Site', nullable=True), 'networks': ovo_fields.ObjectField('NetworkList', nullable=True), 'network_links': ovo_fields.ObjectField('NetworkLinkList', nullable=True), 'host_profiles': ovo_fields.ObjectField('HostProfileList', nullable=True), 'hardware_profiles': ovo_fields.ObjectField('HardwareProfileList', nullable=True), 'baremetal_nodes': ovo_fields.ObjectField('BaremetalNodeList', nullable=True), 'prom_configs': ovo_fields.ObjectField('PromenadeConfigList', nullable=True), 'racks': ovo_fields.ObjectField('RackList', nullable=True), } def __init__(self, **kwargs): super(SiteDesign, self).__init__(**kwargs) # Assign UUID id def assign_id(self): self.id = uuid.uuid4() return self.id # SiteDesign Keyed by id def get_id(self): return self.id def get_site(self): return self.site def set_site(self, site): self.site = site def add_network(self, new_network): if new_network is None: raise errors.DesignError("Invalid Network model") if self.networks is None: self.networks = objects.NetworkList() self.networks.append(new_network) def get_network(self, network_key): for n in self.networks: if n.get_id() == network_key: return n raise errors.DesignError( "Network %s not found in design state" % network_key) def add_network_link(self, new_network_link): if new_network_link is None: raise errors.DesignError("Invalid NetworkLink model") if self.network_links is None: self.network_links = objects.NetworkLinkList() self.network_links.append(new_network_link) def get_network_link(self, link_key): for l in self.network_links: if l.get_id() == link_key: return l raise errors.DesignError( "NetworkLink %s not found in design state" % link_key) def add_rack(self, new_rack): if new_rack is None: raise errors.DesignError("Invalid Rack model") if self.racks is None: self.racks = objects.RackList() self.racks.append(new_rack) def get_rack(self, rack_key): for r in self.racks: if r.get_id() == rack_key: return r raise errors.DesignError( "Rack %s not found in design state" % rack_key) def add_host_profile(self, new_host_profile): if new_host_profile is None: raise errors.DesignError("Invalid HostProfile model") if self.host_profiles is None: self.host_profiles = objects.HostProfileList() self.host_profiles.append(new_host_profile) def get_host_profile(self, profile_key): for p in self.host_profiles: if p.get_id() == profile_key: return p raise errors.DesignError( "HostProfile %s not found in design state" % profile_key) def add_hardware_profile(self, new_hardware_profile): if new_hardware_profile is None: raise errors.DesignError("Invalid HardwareProfile model") if self.hardware_profiles is None: self.hardware_profiles = objects.HardwareProfileList() self.hardware_profiles.append(new_hardware_profile) def get_hardware_profile(self, profile_key): for p in self.hardware_profiles: if p.get_id() == profile_key: return p raise errors.DesignError( "HardwareProfile %s not found in design state" % profile_key) def add_baremetal_node(self, new_baremetal_node): if new_baremetal_node is None: raise errors.DesignError("Invalid BaremetalNode model") if self.baremetal_nodes is None: self.baremetal_nodes = objects.BaremetalNodeList() self.baremetal_nodes.append(new_baremetal_node) def get_baremetal_node(self, node_key): for n in self.baremetal_nodes: if n.get_id() == node_key: return n raise errors.DesignError( "BaremetalNode %s not found in design state" % node_key) def add_promenade_config(self, prom_conf): if self.prom_configs is None: self.prom_configs = objects.PromenadeConfigList() self.prom_configs.append(prom_conf) def get_promenade_configs(self): return self.prom_configs def get_promenade_config(self, target_list): targeted_docs = [] if target_list is None or not isinstance(target_list, list): return targeted_docs for t in target_list: targeted_docs.extend(self.prom_configs.select_for_target(t)) return targeted_docs def create(self, ctx, state_manager): self.created_at = datetime.datetime.now() self.created_by = ctx.user state_manager.post_design(self) def save(self, ctx, state_manager): self.updated_at = datetime.datetime.now() self.updated_by = ctx.user state_manager.put_design(self) """ Support filtering on rack name, node name or node tag for now. Each filter can be a comma-delimited list of values. The final result is an intersection of all the filters """ def get_filtered_nodes(self, node_filter): effective_nodes = self.baremetal_nodes # filter by rack rack_filter = node_filter.get('rackname', None) if rack_filter is not None: rack_list = rack_filter.split(',') effective_nodes = [ x for x in effective_nodes if x.get_rack() in rack_list ] # filter by name name_filter = node_filter.get('nodename', None) if name_filter is not None: name_list = name_filter.split(',') effective_nodes = [ x for x in effective_nodes if x.get_name() in name_list ] # filter by tag tag_filter = node_filter.get('tags', None) if tag_filter is not None: tag_list = tag_filter.split(',') effective_nodes = [ x for x in effective_nodes for t in tag_list if x.has_tag(t) ] return effective_nodes
class HostPartition(base.DrydockObject): VERSION = '1.0' fields = { 'name': obj_fields.StringField(), 'source': hd_fields.ModelSourceField(), 'device': obj_fields.StringField(nullable=True), 'part_uuid': obj_fields.UUIDField(nullable=True), 'size': obj_fields.StringField(nullable=True), 'mountpoint': obj_fields.StringField(nullable=True), 'fstype': obj_fields.StringField(nullable=True, default='ext4'), 'mount_options': obj_fields.StringField(nullable=True, default='defaults'), 'fs_uuid': obj_fields.UUIDField(nullable=True), 'fs_label': obj_fields.StringField(nullable=True), 'selector': obj_fields.ObjectField('HardwareDeviceSelector', nullable=True), } def __init__(self, **kwargs): super(HostPartition, self).__init__(**kwargs) def get_device(self): return self.device # HostPartition keyed by name def get_id(self): return self.get_name() def get_name(self): return self.name # The device attribute may be hardware alias that translates to a # physical device address. If the device attribute does not match an # alias, we assume it directly identifies a OS device name. When the # apply_hardware_profile method is called on the parent Node of this # device, the selector will be decided and applied def set_selector(self, selector): self.selector = selector def get_selector(self): return self.selector """ Merge two lists of HostPartition models with child_list taking priority when conflicts. If a member of child_list has a name beginning with '!' it indicates that HostPartition should be removed from the merged list """ @staticmethod def merge_lists(child_list, parent_list): effective_list = [] if len(child_list) == 0 and len(parent_list) > 0: for p in parent_list: pp = deepcopy(p) pp.source = hd_fields.ModelSource.Compiled effective_list.append(pp) elif len(parent_list) == 0 and len(child_list) > 0: for i in child_list: if i.get_name().startswith('!'): continue else: ii = deepcopy(i) ii.source = hd_fields.ModelSource.Compiled effective_list.append(ii) elif len(parent_list) > 0 and len(child_list) > 0: inherit_field_list = ["device", "part_uuid", "size", "mountpoint", "fstype", "mount_options", "fs_uuid", "fs_label"] parent_partitions = [] for i in parent_list: parent_name = i.get_name() parent_partitions.append(parent_name) add = True for j in child_list: if j.get_name() == ("!" + parent_name): add = False break elif j.get_name() == parent_name: p = objects.HostPartition() p.name = j.get_name() for f in inherit_field_list: setattr(p, f, objects.Utils.apply_field_inheritance(getattr(j, f, None), getattr(i, f, None))) add = False p.source = hd_fields.ModelSource.Compiled effective_list.append(p) if add: ii = deepcopy(i) ii.source = hd_fields.ModelSource.Compiled effective_list.append(ii) for j in child_list: if (j.get_name() not in parent_list and not j.get_name().startswith("!")): jj = deepcopy(j) jj.source = hd_fields.ModelSource.Compiled effective_list.append(jj) return effective_list