config/controllerconfig/controllerconfig/controllerconfig/sysinv_api.py

580 lines
19 KiB
Python

#
# Copyright (c) 2014-2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
"""
System Inventory Interactions
"""
import json
import openstack
from six.moves.urllib import request as urlrequest
from six.moves.urllib.error import URLError
from six.moves.urllib.error import HTTPError
from controllerconfig.common import log
from controllerconfig.common.exceptions import KeystoneFail
LOG = log.get_logger(__name__)
API_VERSION = 1
# Host Personality Constants
HOST_PERSONALITY_NOT_SET = ""
HOST_PERSONALITY_UNKNOWN = "unknown"
HOST_PERSONALITY_CONTROLLER = "controller"
HOST_PERSONALITY_WORKER = "worker"
HOST_PERSONALITY_STORAGE = "storage"
# Host Administrative State Constants
HOST_ADMIN_STATE_NOT_SET = ""
HOST_ADMIN_STATE_UNKNOWN = "unknown"
HOST_ADMIN_STATE_LOCKED = "locked"
HOST_ADMIN_STATE_UNLOCKED = "unlocked"
# Host Operational State Constants
HOST_OPERATIONAL_STATE_NOT_SET = ""
HOST_OPERATIONAL_STATE_UNKNOWN = "unknown"
HOST_OPERATIONAL_STATE_ENABLED = "enabled"
HOST_OPERATIONAL_STATE_DISABLED = "disabled"
# Host Availability State Constants
HOST_AVAIL_STATE_NOT_SET = ""
HOST_AVAIL_STATE_UNKNOWN = "unknown"
HOST_AVAIL_STATE_AVAILABLE = "available"
HOST_AVAIL_STATE_ONLINE = "online"
HOST_AVAIL_STATE_OFFLINE = "offline"
HOST_AVAIL_STATE_POWERED_OFF = "powered-off"
HOST_AVAIL_STATE_POWERED_ON = "powered-on"
# Host Board Management Constants
HOST_BM_TYPE_NOT_SET = ""
HOST_BM_TYPE_UNKNOWN = "unknown"
HOST_BM_TYPE_ILO3 = 'ilo3'
HOST_BM_TYPE_ILO4 = 'ilo4'
# Host invprovision state
HOST_PROVISIONING = "provisioning"
HOST_PROVISIONED = "provisioned"
class Host(object):
def __init__(self, hostname, host_data=None):
self.name = hostname
self.personality = HOST_PERSONALITY_NOT_SET
self.admin_state = HOST_ADMIN_STATE_NOT_SET
self.operational_state = HOST_OPERATIONAL_STATE_NOT_SET
self.avail_status = []
self.bm_type = HOST_BM_TYPE_NOT_SET
self.uuid = None
self.config_status = None
self.invprovision = None
self.boot_device = None
self.rootfs_device = None
self.console = None
self.tboot = None
if host_data is not None:
self.__host_set_state__(host_data)
def __host_set_state__(self, host_data):
if host_data is None:
self.admin_state = HOST_ADMIN_STATE_UNKNOWN
self.operational_state = HOST_OPERATIONAL_STATE_UNKNOWN
self.avail_status = []
self.bm_type = HOST_BM_TYPE_NOT_SET
# Set personality
if host_data['personality'] == "controller":
self.personality = HOST_PERSONALITY_CONTROLLER
elif host_data['personality'] == "worker":
self.personality = HOST_PERSONALITY_WORKER
elif host_data['personality'] == "storage":
self.personality = HOST_PERSONALITY_STORAGE
else:
self.personality = HOST_PERSONALITY_UNKNOWN
# Set administrative state
if host_data['administrative'] == "locked":
self.admin_state = HOST_ADMIN_STATE_LOCKED
elif host_data['administrative'] == "unlocked":
self.admin_state = HOST_ADMIN_STATE_UNLOCKED
else:
self.admin_state = HOST_ADMIN_STATE_UNKNOWN
# Set operational state
if host_data['operational'] == "enabled":
self.operational_state = HOST_OPERATIONAL_STATE_ENABLED
elif host_data['operational'] == "disabled":
self.operational_state = HOST_OPERATIONAL_STATE_DISABLED
else:
self.operational_state = HOST_OPERATIONAL_STATE_UNKNOWN
# Set availability status
self.avail_status[:] = []
if host_data['availability'] == "available":
self.avail_status.append(HOST_AVAIL_STATE_AVAILABLE)
elif host_data['availability'] == "online":
self.avail_status.append(HOST_AVAIL_STATE_ONLINE)
elif host_data['availability'] == "offline":
self.avail_status.append(HOST_AVAIL_STATE_OFFLINE)
elif host_data['availability'] == "power-on":
self.avail_status.append(HOST_AVAIL_STATE_POWERED_ON)
elif host_data['availability'] == "power-off":
self.avail_status.append(HOST_AVAIL_STATE_POWERED_OFF)
else:
self.avail_status.append(HOST_AVAIL_STATE_AVAILABLE)
# Set board management type
if host_data['bm_type'] is None:
self.bm_type = HOST_BM_TYPE_NOT_SET
elif host_data['bm_type'] == 'ilo3':
self.bm_type = HOST_BM_TYPE_ILO3
elif host_data['bm_type'] == 'ilo4':
self.bm_type = HOST_BM_TYPE_ILO4
else:
self.bm_type = HOST_BM_TYPE_UNKNOWN
if host_data['invprovision'] == 'provisioned':
self.invprovision = HOST_PROVISIONED
else:
self.invprovision = HOST_PROVISIONING
self.uuid = host_data['uuid']
self.config_status = host_data['config_status']
self.boot_device = host_data['boot_device']
self.rootfs_device = host_data['rootfs_device']
self.console = host_data['console']
self.tboot = host_data['tboot']
def __host_update__(self, admin_token, region_name):
try:
url = admin_token.get_service_admin_url("platform", "sysinv",
region_name)
url += "/ihosts/" + self.name
request_info = urlrequest.Request(url)
request_info.add_header("X-Auth-Token", admin_token.get_id())
request_info.add_header("Accept", "application/json")
request = urlrequest.urlopen(request_info)
response = json.loads(request.read())
request.close()
return response
except KeystoneFail as e:
LOG.error("Keystone authentication failed:{} ".format(e))
return None
except HTTPError as e:
LOG.error("%s, %s" % (e.code, e.read()))
if e.code == 401:
admin_token.set_expired()
return None
except URLError as e:
LOG.error(e)
return None
def __host_action__(self, admin_token, action, region_name):
try:
url = admin_token.get_service_admin_url("platform", "sysinv",
region_name)
url += "/ihosts/" + self.name
request_info = urlrequest.Request(url)
request_info.get_method = lambda: 'PATCH'
request_info.add_header("X-Auth-Token", admin_token.get_id())
request_info.add_header("Content-type", "application/json")
request_info.add_header("Accept", "application/json")
request_info.add_data(action)
request = urlrequest.urlopen(request_info)
request.close()
return True
except KeystoneFail as e:
LOG.error("Keystone authentication failed:{} ".format(e))
return False
except HTTPError as e:
LOG.error("%s, %s" % (e.code, e.read()))
if e.code == 401:
admin_token.set_expired()
return False
except URLError as e:
LOG.error(e)
return False
def is_unlocked(self):
return(self.admin_state == HOST_ADMIN_STATE_UNLOCKED)
def is_locked(self):
return(not self.is_unlocked())
def is_enabled(self):
return(self.admin_state == HOST_ADMIN_STATE_UNLOCKED and
self.operational_state == HOST_OPERATIONAL_STATE_ENABLED)
def is_controller_enabled_provisioned(self):
return(self.admin_state == HOST_ADMIN_STATE_UNLOCKED and
self.operational_state == HOST_OPERATIONAL_STATE_ENABLED and
self.personality == HOST_PERSONALITY_CONTROLLER and
self.invprovision == HOST_PROVISIONED)
def is_disabled(self):
return(not self.is_enabled())
def support_power_off(self):
return(HOST_BM_TYPE_NOT_SET != self.bm_type)
def is_powered_off(self):
for status in self.avail_status:
if status == HOST_AVAIL_STATE_POWERED_OFF:
return(self.admin_state == HOST_ADMIN_STATE_LOCKED and
self.operational_state ==
HOST_OPERATIONAL_STATE_DISABLED)
return False
def is_powered_on(self):
return not self.is_powered_off()
def refresh_data(self, admin_token, region_name):
""" Ask the System Inventory for an update view of the host """
host_data = self.__host_update__(admin_token, region_name)
self.__host_set_state__(host_data)
def lock(self, admin_token, region_name):
""" Asks the Platform to perform a lock against a host """
if self.is_unlocked():
action = json.dumps([{"path": "/action",
"value": "lock", "op": "replace"}])
return self.__host_action__(admin_token, action, region_name)
return True
def force_lock(self, admin_token, region_name):
""" Asks the Platform to perform a force lock against a host """
if self.is_unlocked():
action = json.dumps([{"path": "/action",
"value": "force-lock", "op": "replace"}])
return self.__host_action__(admin_token, action, region_name)
return True
def unlock(self, admin_token, region_name):
""" Asks the Platform to perform an ulock against a host """
if self.is_locked():
action = json.dumps([{"path": "/action",
"value": "unlock", "op": "replace"}])
return self.__host_action__(admin_token, action, region_name)
return True
def power_off(self, admin_token, region_name):
""" Asks the Platform to perform a power-off against a host """
if self.is_powered_on():
action = json.dumps([{"path": "/action",
"value": "power-off", "op": "replace"}])
return self.__host_action__(admin_token, action, region_name)
return True
def power_on(self, admin_token, region_name):
""" Asks the Platform to perform a power-on against a host """
if self.is_powered_off():
action = json.dumps([{"path": "/action",
"value": "power-on", "op": "replace"}])
return self.__host_action__(admin_token, action, region_name)
return True
def get_hosts(admin_token, region_name, personality=None,
exclude_hostnames=None):
""" Asks System Inventory for a list of hosts """
if exclude_hostnames is None:
exclude_hostnames = []
try:
url = admin_token.get_service_admin_url("platform", "sysinv",
region_name)
url += "/ihosts/"
request_info = urlrequest.Request(url)
request_info.add_header("X-Auth-Token", admin_token.get_id())
request_info.add_header("Accept", "application/json")
request = urlrequest.urlopen(request_info)
response = json.loads(request.read())
request.close()
host_list = []
if personality is None:
for host in response['ihosts']:
if host['hostname'] not in exclude_hostnames:
host_list.append(Host(host['hostname'], host))
else:
for host in response['ihosts']:
if host['hostname'] not in exclude_hostnames:
if (host['personality'] == "controller" and
personality == HOST_PERSONALITY_CONTROLLER):
host_list.append(Host(host['hostname'], host))
elif (host['personality'] == "worker" and
personality == HOST_PERSONALITY_WORKER):
host_list.append(Host(host['hostname'], host))
elif (host['personality'] == "storage" and
personality == HOST_PERSONALITY_STORAGE):
host_list.append(Host(host['hostname'], host))
return host_list
except KeystoneFail as e:
LOG.error("Keystone authentication failed:{} ".format(e))
return []
except HTTPError as e:
LOG.error("%s, %s" % (e.code, e.read()))
if e.code == 401:
admin_token.set_expired()
return []
except URLError as e:
LOG.error(e)
return []
def dict_to_patch(values, install_action=False):
# install default action
if install_action:
values.update({'action': 'install'})
patch = []
for key, value in values.items():
path = '/' + key
patch.append({'op': 'replace', 'path': path, 'value': value})
return patch
def get_shared_services():
try:
services = ""
with openstack.OpenStack() as client:
systems = client.sysinv.isystem.list()
if systems:
services = systems[0].capabilities.get("shared_services", "")
except Exception as e:
LOG.exception("failed to get shared services")
raise e
return services
def get_alarms():
""" get all alarms """
alarm_list = []
try:
with openstack.OpenStack() as client:
alarm_list = client.sysinv.ialarm.list()
except Exception as e:
LOG.exception("failed to get alarms")
raise e
return alarm_list
def controller_enabled_provisioned(hostname):
""" check if host is enabled """
try:
with openstack.OpenStack() as client:
hosts = get_hosts(client.admin_token,
client.conf['region_name'])
for host in hosts:
if (hostname == host.name and
host.is_controller_enabled_provisioned()):
LOG.info("host %s is enabled/provisioned" % host.name)
return True
except Exception as e:
LOG.exception("failed to check if host is enabled/provisioned")
raise e
return False
def get_system_uuid():
""" get system uuid """
try:
sysuuid = ""
with openstack.OpenStack() as client:
systems = client.sysinv.isystem.list()
if systems:
sysuuid = systems[0].uuid
except Exception as e:
LOG.exception("failed to get system uuid")
raise e
return sysuuid
def get_oam_ip():
""" get OAM ip details """
try:
with openstack.OpenStack() as client:
oam_list = client.sysinv.iextoam.list()
if oam_list:
return oam_list[0]
except Exception as e:
LOG.exception("failed to get OAM IP")
raise e
return None
def get_mac_addresses(hostname):
""" get MAC addresses for the host """
macs = {}
try:
with openstack.OpenStack() as client:
hosts = get_hosts(client.admin_token,
client.conf['region_name'])
for host in hosts:
if hostname == host.name:
port_list = client.sysinv.ethernet_port.list(host.uuid)
macs = {port.name: port.mac for port in port_list}
except Exception as e:
LOG.exception("failed to get MAC addresses")
raise e
return macs
def get_disk_serial_ids(hostname):
""" get disk serial ids for the host """
disk_serial_ids = {}
try:
with openstack.OpenStack() as client:
hosts = get_hosts(client.admin_token,
client.conf['region_name'])
for host in hosts:
if hostname == host.name:
disk_list = client.sysinv.idisk.list(host.uuid)
disk_serial_ids = {
disk.device_node: disk.serial_id for disk in disk_list}
except Exception as e:
LOG.exception("failed to get disks")
raise e
return disk_serial_ids
def update_clone_system(descr, hostname):
""" update system parameters on clone installation """
try:
with openstack.OpenStack() as client:
systems = client.sysinv.isystem.list()
if not systems:
return False
values = {
'name': "Cloned_system",
'description': descr
}
patch = dict_to_patch(values)
LOG.info("Updating system: {} [{}]".format(systems[0].name, patch))
client.sysinv.isystem.update(systems[0].uuid, patch)
hosts = get_hosts(client.admin_token,
client.conf['region_name'])
for host in hosts:
if hostname == host.name:
values = {
'location': {},
'serialid': ""
}
patch = dict_to_patch(values)
client.sysinv.ihost.update(host.uuid, patch)
LOG.info("Updating host: {} [{}]".format(host, patch))
except Exception as e:
LOG.exception("failed to update system parameters")
raise e
return True
def get_config_status(hostname):
""" get config status of the host """
try:
with openstack.OpenStack() as client:
hosts = get_hosts(client.admin_token,
client.conf['region_name'])
for host in hosts:
if hostname == host.name:
return host.config_status
except Exception as e:
LOG.exception("failed to get config status")
raise e
return None
def get_host_data(hostname):
""" get data for the specified host """
try:
with openstack.OpenStack() as client:
hosts = get_hosts(client.admin_token,
client.conf['region_name'])
for host in hosts:
if hostname == host.name:
return host
except Exception as e:
LOG.exception("failed to get host data")
raise e
return None
def do_worker_config_complete(hostname):
""" enable worker functionality """
try:
with openstack.OpenStack() as client:
hosts = get_hosts(client.admin_token,
client.conf['region_name'])
for host in hosts:
if hostname == host.name:
# Create/apply worker manifests
values = {
'action': "subfunction_config"
}
patch = dict_to_patch(values)
LOG.info("Applying worker manifests: {} [{}]"
.format(host, patch))
client.sysinv.ihost.update(host.uuid, patch)
except Exception as e:
LOG.exception("worker_config_complete failed")
raise e
def get_storage_backend_services():
""" get all storage backends and their assigned services """
backend_service_dict = {}
try:
with openstack.OpenStack() as client:
backend_list = client.sysinv.storage_backend.list()
for backend in backend_list:
backend_service_dict.update(
{backend.backend: backend.services})
except Exception as e:
LOG.exception("failed to get storage backend services")
raise e
return backend_service_dict