
This PS implements the following changes: - switches freeze approach to requirements-direct.txt and requirements-frozen.txt files - adjusts code tabulation style according to yapf recommendations - replaces deprecated usage of responce.body attribute with responce.text - fixes integration tests in controlled by Makefile + tox - uplifts Helm to v3.9.4 Change-Id: I751db72eb8f670825382f11a36657112faeb169a
393 lines
12 KiB
Python
393 lines
12 KiB
Python
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Object models for a Region and the combined site design."""
|
|
|
|
import uuid
|
|
import datetime
|
|
|
|
import oslo_versionedobjects.fields as ovo_fields
|
|
|
|
import drydock_provisioner.error as errors
|
|
import drydock_provisioner.objects as objects
|
|
import drydock_provisioner.objects.base as base
|
|
import drydock_provisioner.objects.fields as hd_fields
|
|
|
|
|
|
@base.DrydockObjectRegistry.register
|
|
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)
|
|
|
|
|
|
@base.DrydockObjectRegistry.register
|
|
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
|
|
|
|
|
|
@base.DrydockObjectRegistry.register
|
|
class NodeTagDefinitionList(base.DrydockObjectListBase, base.DrydockObject):
|
|
|
|
VERSION = '1.0'
|
|
|
|
fields = {
|
|
'objects': ovo_fields.ListOfObjectsField('NodeTagDefinition'),
|
|
}
|
|
|
|
|
|
@base.DrydockObjectRegistry.register
|
|
class Repository(base.DrydockObject):
|
|
|
|
VERSION = '1.0'
|
|
|
|
fields = {
|
|
'name': ovo_fields.StringField(),
|
|
'url': ovo_fields.StringField(),
|
|
'repo_type': ovo_fields.StringField(),
|
|
'gpgkey': ovo_fields.StringField(nullable=True),
|
|
'distributions': ovo_fields.ListOfStringsField(nullable=True),
|
|
'subrepos': ovo_fields.ListOfStringsField(nullable=True),
|
|
'components': ovo_fields.ListOfStringsField(nullable=True),
|
|
'arches': ovo_fields.ListOfStringsField(default=['amd64']),
|
|
'options': ovo_fields.DictOfStringsField(nullable=True)
|
|
}
|
|
|
|
STANDARD_COMPONENTS = {
|
|
'apt': {'main', 'restricted', 'universe', 'multiverse'},
|
|
}
|
|
|
|
STANDARD_SUBREPOS = {
|
|
'apt': {'security', 'updates', 'backports'},
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
super(Repository, self).__init__(**kwargs)
|
|
|
|
# Repository keyed by tag
|
|
def get_id(self):
|
|
return self.name
|
|
|
|
def get_disabled_components(self):
|
|
enabled = set(self.components or [])
|
|
std = self.STANDARD_COMPONENTS.get(self.repo_type, ())
|
|
return std - enabled
|
|
|
|
def get_disabled_subrepos(self):
|
|
enabled = set(self.subrepos or [])
|
|
std = self.STANDARD_SUBREPOS.get(self.repo_type, ())
|
|
return std - enabled
|
|
|
|
|
|
@base.DrydockObjectRegistry.register
|
|
class RepositoryList(base.DrydockObjectListBase, base.DrydockObject):
|
|
|
|
VERSION = '1.0'
|
|
|
|
fields = {
|
|
'objects': ovo_fields.ListOfObjectsField('Repository'),
|
|
'remove_unlisted': ovo_fields.BooleanField(default=False),
|
|
}
|
|
|
|
|
|
@base.DrydockObjectRegistry.register
|
|
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),
|
|
'racks':
|
|
ovo_fields.ObjectField('RackList', nullable=True),
|
|
'bootactions':
|
|
ovo_fields.ObjectField('BootActionList', 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):
|
|
if self.networks:
|
|
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):
|
|
if self.network_links:
|
|
for network_link in self.network_links:
|
|
if network_link.get_id() == link_key:
|
|
return network_link
|
|
|
|
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):
|
|
if self.racks:
|
|
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_bootaction(self, new_ba):
|
|
"""Add a bootaction definition to this site design.
|
|
|
|
:param new_ba: instance of BootAction to add to the design
|
|
"""
|
|
if self.bootactions is None:
|
|
self.bootactions = objects.BootActionList()
|
|
|
|
self.bootactions.append(new_ba)
|
|
|
|
def get_bootaction(self, ba_key):
|
|
"""Select a boot action from this site design with the matchkey key.
|
|
|
|
:param ba_key: Value should match the ``get_id()`` value of the BootAction returned
|
|
"""
|
|
if self.bootactions:
|
|
for ba in self.bootactions:
|
|
if ba.get_id() == ba_key:
|
|
return ba
|
|
raise errors.DesignError("BootAction %s not found in design state" %
|
|
ba_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):
|
|
if self.host_profiles:
|
|
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):
|
|
if self.hardware_profiles:
|
|
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):
|
|
if self.baremetal_nodes:
|
|
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
|