295 lines
9.5 KiB
Python
295 lines
9.5 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.
|
|
from copy import deepcopy
|
|
from datetime import datetime
|
|
from datetime import timezone
|
|
|
|
import uuid
|
|
|
|
import helm_drydock.model.node as node
|
|
import helm_drydock.model.hostprofile as hostprofile
|
|
import helm_drydock.model.network as network
|
|
import helm_drydock.model.site as site
|
|
import helm_drydock.model.hwprofile as hwprofile
|
|
|
|
from helm_drydock.error import DesignError
|
|
|
|
class DesignState(object):
|
|
|
|
def __init__(self):
|
|
self.design_base = None
|
|
|
|
self.design_changes = []
|
|
|
|
self.builds = []
|
|
return
|
|
|
|
# TODO Need to lock a design base or change once implementation
|
|
# has started
|
|
def get_design_base(self):
|
|
if self.design_base is None:
|
|
raise DesignError("No design base submitted")
|
|
|
|
return deepcopy(self.design_base)
|
|
|
|
def post_design_base(self, site_design):
|
|
if site_design is not None and isinstance(site_design, SiteDesign):
|
|
self.design_base = deepcopy(site_design)
|
|
return True
|
|
|
|
def put_design_base(self, site_design):
|
|
# TODO Support merging
|
|
if site_design is not None and isinstance(site_design, SiteDesign):
|
|
self.design_base = deepcopy(site_design)
|
|
return True
|
|
|
|
def get_design_change(self, changeid):
|
|
match = [x for x in self.design_changes if x.changeid == changeid]
|
|
|
|
if len(match) == 0:
|
|
raise DesignError("No design change %s found." % (changeid))
|
|
else:
|
|
return deepcopy(match[0])
|
|
|
|
def post_design_change(self, site_design):
|
|
if site_design is not None and isinstance(site_design, SiteDesign):
|
|
exists = [(x) for x
|
|
in self.design_changes
|
|
if x.changeid == site_design.changeid]
|
|
if len(exists) > 0:
|
|
raise DesignError("Existing change %s found" %
|
|
(site_design.changeid))
|
|
self.design_changes.append(deepcopy(site_design))
|
|
return True
|
|
else:
|
|
raise DesignError("Design change must be a SiteDesign instance")
|
|
|
|
def put_design_change(self, site_design):
|
|
# TODO Support merging
|
|
if site_design is not None and isinstance(site_design, SiteDesign):
|
|
design_copy = deepcopy(site_design)
|
|
self.design_changes = [design_copy
|
|
if x.changeid == design_copy.changeid
|
|
else x
|
|
for x
|
|
in self.design_changes]
|
|
return True
|
|
else:
|
|
raise DesignError("Design change must be a SiteDesign instance")
|
|
|
|
def get_current_build(self):
|
|
latest_stamp = 0
|
|
current_build = None
|
|
|
|
for b in self.builds:
|
|
if b.build_id > latest_stamp:
|
|
latest_stamp = b.build_id
|
|
current_build = b
|
|
|
|
return deepcopy(current_build)
|
|
|
|
def get_build(self, build_id):
|
|
for b in self.builds:
|
|
if b.build_id == build_id:
|
|
return b
|
|
|
|
return None
|
|
|
|
def post_build(self, site_build):
|
|
if site_build is not None and isinstance(site_build, SiteBuild):
|
|
exists = [b for b in self.builds
|
|
if b.build_id == site_build.build_id]
|
|
|
|
if len(exists) > 0:
|
|
raise DesignError("Already a site build with ID %s" %
|
|
(str(site_build.build_id)))
|
|
else:
|
|
self.builds.append(deepcopy(site_build))
|
|
return True
|
|
|
|
|
|
class SiteDesign(object):
|
|
|
|
def __init__(self, ischange=False):
|
|
if ischange:
|
|
self.changeid = uuid.uuid4()
|
|
else:
|
|
self.changeid = 0
|
|
|
|
self.sites = []
|
|
self.networks = []
|
|
self.network_links = []
|
|
self.host_profiles = []
|
|
self.hardware_profiles = []
|
|
self.baremetal_nodes = []
|
|
|
|
def add_site(self, new_site):
|
|
if new_site is None or not isinstance(new_site, site.Site):
|
|
raise DesignError("Invalid Site model")
|
|
|
|
self.sites.append(new_site)
|
|
|
|
def get_sites(self):
|
|
return self.sites
|
|
|
|
def get_site(self, site_name):
|
|
for s in self.sites:
|
|
if s.name == site_name:
|
|
return s
|
|
|
|
raise DesignError("Site %s not found in design state" % site_name)
|
|
|
|
def add_network(self, new_network):
|
|
if new_network is None or not isinstance(new_network, network.Network):
|
|
raise DesignError("Invalid Network model")
|
|
|
|
self.networks.append(new_network)
|
|
|
|
def get_networks(self):
|
|
return self.networks
|
|
|
|
def get_network(self, network_name):
|
|
for n in self.networks:
|
|
if n.name == network_name:
|
|
return n
|
|
|
|
raise DesignError("Network %s not found in design state"
|
|
% network_name)
|
|
|
|
def add_network_link(self, new_network_link):
|
|
if new_network_link is None or not isinstance(new_network_link,
|
|
network.NetworkLink):
|
|
raise DesignError("Invalid NetworkLink model")
|
|
|
|
self.network_links.append(new_network_link)
|
|
|
|
def get_network_links(self):
|
|
return self.network_links
|
|
|
|
def get_network_link(self, link_name):
|
|
for l in self.network_links:
|
|
if l.name == link_name:
|
|
return l
|
|
|
|
raise DesignError("NetworkLink %s not found in design state"
|
|
% link_name)
|
|
|
|
def add_host_profile(self, new_host_profile):
|
|
if new_host_profile is None or not isinstance(new_host_profile,
|
|
hostprofile.HostProfile):
|
|
raise DesignError("Invalid HostProfile model")
|
|
|
|
self.host_profiles.append(new_host_profile)
|
|
|
|
def get_host_profiles(self):
|
|
return self.host_profiles
|
|
|
|
def get_host_profile(self, profile_name):
|
|
for p in self.host_profiles:
|
|
if p.name == profile_name:
|
|
return p
|
|
|
|
raise DesignError("HostProfile %s not found in design state"
|
|
% profile_name)
|
|
|
|
def add_hardware_profile(self, new_hardware_profile):
|
|
if (new_hardware_profile is None or
|
|
not isinstance(new_hardware_profile, hwprofile.HardwareProfile)):
|
|
raise DesignError("Invalid HardwareProfile model")
|
|
|
|
self.hardware_profiles.append(new_hardware_profile)
|
|
|
|
def get_hardware_profiles(self):
|
|
return self.hardware_profiles
|
|
|
|
def get_hardware_profile(self, profile_name):
|
|
for p in self.hardware_profiles:
|
|
if p.name == profile_name:
|
|
return p
|
|
|
|
raise DesignError("HardwareProfile %s not found in design state"
|
|
% profile_name)
|
|
|
|
def add_baremetal_node(self, new_baremetal_node):
|
|
if (new_baremetal_node is None or
|
|
not isinstance(new_baremetal_node, node.BaremetalNode)):
|
|
raise DesignError("Invalid BaremetalNode model")
|
|
|
|
self.baremetal_nodes.append(new_baremetal_node)
|
|
|
|
def get_baremetal_nodes(self):
|
|
return self.baremetal_nodes
|
|
|
|
def get_baremetal_node(self, node_name):
|
|
for n in self.baremetal_nodes:
|
|
if n.name == node_name:
|
|
return n
|
|
|
|
raise DesignError("BaremetalNode %s not found in design state"
|
|
% node_name)
|
|
|
|
|
|
class SiteBuild(SiteDesign):
|
|
|
|
def __init__(self, build_id=None):
|
|
super(SiteBuild, self).__init__()
|
|
|
|
if build_id is None:
|
|
self.build_id = datetime.datetime.now(timezone.utc).timestamp()
|
|
else:
|
|
self.build_id = build_id
|
|
|
|
def get_filtered_nodes(self, node_filter):
|
|
effective_nodes = self.get_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
|
|
"""
|
|
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 set_nodes_status(self, node_filter, status):
|
|
target_nodes = self.get_filtered_nodes(node_filter)
|
|
|
|
for n in target_nodes:
|
|
n.set_status(status)
|