2565 lines
116 KiB
Python
Executable File
2565 lines
116 KiB
Python
Executable File
# Copyright 2013 OpenStack Foundation
|
|
# All 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.
|
|
|
|
"""
|
|
/hosts endpoint for Daisy v1 API
|
|
"""
|
|
import subprocess
|
|
import re
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from webob.exc import HTTPBadRequest
|
|
from webob.exc import HTTPConflict
|
|
from webob.exc import HTTPForbidden
|
|
from webob.exc import HTTPNotFound
|
|
from webob import Response
|
|
from daisy.api import policy
|
|
import daisy.api.v1
|
|
from daisy.api.v1 import controller
|
|
from daisy.api.v1 import filters
|
|
from daisy.common import exception
|
|
from daisy.common import property_utils
|
|
from daisy.common import utils
|
|
from daisy.common import wsgi
|
|
from daisy.common import vcpu_pin
|
|
from daisy import i18n
|
|
from daisy import notifier
|
|
import daisy.registry.client.v1.api as registry
|
|
import threading
|
|
import daisy.api.backends.common as daisy_cmn
|
|
import daisy.api.backends.tecs.common as tecs_cmn
|
|
import ConfigParser
|
|
import socket
|
|
import netaddr
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
_ = i18n._
|
|
_LE = i18n._LE
|
|
_LI = i18n._LI
|
|
_LW = i18n._LW
|
|
SUPPORTED_PARAMS = daisy.api.v1.SUPPORTED_PARAMS
|
|
SUPPORTED_FILTERS = daisy.api.v1.SUPPORTED_FILTERS
|
|
ACTIVE_IMMUTABLE = daisy.api.v1.ACTIVE_IMMUTABLE
|
|
|
|
CONF = cfg.CONF
|
|
CONF.import_opt('disk_formats', 'daisy.common.config', group='image_format')
|
|
CONF.import_opt('container_formats', 'daisy.common.config',
|
|
group='image_format')
|
|
CONF.import_opt('image_property_quota', 'daisy.common.config')
|
|
|
|
DISCOVER_DEFAULTS = {
|
|
'listen_port': '5050',
|
|
'ironic_url': 'http://127.0.0.1:6385/v1',
|
|
}
|
|
|
|
ML2_TYPE = [
|
|
'ovs',
|
|
'dvs',
|
|
'ovs,sriov(macvtap)',
|
|
'ovs,sriov(direct)',
|
|
'sriov(macvtap)',
|
|
'sriov(direct)']
|
|
SUPPORT_HOST_PAGE_SIZE = ['2M', '1G']
|
|
|
|
|
|
class Controller(controller.BaseController):
|
|
"""
|
|
WSGI controller for hosts resource in Daisy v1 API
|
|
|
|
The hosts resource API is a RESTful web service for host data. The API
|
|
is as follows::
|
|
|
|
GET /nodes -- Returns a set of brief metadata about hosts
|
|
GET /nodes -- Returns a set of detailed metadata about
|
|
hosts
|
|
HEAD /nodes/<ID> -- Return metadata about an host with id <ID>
|
|
GET /nodes/<ID> -- Return host data for host with id <ID>
|
|
POST /nodes -- Store host data and return metadata about the
|
|
newly-stored host
|
|
PUT /nodes/<ID> -- Update host metadata and/or upload host
|
|
data for a previously-reserved host
|
|
DELETE /nodes/<ID> -- Delete the host with id <ID>
|
|
"""
|
|
support_resource_type = ['baremetal', 'server', 'docker']
|
|
|
|
def __init__(self):
|
|
self.notifier = notifier.Notifier()
|
|
registry.configure_registry_client()
|
|
self.policy = policy.Enforcer()
|
|
if property_utils.is_property_protection_enabled():
|
|
self.prop_enforcer = property_utils.PropertyRules(self.policy)
|
|
else:
|
|
self.prop_enforcer = None
|
|
|
|
def _enforce(self, req, action, target=None):
|
|
"""Authorize an action against our policies"""
|
|
if target is None:
|
|
target = {}
|
|
try:
|
|
self.policy.enforce(req.context, action, target)
|
|
except exception.Forbidden:
|
|
raise HTTPForbidden()
|
|
|
|
def _raise_404_if_network_deleted(self, req, network_id):
|
|
network = self.get_network_meta_or_404(req, network_id)
|
|
if network is None or network['deleted']:
|
|
msg = _("Network with identifier %s has been deleted.") % \
|
|
network_id
|
|
raise HTTPNotFound(msg)
|
|
|
|
def _raise_404_if_cluster_deleted(self, req, cluster_id):
|
|
cluster = self.get_cluster_meta_or_404(req, cluster_id)
|
|
if cluster is None or cluster['deleted']:
|
|
msg = _("Cluster with identifier %s has been deleted.") % \
|
|
cluster_id
|
|
raise HTTPNotFound(msg)
|
|
|
|
def _raise_404_if_role_deleted(self, req, role_id):
|
|
role = self.get_role_meta_or_404(req, role_id)
|
|
if role is None or role['deleted']:
|
|
msg = _("Cluster with identifier %s has been deleted.") % role_id
|
|
raise HTTPNotFound(msg)
|
|
|
|
def _get_filters(self, req):
|
|
"""
|
|
Return a dictionary of query param filters from the request
|
|
|
|
:param req: the Request object coming from the wsgi layer
|
|
:retval a dict of key/value filters
|
|
"""
|
|
query_filters = {}
|
|
for param in req.params:
|
|
if param in SUPPORTED_FILTERS:
|
|
query_filters[param] = req.params.get(param)
|
|
if not filters.validate(param, query_filters[param]):
|
|
raise HTTPBadRequest(_('Bad value passed to filter '
|
|
'%(filter)s got %(val)s')
|
|
% {'filter': param,
|
|
'val': query_filters[param]})
|
|
return query_filters
|
|
|
|
def _get_query_params(self, req):
|
|
"""
|
|
Extracts necessary query params from request.
|
|
|
|
:param req: the WSGI Request object
|
|
:retval dict of parameters that can be used by registry client
|
|
"""
|
|
params = {'filters': self._get_filters(req)}
|
|
|
|
for PARAM in SUPPORTED_PARAMS:
|
|
if PARAM in req.params:
|
|
params[PARAM] = req.params.get(PARAM)
|
|
return params
|
|
|
|
def check_bond_slaves_validity(
|
|
self,
|
|
bond_slaves_lists,
|
|
ether_nic_names_list):
|
|
'''
|
|
members in bond slaves must be in ether_nic_names_list
|
|
len(set(bond_slaves)) == 2, and can not be overlap
|
|
between slaves members
|
|
bond_slaves_lists: [[name1,name2], [name1,name2], ...]
|
|
ether_nic_names_list: [name1, name2, ...]
|
|
'''
|
|
for bond_slaves in bond_slaves_lists:
|
|
LOG.warn('bond_slaves: %s' % bond_slaves)
|
|
if len(set(bond_slaves)) != 2:
|
|
LOG.error('set(bond_slaves: %s' % set(bond_slaves))
|
|
msg = (
|
|
_(
|
|
"Bond slaves(%s) must be different nic and existed "
|
|
"in ether nics in pairs." %
|
|
bond_slaves))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
if not set(bond_slaves).issubset(set(ether_nic_names_list)):
|
|
msg = (
|
|
_("Pay attention: illegal ether nic existed "
|
|
"in bond slaves(%s)." % bond_slaves))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
|
|
def validate_ip_format(self, ip_str):
|
|
'''
|
|
valid ip_str format = '10.43.178.9'
|
|
invalid ip_str format : '123. 233.42.12', spaces existed in field
|
|
'3234.23.453.353', out of range
|
|
'-2.23.24.234', negative number in field
|
|
'1.2.3.4d', letter in field
|
|
'10.43.1789', invalid format
|
|
'''
|
|
valid_fromat = False
|
|
if ip_str.count('.') == 3 and all(num.isdigit() and 0 <= int(
|
|
num) < 256 for num in ip_str.rstrip().split('.')):
|
|
valid_fromat = True
|
|
if not valid_fromat:
|
|
msg = (_("%s invalid ip format!") % ip_str)
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
|
|
def validate_mac_format(self, mac_str):
|
|
'''Validates a mac address'''
|
|
if re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$",
|
|
mac_str.lower()):
|
|
return
|
|
else:
|
|
msg = (_("%s invalid mac format!") % mac_str)
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
|
|
def get_cluster_networks_info(self, req, cluster_id=None, type=None):
|
|
'''
|
|
get_cluster_networks_info by cluster id
|
|
'''
|
|
params = {}
|
|
if type:
|
|
params['filters'] = {'type': type}
|
|
|
|
all_networks = registry.get_all_networks(req.context, **params)
|
|
if cluster_id:
|
|
cluster_networks = [network for network in all_networks
|
|
if network['cluster_id'] == cluster_id]
|
|
return cluster_networks
|
|
else:
|
|
return all_networks
|
|
|
|
def _check_assigned_networks(self, req, cluster_id, assigned_networks):
|
|
LOG.info("assigned_networks %s " % assigned_networks)
|
|
cluster_networks = self.get_cluster_networks_info(req, cluster_id)
|
|
list_of_assigned_networks = []
|
|
for assigned_network in assigned_networks:
|
|
LOG.info("assigned_network %s " % assigned_network)
|
|
if 'name' not in assigned_network or not assigned_network['name']:
|
|
msg = "assigned networks '%s' are invalid" % (
|
|
assigned_networks)
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
network_info = [network for network in cluster_networks if network[
|
|
'name'] == assigned_network['name']]
|
|
if network_info and network_info[0]:
|
|
network_cidr = network_info[0]['cidr']
|
|
LOG.info("network_info %s " % network_info)
|
|
if network_info[0]['network_type'] != 'DATAPLANE':
|
|
if network_cidr:
|
|
if 'ip' in assigned_network and assigned_network['ip']:
|
|
self.validate_ip_format(assigned_network['ip'])
|
|
ip_in_cidr = utils.is_ip_in_cidr(
|
|
assigned_network['ip'], network_cidr)
|
|
if not ip_in_cidr:
|
|
msg = (_("The ip '%s' for network '%s'"
|
|
" is not in cidr range." %
|
|
(assigned_network['ip'],
|
|
assigned_network['name'])))
|
|
raise HTTPBadRequest(explanation=msg)
|
|
else:
|
|
msg = "error, cidr of network '%s' is empty" % (
|
|
assigned_network['name'])
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
msg = "can't find network named '%s' in cluster '%s'" % (
|
|
assigned_network['name'], cluster_id)
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
list_of_assigned_networks.append(network_info[0])
|
|
return list_of_assigned_networks
|
|
|
|
def _compare_assigned_networks_of_interface(self, interface1, interface2):
|
|
for network in interface1:
|
|
if network.get('segmentation_type') in ['vlan']:
|
|
continue
|
|
for network_compare in interface2:
|
|
if network_compare.get('segmentation_type') in ['vlan']:
|
|
continue
|
|
if network.get('cidr', None) \
|
|
and network_compare.get('cidr', None) \
|
|
and network['cidr'] == network_compare['cidr']:
|
|
return network['name'], network_compare['name']
|
|
return False, False
|
|
|
|
def _compare_assigned_networks_between_interfaces(
|
|
self, interface_num, assigned_networks_of_interfaces):
|
|
for interface_id in range(interface_num):
|
|
for interface_id_compare in range(interface_id + 1, interface_num):
|
|
network1_name, network2_name = self.\
|
|
_compare_assigned_networks_of_interface(
|
|
assigned_networks_of_interfaces[interface_id],
|
|
assigned_networks_of_interfaces[interface_id_compare])
|
|
if network1_name and network2_name:
|
|
msg = (_('Network %s and network %s with same '
|
|
'cidr can not be assigned to different '
|
|
'interfaces.')) % (network1_name, network2_name)
|
|
raise HTTPBadRequest(explanation=msg)
|
|
|
|
def _check_add_host_interfaces(self, req, host_meta):
|
|
host_meta_interfaces = []
|
|
if 'interfaces' in host_meta:
|
|
host_meta_interfaces = list(eval(host_meta['interfaces']))
|
|
else:
|
|
return
|
|
|
|
cluster_id = host_meta.get('cluster', None)
|
|
|
|
exist_id, os_status = self._verify_interface_among_hosts(
|
|
req, host_meta)
|
|
if exist_id:
|
|
if os_status == "active":
|
|
msg = _(
|
|
'The host %s os_status is active,'
|
|
'forbidden ironic to add host.') % exist_id
|
|
raise HTTPBadRequest(explanation=msg)
|
|
host_meta['id'] = exist_id
|
|
self.update_host(req, exist_id, host_meta)
|
|
LOG.info(
|
|
"<<<FUN:verify_interface, host:%s is already update.>>>" %
|
|
exist_id)
|
|
return {'host_meta': host_meta}
|
|
|
|
ether_nic_names_list = list()
|
|
bond_nic_names_list = list()
|
|
bond_slaves_lists = list()
|
|
have_assigned_network = False
|
|
have_ip_netmask = False
|
|
assigned_networks_of_intefaces = []
|
|
interface_num = 0
|
|
for interface in host_meta_interfaces:
|
|
assigned_networks_of_one_interface = []
|
|
if interface.get(
|
|
'type',
|
|
None) != 'bond' and not interface.get(
|
|
'mac',
|
|
None):
|
|
msg = _('The ether interface need a non-null mac ')
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
if interface.get(
|
|
'type',
|
|
None) == 'ether' and not interface.get(
|
|
'pci',
|
|
None):
|
|
msg = "The Interface need a non-null pci"
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
if interface.get('name', None):
|
|
if 'type' in interface and interface['type'] == 'bond':
|
|
bond_nic_names_list.append(interface['name'])
|
|
if interface.get('slaves', None):
|
|
bond_slaves_lists.append(interface['slaves'])
|
|
else:
|
|
msg = (
|
|
_("Slaves parameter can not be None "
|
|
"when nic type was bond."))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
else: # type == ether or interface without type field
|
|
ether_nic_names_list.append(interface['name'])
|
|
else:
|
|
msg = (_("Nic name can not be None."))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
|
|
if 'is_deployment' in interface:
|
|
if interface['is_deployment'] == "True" or interface[
|
|
'is_deployment']:
|
|
interface['is_deployment'] = 1
|
|
else:
|
|
interface['is_deployment'] = 0
|
|
|
|
if ('assigned_networks' in interface and
|
|
interface['assigned_networks'] != [''] and
|
|
interface['assigned_networks']):
|
|
have_assigned_network = True
|
|
if cluster_id:
|
|
assigned_networks_of_one_interface = self.\
|
|
_check_assigned_networks(req,
|
|
cluster_id,
|
|
interface[
|
|
'assigned_networks'])
|
|
else:
|
|
msg = "cluster must be given first when network " \
|
|
"plane is allocated"
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
if ('ip' in interface and interface['ip'] and
|
|
'netmask' in interface and interface['netmask']):
|
|
have_ip_netmask = True
|
|
|
|
if 'mac' in interface and 'ip' in interface:
|
|
host_infos = registry.get_host_interface(
|
|
req.context, host_meta)
|
|
for host_info in host_infos:
|
|
if 'host_id' in host_info:
|
|
host_meta["id"] = host_info['host_id']
|
|
|
|
if 'vswitch_type' in interface and interface[
|
|
'vswitch_type'] != '' and \
|
|
interface['vswitch_type'] not in \
|
|
ML2_TYPE:
|
|
msg = "vswitch_type %s is not supported" % interface[
|
|
'vswitch_type']
|
|
raise HTTPBadRequest(explanation=msg, request=req,
|
|
content_type="text/plain")
|
|
interface_num += 1
|
|
assigned_networks_of_intefaces.\
|
|
append(assigned_networks_of_one_interface)
|
|
|
|
for interface_id in range(interface_num):
|
|
for interface_id_compare in range(interface_id + 1, interface_num):
|
|
network1_name, network2_name = self.\
|
|
_compare_assigned_networks_of_interface(
|
|
assigned_networks_of_intefaces[interface_id],
|
|
assigned_networks_of_intefaces[interface_id_compare])
|
|
if network1_name and network2_name:
|
|
msg = (_('Network %s and network %s with same '
|
|
'cidr can not be assigned to different '
|
|
'interfaces.')) % (network1_name, network2_name)
|
|
raise HTTPBadRequest(explanation=msg)
|
|
|
|
# when assigned_network is empty, ip must be config
|
|
if not have_assigned_network:
|
|
if not have_ip_netmask:
|
|
msg = "ip and netmask must be given when network " \
|
|
"plane is not allocated"
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
# check bond slaves validity
|
|
self.check_bond_slaves_validity(
|
|
bond_slaves_lists, ether_nic_names_list)
|
|
nic_name_list = ether_nic_names_list + bond_nic_names_list
|
|
if len(set(nic_name_list)) != len(nic_name_list):
|
|
msg = (_("Nic name must be unique."))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
|
|
def _check_dvs_huge(self, host_meta, orig_host_meta={}):
|
|
host_interfaces = (host_meta.get('interfaces') or
|
|
orig_host_meta.get('interfaces'))
|
|
if host_interfaces:
|
|
if not isinstance(host_interfaces, list):
|
|
host_interfaces = eval(host_interfaces)
|
|
|
|
has_dvs = utils.get_dvs_interfaces(host_interfaces)
|
|
if has_dvs:
|
|
if (('hugepages' in host_meta and
|
|
int(host_meta['hugepages']) < 10) or
|
|
('hugepagesize' in host_meta and
|
|
host_meta['hugepagesize'] != '1G')):
|
|
host_name = (host_meta.get('name') or
|
|
orig_host_meta.get('name'))
|
|
msg = _("hugepages should be larger than 10G "
|
|
" when dvs installed on host %s") % host_name
|
|
raise HTTPForbidden(explanation=msg)
|
|
|
|
@utils.mutating
|
|
def add_host(self, req, host_meta):
|
|
"""
|
|
Adds a new host to Daisy
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:param image_meta: Mapping of metadata about host
|
|
|
|
:raises HTTPBadRequest if x-host-name is missing
|
|
"""
|
|
self._enforce(req, 'add_host')
|
|
# if host is update in '_verify_interface_among_hosts', no need add
|
|
# host continue.
|
|
cluster_id = host_meta.get('cluster', None)
|
|
if cluster_id:
|
|
self.get_cluster_meta_or_404(req, cluster_id)
|
|
|
|
if 'role' in host_meta and host_meta['role']:
|
|
role_id_list = []
|
|
host_roles = []
|
|
if 'cluster' in host_meta:
|
|
params = self._get_query_params(req)
|
|
role_list = registry.get_roles_detail(req.context, **params)
|
|
for role_name in role_list:
|
|
if role_name['cluster_id'] == host_meta['cluster']:
|
|
host_roles = list(eval(host_meta['role']))
|
|
for host_role in host_roles:
|
|
if role_name['name'] == host_role:
|
|
role_id_list.append(role_name['id'])
|
|
continue
|
|
if len(role_id_list) != len(host_roles):
|
|
msg = "The role of params %s is not exist, " \
|
|
"please use the right name" % host_roles
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
host_meta['role'] = role_id_list
|
|
else:
|
|
msg = "cluster params is none"
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
# if host is found from ssh, don't set pxe interface
|
|
if host_meta.get('os_status', None) == 'init':
|
|
self._set_pxe_interface_for_host(req, host_meta)
|
|
|
|
self._check_add_host_interfaces(req, host_meta)
|
|
|
|
if 'resource_type' in host_meta:
|
|
if host_meta['resource_type'] not in self.support_resource_type:
|
|
msg = "resource type is not supported, please use it in %s" % \
|
|
self.support_resource_type
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
host_meta['resource_type'] = 'baremetal'
|
|
|
|
if 'os_status' in host_meta:
|
|
if host_meta['os_status'] not in ['init', 'installing',
|
|
'active', 'failed', 'none']:
|
|
msg = "os_status is not valid."
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
if 'ipmi_addr' in host_meta and host_meta['ipmi_addr']:
|
|
if 'ipmi_user' not in host_meta:
|
|
host_meta['ipmi_user'] = 'zteroot'
|
|
if 'ipmi_passwd' not in host_meta:
|
|
host_meta['ipmi_passwd'] = 'superuser'
|
|
|
|
self._check_dvs_huge(host_meta)
|
|
|
|
if host_meta.get('config_set_id'):
|
|
self.get_config_set_meta_or_404(req,
|
|
host_meta['config_set_id'])
|
|
|
|
host_meta = registry.add_host_metadata(req.context, host_meta)
|
|
|
|
return {'host_meta': host_meta}
|
|
|
|
@utils.mutating
|
|
def delete_host(self, req, id):
|
|
"""
|
|
Deletes a host from Daisy.
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:param image_meta: Mapping of metadata about host
|
|
|
|
:raises HTTPBadRequest if x-host-name is missing
|
|
"""
|
|
self._enforce(req, 'delete_host')
|
|
try:
|
|
registry.delete_host_metadata(req.context, id)
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find host to delete: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to delete host: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.InUseByStore as e:
|
|
msg = (_("Host %(id)s could not be deleted because it is in use: "
|
|
"%(exc)s") % {"id": id, "exc": utils.exception_to_str(e)})
|
|
LOG.error(msg)
|
|
raise HTTPConflict(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
# self.notifier.info('host.delete', host)
|
|
params = {}
|
|
discover_hosts = registry.get_discover_hosts_detail(
|
|
req.context, **params)
|
|
for host in discover_hosts:
|
|
if host.get('host_id') == id:
|
|
LOG.info("delete discover host: %s" % id)
|
|
registry.delete_discover_host_metadata(
|
|
req.context, host['id'])
|
|
return Response(body='', status=200)
|
|
|
|
@utils.mutating
|
|
def get_host(self, req, id):
|
|
"""
|
|
Returns metadata about an host in the HTTP headers of the
|
|
response object
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:param id: The opaque host identifier
|
|
|
|
:raises HTTPNotFound if host metadata is not available to user
|
|
"""
|
|
self._enforce(req, 'get_host')
|
|
host_meta = self.get_host_meta_or_404(req, id)
|
|
host_vcpu_pin = vcpu_pin.allocate_cpus(host_meta)
|
|
host_meta.update(host_vcpu_pin)
|
|
if 'role' in host_meta and 'CONTROLLER_HA' in host_meta['role']:
|
|
host_cluster_name = host_meta['cluster']
|
|
params = {'filters': {u'name': host_cluster_name}}
|
|
cluster_info = registry.get_clusters_detail(req.context, **params)
|
|
cluster_id = cluster_info[0]['id']
|
|
|
|
ctl_ha_nodes_min_mac =\
|
|
tecs_cmn.get_ctl_ha_nodes_min_mac(req, cluster_id)
|
|
sorted_ha_nodes = \
|
|
sorted(ctl_ha_nodes_min_mac.iteritems(), key=lambda d: d[1])
|
|
sorted_ha_nodes_min_mac = \
|
|
[min_mac[1] for min_mac in sorted_ha_nodes]
|
|
|
|
host_min_mac = utils.get_host_min_mac(host_meta['interfaces'])
|
|
host_iqn = daisy_cmn.calc_host_iqn(host_min_mac)
|
|
host_meta['iqn'] = host_iqn
|
|
|
|
cluster_roles = daisy_cmn.get_cluster_roles_detail(req, cluster_id)
|
|
role_id = ''
|
|
for role in cluster_roles:
|
|
if role['name'] == 'CONTROLLER_HA':
|
|
role_id = role['id']
|
|
break
|
|
service_disks = \
|
|
tecs_cmn.get_service_disk_list(req,
|
|
{'filters': {
|
|
'role_id': role_id}})
|
|
db_share_cluster_disk = []
|
|
service_lun_info = []
|
|
for disk in service_disks:
|
|
if disk['service'] == 'db' and \
|
|
disk['disk_location'] == 'share_cluster':
|
|
db_share_cluster_disk.append(disk)
|
|
if disk['disk_location'] == 'share':
|
|
tmp_disk = {}
|
|
tmp_disk[disk['service']] = disk['lun']
|
|
service_lun_info.append(tmp_disk)
|
|
|
|
sorted_db_share_cluster = \
|
|
sorted(db_share_cluster_disk, key=lambda s: s['lun'])
|
|
|
|
db_service_lun_info = {}
|
|
for (min_mac, share_disk) in \
|
|
zip(sorted_ha_nodes_min_mac, sorted_db_share_cluster):
|
|
if host_min_mac == min_mac:
|
|
db_service_lun_info['db'] = share_disk['lun']
|
|
break
|
|
if db_service_lun_info:
|
|
service_lun_info.append(db_service_lun_info)
|
|
if service_lun_info:
|
|
host_meta['lun'] = service_lun_info
|
|
|
|
return {'host_meta': host_meta}
|
|
|
|
def detail(self, req):
|
|
"""
|
|
Returns detailed information for all available nodes
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:retval The response body is a mapping of the following form::
|
|
|
|
{'nodes': [
|
|
{'id': <ID>,
|
|
'name': <NAME>,
|
|
'description': <DESCRIPTION>,
|
|
'created_at': <TIMESTAMP>,
|
|
'updated_at': <TIMESTAMP>,
|
|
'deleted_at': <TIMESTAMP>|<NONE>,}, ...
|
|
]}
|
|
"""
|
|
self._enforce(req, 'get_hosts')
|
|
params = self._get_query_params(req)
|
|
try:
|
|
nodes = registry.get_hosts_detail(req.context, **params)
|
|
for node in nodes:
|
|
if node.get("hwm_id"):
|
|
self.check_discover_state_with_hwm(req, node)
|
|
else:
|
|
self.check_discover_state_with_no_hwm(req, node)
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
return dict(nodes=nodes)
|
|
|
|
def check_discover_state_with_hwm(self, req, node):
|
|
node['discover_state'] = None
|
|
host_meta = self.get_host_meta_or_404(req, node.get('id'))
|
|
if host_meta and host_meta.get('interfaces'):
|
|
mac_list = [
|
|
interface['mac'] for interface in
|
|
host_meta.get('interfaces') if interface.get('mac')]
|
|
if mac_list:
|
|
min_mac = min(mac_list)
|
|
pxe_discover_host = self._get_discover_host_by_mac(req,
|
|
min_mac)
|
|
if pxe_discover_host:
|
|
if pxe_discover_host.get('ip'):
|
|
node['discover_state'] = \
|
|
"SSH:" + pxe_discover_host.get('status')
|
|
else:
|
|
node['discover_state'] = \
|
|
"PXE:" + pxe_discover_host.get('status')
|
|
|
|
return node
|
|
|
|
def check_discover_state_with_no_hwm(self, req, node):
|
|
node['discover_state'] = None
|
|
host_meta = self.get_host_meta_or_404(req, node.get('id'))
|
|
if host_meta and host_meta.get('interfaces'):
|
|
ip_list = [interface['ip'] for interface
|
|
in host_meta.get('interfaces') if interface['ip']]
|
|
if ip_list:
|
|
for ip in ip_list:
|
|
ssh_discover_host = self._get_host_by_ip(req, ip)
|
|
if ssh_discover_host:
|
|
node['discover_state'] = \
|
|
"SSH:" + ssh_discover_host.get('status')
|
|
|
|
return node
|
|
|
|
def _update_hwm_host(self, req, hwm_host, hosts, hwm_ip):
|
|
hwm_host_mac = [hwm_host_interface['mac'] for hwm_host_interface
|
|
in hwm_host.get('interfaces')]
|
|
for host in hosts:
|
|
host_update_meta = dict()
|
|
host_meta = self.get_host_meta_or_404(req, host['id'])
|
|
host_mac = [host_interface['mac'] for host_interface
|
|
in host_meta.get('interfaces')]
|
|
set_same_mac = set(hwm_host_mac) & set(host_mac)
|
|
|
|
if set_same_mac:
|
|
host_update_meta['hwm_id'] = hwm_host['id']
|
|
host_update_meta['hwm_ip'] = hwm_ip
|
|
node = registry.update_host_metadata(req.context, host['id'],
|
|
host_update_meta)
|
|
return node
|
|
|
|
host_add_meta = dict()
|
|
host_add_meta['name'] = str(hwm_host['id'])
|
|
host_add_meta['description'] = 'default'
|
|
host_add_meta['os_status'] = 'init'
|
|
host_add_meta['hwm_id'] = str(hwm_host['id'])
|
|
host_add_meta['hwm_ip'] = str(hwm_ip)
|
|
host_add_meta['interfaces'] = str(hwm_host['interfaces'])
|
|
node = registry.add_host_metadata(req.context, host_add_meta)
|
|
return node
|
|
|
|
def update_hwm_host(self, req, host_meta):
|
|
self._enforce(req, 'get_hosts')
|
|
params = self._get_query_params(req)
|
|
try:
|
|
hosts = registry.get_hosts_detail(req.context, **params)
|
|
hosts_without_hwm_id = list()
|
|
hosts_hwm_id_list = list()
|
|
for host in hosts:
|
|
if host.get('hwm_id'):
|
|
hosts_hwm_id_list.append(host['hwm_id'])
|
|
else:
|
|
hosts_without_hwm_id.append(host)
|
|
|
|
hwm_hosts = host_meta['nodes']
|
|
hwm_ip = host_meta['hwm_ip']
|
|
nodes = list()
|
|
for hwm_host in eval(hwm_hosts):
|
|
if hwm_host['id'] in hosts_hwm_id_list:
|
|
continue
|
|
node = self._update_hwm_host(req, hwm_host,
|
|
hosts_without_hwm_id, hwm_ip)
|
|
nodes.append(node)
|
|
return dict(nodes=nodes)
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
|
|
def _compute_hugepage_memory(self, hugepages, memory, hugepagesize='1G'):
|
|
hugepage_memory = 0
|
|
if hugepagesize == '2M':
|
|
hugepage_memory = 2 * 1024 * int(hugepages)
|
|
if hugepagesize == '1G':
|
|
hugepage_memory = 1 * 1024 * 1024 * int(hugepages)
|
|
if hugepage_memory > memory:
|
|
msg = "The memory hugepages used is bigger " \
|
|
"than total memory."
|
|
raise HTTPBadRequest(explanation=msg)
|
|
|
|
def _count_host_pxe_info(self, interfaces):
|
|
interfaces = eval(interfaces)
|
|
input_host_pxe_info = [
|
|
interface for interface in interfaces if interface.get(
|
|
'is_deployment',
|
|
None) == "True" or interface.get(
|
|
'is_deployment',
|
|
None) == "true" or interface.get(
|
|
'is_deployment',
|
|
None) == 1]
|
|
return input_host_pxe_info
|
|
|
|
def _update_networks_phyname(self, req, interface, cluster_id):
|
|
phyname_networks = {}
|
|
cluster_networks = registry.get_networks_detail(
|
|
req.context, cluster_id)
|
|
for assigned_network in list(interface['assigned_networks']):
|
|
network_info_list = [network for network in cluster_networks
|
|
if assigned_network['name'] ==
|
|
network['name']]
|
|
if network_info_list and network_info_list[0]:
|
|
network_info = network_info_list[0]
|
|
phyname_networks[network_info['id']] = \
|
|
[network_info['name'], interface['name']]
|
|
else:
|
|
msg = "can't find network named '%s' in cluster '%s'" % (
|
|
assigned_network['name'], cluster_id)
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
# by cluster id and network_name search interface table
|
|
registry.update_phyname_of_network(req.context, phyname_networks)
|
|
|
|
def _verify_interface_in_same_host(self, interfaces, id=None):
|
|
"""
|
|
Verify interface in the input host.
|
|
:param interface: host interface info
|
|
:return:
|
|
"""
|
|
# verify interface among the input host
|
|
interfaces = eval(interfaces)
|
|
same_mac_list = [interface1['name']
|
|
for interface1 in interfaces for interface2 in
|
|
interfaces
|
|
if interface1.get('name', None) and
|
|
interface1.get('mac', None) and
|
|
interface2.get('name', None) and
|
|
interface2.get('mac', None) and
|
|
interface1.get('type', None) and
|
|
interface2.get('type', None) and
|
|
interface1['name'] != interface2['name'] and
|
|
interface1['mac'] == interface2['mac'] and
|
|
interface1['type'] != "bond" and
|
|
interface2['type'] != "bond"]
|
|
# Notice:If interface with same 'mac' is illegal,
|
|
# we need delete code #1,and raise exception in 'if' block.
|
|
# This code block is just verify for early warning.
|
|
if same_mac_list:
|
|
msg = "%s%s" % ("" if not id else "Host id:%s." % id,
|
|
"The nic name of interface [%s] with same mac,"
|
|
"please check!" %
|
|
",".join(same_mac_list))
|
|
LOG.warn(msg)
|
|
|
|
# 1-----------------------------------------------------------------
|
|
# if interface with same 'pci', raise exception
|
|
same_pci_list = [interface1['name']
|
|
for interface1 in interfaces for interface2 in
|
|
interfaces
|
|
if interface1.get('name', None) and
|
|
interface1.get('pci', None) and
|
|
interface2.get('name', None) and
|
|
interface2.get('pci', None) and
|
|
interface1.get('type', None) and
|
|
interface2.get('type', None) and
|
|
interface1['name'] != interface2['name'] and
|
|
interface1['pci'] == interface2['pci'] and
|
|
interface1['type'] == "ether" and
|
|
interface2['type'] == "ether"]
|
|
|
|
if same_pci_list:
|
|
msg = "The nic name of interface [%s] " \
|
|
"with same pci,please check!" % ",".join(
|
|
same_pci_list)
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
# 1-----------------------------------------------------------------
|
|
|
|
def _verify_interface_among_hosts(self, req, host_meta):
|
|
"""
|
|
Verify interface among the hosts in cluster
|
|
:param req:
|
|
:param cluster_id:
|
|
:param host_meta:
|
|
:return:True,host already update False,host need add
|
|
"""
|
|
# If true, the host need update, not add and update is successful.
|
|
self._verify_interface_in_same_host(host_meta['interfaces'])
|
|
|
|
# host pxe interface info
|
|
input_host_pxe_info = self._count_host_pxe_info(
|
|
host_meta['interfaces'])
|
|
# verify interface between exist host and input host in cluster
|
|
list_params = {
|
|
'sort_key': u'name',
|
|
'sort_dir': u'asc'}
|
|
all_hosts = registry.get_hosts_detail(req.context, **list_params)
|
|
exist_nodes = []
|
|
for id in [host['id'] for host in all_hosts]:
|
|
host_meta_list = registry.get_host_metadata(req.context, id)
|
|
exist_nodes.append(host_meta_list)
|
|
if input_host_pxe_info:
|
|
input_host_pxe_info = input_host_pxe_info[0]
|
|
for exist_node in exist_nodes:
|
|
id = exist_node.get('id', None)
|
|
os_status = exist_node.get('os_status', None)
|
|
exist_node_info = self.get_host(req, id).get('host_meta', None)
|
|
if not exist_node_info.get('interfaces', None):
|
|
continue
|
|
|
|
for interface in exist_node_info['interfaces']:
|
|
if interface.get(
|
|
'mac', None) != input_host_pxe_info.get(
|
|
'mac', None) or interface.get(
|
|
'type', None) == "bond":
|
|
continue
|
|
if exist_node.get('dmi_uuid') \
|
|
and exist_node.get('dmi_uuid') != \
|
|
host_meta.get('dmi_uuid'):
|
|
msg = "The 'mac' of host interface is exist in " \
|
|
"db, but 'dmi_uuid' is different.We think " \
|
|
"you want update the host, but the host " \
|
|
"can't find."
|
|
raise HTTPForbidden(explanation=msg)
|
|
return (id, os_status)
|
|
return (None, None)
|
|
|
|
def _get_swap_lv_size_m(self, memory_size_m):
|
|
if memory_size_m <= 4096:
|
|
swap_lv_size_m = 4096
|
|
elif memory_size_m <= 16384:
|
|
swap_lv_size_m = 8192
|
|
elif memory_size_m <= 65536:
|
|
swap_lv_size_m = 32768
|
|
else:
|
|
swap_lv_size_m = 65536
|
|
return swap_lv_size_m
|
|
|
|
def _ready_to_discover_host(self, host_meta, orig_host_meta):
|
|
if orig_host_meta.get('interfaces', None):
|
|
macs = [interface['mac'] for interface
|
|
in orig_host_meta['interfaces'] if interface['mac']]
|
|
for mac in macs:
|
|
delete_host_discovery_info = 'pxe_os_install_clean ' + mac
|
|
subprocess.call(delete_host_discovery_info,
|
|
shell=True,
|
|
stdout=open('/dev/null', 'w'),
|
|
stderr=subprocess.STDOUT)
|
|
if ('role' not in host_meta and
|
|
'status' in orig_host_meta and
|
|
orig_host_meta['status'] == 'with-role' and
|
|
orig_host_meta['os_status'] != 'init'):
|
|
host_meta['role'] = []
|
|
if 'os_progress' not in host_meta:
|
|
host_meta['os_progress'] = 0
|
|
if 'messages' not in host_meta:
|
|
host_meta['messages'] = ''
|
|
|
|
def _set_pxe_interface_for_host(self, req, host_meta):
|
|
all_networks = self.get_cluster_networks_info(req, type='system')
|
|
template_deploy_network = [network for network in all_networks
|
|
if network['type'] == 'system' and
|
|
network['name'] == 'DEPLOYMENT']
|
|
if not template_deploy_network:
|
|
msg = "error, can't find deployment network of system"
|
|
raise HTTPNotFound(msg)
|
|
|
|
dhcp_cidr = template_deploy_network[0]['cidr']
|
|
dhcp_ip_ranges = template_deploy_network[0]['ip_ranges']
|
|
|
|
deployment_interface_count = 0
|
|
host_meta['interfaces'] = eval(host_meta['interfaces'])
|
|
for interface in host_meta['interfaces']:
|
|
if 'ip' in interface and interface['ip']:
|
|
ip_in_cidr = utils.is_ip_in_cidr(interface['ip'],
|
|
dhcp_cidr)
|
|
if dhcp_ip_ranges:
|
|
ip_in_ranges = utils.is_ip_in_ranges(interface['ip'],
|
|
dhcp_ip_ranges)
|
|
else:
|
|
ip_in_ranges = True
|
|
if ip_in_cidr and ip_in_ranges:
|
|
interface['is_deployment'] = 1
|
|
deployment_interface_count += 1
|
|
|
|
if deployment_interface_count != 1:
|
|
if deployment_interface_count == 0:
|
|
msg = "error, can't find dhcp ip"
|
|
if deployment_interface_count > 1:
|
|
msg = "error, find more than one dhcp ip"
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg)
|
|
host_meta['interfaces'] = unicode(host_meta['interfaces'])
|
|
|
|
@utils.mutating
|
|
def update_host(self, req, id, host_meta):
|
|
"""
|
|
Updates an existing host with the registry.
|
|
|
|
:param request: The WSGI/Webob Request object
|
|
:param id: The opaque image identifier
|
|
|
|
:retval Returns the updated image information as a mapping
|
|
"""
|
|
self._enforce(req, 'update_host')
|
|
orig_host_meta = self.get_host_meta_or_404(req, id)
|
|
# Do not allow any updates on a deleted image.
|
|
# Fix for LP Bug #1060930
|
|
if orig_host_meta['deleted']:
|
|
msg = _("Forbidden to update deleted host.")
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
orig_mac_list = list()
|
|
if 'interfaces' in host_meta:
|
|
for interface_param in eval(host_meta['interfaces']):
|
|
if not interface_param.get('pci', None) and \
|
|
interface_param.get('type', None) == 'ether':
|
|
msg = "The Interface need a non-null pci"
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
if 'vswitch_type' in interface_param and interface_param[
|
|
'vswitch_type'] != '' and \
|
|
interface_param['vswitch_type'] not in ML2_TYPE:
|
|
msg = "vswitch_type %s is not supported" % interface_param[
|
|
'vswitch_type']
|
|
raise HTTPBadRequest(explanation=msg, request=req,
|
|
content_type="text/plain")
|
|
interfaces_db = orig_host_meta.get('interfaces', None)
|
|
orig_mac_list = [interface_db['mac'] for interface_db in
|
|
interfaces_db if interface_db['mac']]
|
|
orig_pci_list = [interface_db['pci'] for interface_db in
|
|
interfaces_db if interface_db['pci']]
|
|
if interfaces_db and len(orig_pci_list):
|
|
interfaces_param = eval(host_meta['interfaces'])
|
|
interfaces_db_ether = [
|
|
interface_db for interface_db in interfaces_db if
|
|
interface_db.get(
|
|
'type', None) != 'bond']
|
|
interfaces_param_ether = [
|
|
interface_param for interface_param in interfaces_param if
|
|
interface_param.get(
|
|
'type', None) != 'bond']
|
|
if len(interfaces_param) < len(interfaces_db_ether):
|
|
msg = "Forbidden to update part of interfaces"
|
|
raise HTTPForbidden(explanation=msg)
|
|
# pci in subnet interface is null,
|
|
# comment it to avoid the bug. 20160508 gaoming
|
|
if '':
|
|
pci_count = 0
|
|
for interface_db in interfaces_db:
|
|
if interface_db.get('type', None) != 'bond':
|
|
for interface_param in interfaces_param_ether:
|
|
if interface_param['pci'] == interface_db['pci']:
|
|
pci_count += 1
|
|
if interface_param[
|
|
'mac'] != interface_db['mac']:
|
|
msg = "Forbidden to modify mac of " \
|
|
"interface with pci %s" % \
|
|
interface_db['pci']
|
|
raise HTTPForbidden(explanation=msg)
|
|
if interface_param[
|
|
'type'] != interface_db['type']:
|
|
msg = "Forbidden to modify type of " \
|
|
"interface with pci %s" % \
|
|
interface_db['pci']
|
|
raise HTTPForbidden(explanation=msg)
|
|
if pci_count != len(interfaces_db_ether):
|
|
msg = "Forbidden to modify pci of interface"
|
|
raise HTTPForbidden(explanation=msg)
|
|
|
|
if 'cluster' in host_meta:
|
|
self.get_cluster_meta_or_404(req, host_meta['cluster'])
|
|
if 'cluster' in host_meta:
|
|
if orig_host_meta['status'] == 'in-cluster':
|
|
host_cluster = registry.get_host_clusters(req.context, id)
|
|
if host_meta['cluster'] != host_cluster[0]['cluster_id']:
|
|
msg = _("Forbidden to add host %s with status "
|
|
"'in-cluster' in another cluster") % id
|
|
raise HTTPForbidden(explanation=msg)
|
|
|
|
if ('resource_type' in host_meta and
|
|
host_meta['resource_type'] not in self.support_resource_type):
|
|
msg = "resource type is not supported, please use it in %s" % \
|
|
self.support_resource_type
|
|
raise HTTPNotFound(msg)
|
|
|
|
if host_meta.get(
|
|
'os_status',
|
|
None) != 'init' and orig_host_meta.get(
|
|
'os_status',
|
|
None) == 'active':
|
|
if host_meta.get('root_disk', None) and host_meta[
|
|
'root_disk'] != orig_host_meta['root_disk']:
|
|
msg = _(
|
|
"Forbidden to update root_disk of %s "
|
|
"when os_status is active if "
|
|
"you don't want to install os") % host_meta['name']
|
|
raise HTTPForbidden(explanation=msg)
|
|
else:
|
|
host_meta['root_disk'] = orig_host_meta['root_disk']
|
|
else:
|
|
if host_meta.get('root_disk', None):
|
|
root_disk = host_meta['root_disk']
|
|
elif orig_host_meta.get('root_disk', None):
|
|
root_disk = str(orig_host_meta['root_disk'])
|
|
else:
|
|
host_meta['root_disk'] = 'sda'
|
|
root_disk = host_meta['root_disk']
|
|
if not orig_host_meta.get('disks', None):
|
|
msg = "there is no disks in %s" % orig_host_meta['id']
|
|
raise HTTPNotFound(msg)
|
|
if root_disk not in orig_host_meta['disks'].keys():
|
|
msg = "There is no disk named %s" % root_disk
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
if host_meta.get(
|
|
'os_status',
|
|
None) != 'init' and orig_host_meta.get(
|
|
'os_status',
|
|
None) == 'active':
|
|
if host_meta.get(
|
|
'root_lv_size', None) and int(
|
|
host_meta['root_lv_size']) != orig_host_meta[
|
|
'root_lv_size']:
|
|
msg = _(
|
|
"Forbidden to update root_lv_size of %s "
|
|
"when os_status is active if "
|
|
"you don't want to install os") % host_meta['name']
|
|
raise HTTPForbidden(explanation=msg)
|
|
else:
|
|
host_meta['root_lv_size'] = str(orig_host_meta['root_lv_size'])
|
|
else:
|
|
if host_meta.get('root_lv_size', None):
|
|
root_lv_size = host_meta['root_lv_size']
|
|
elif orig_host_meta.get('root_lv_size', None):
|
|
root_lv_size = str(orig_host_meta['root_lv_size'])
|
|
else:
|
|
host_meta['root_lv_size'] = '102400'
|
|
root_lv_size = host_meta['root_lv_size']
|
|
if not orig_host_meta.get('disks', None):
|
|
msg = "there is no disks in %s" % orig_host_meta['id']
|
|
raise HTTPNotFound(msg)
|
|
if root_lv_size.isdigit():
|
|
root_lv_size = int(root_lv_size)
|
|
root_disk_storage_size_b_str = str(
|
|
orig_host_meta['disks'][
|
|
'%s' %
|
|
root_disk]['size'])
|
|
root_disk_storage_size_b_int = int(
|
|
root_disk_storage_size_b_str.strip().split()[0])
|
|
root_disk_storage_size_m = root_disk_storage_size_b_int // (
|
|
1024 * 1024)
|
|
boot_partition_m = 400
|
|
redundant_partiton_m = 600
|
|
free_root_disk_storage_size_m = root_disk_storage_size_m - \
|
|
boot_partition_m - redundant_partiton_m
|
|
if (root_lv_size / 4) * 4 > free_root_disk_storage_size_m:
|
|
msg = "root_lv_size of %s is larger " \
|
|
"than the free_root_disk_storage_size." % \
|
|
orig_host_meta['id']
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
if (root_lv_size / 4) * 4 < 102400:
|
|
msg = "root_lv_size of %s is too small, " \
|
|
"it must be larger than 102400M." % orig_host_meta[
|
|
'id']
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
msg = (
|
|
_("root_lv_size of %s is wrong,"
|
|
"please input a number and it must be positive number") %
|
|
orig_host_meta['id'])
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
if host_meta.get(
|
|
'os_status',
|
|
None) != 'init' and orig_host_meta.get(
|
|
'os_status',
|
|
None) == 'active':
|
|
if host_meta.get(
|
|
'swap_lv_size', None) and int(
|
|
host_meta['swap_lv_size']) != \
|
|
orig_host_meta['swap_lv_size']:
|
|
msg = _(
|
|
"Forbidden to update swap_lv_size of %s "
|
|
"when os_status is active if "
|
|
"you don't want to install os") % host_meta['name']
|
|
raise HTTPForbidden(explanation=msg)
|
|
else:
|
|
host_meta['swap_lv_size'] = str(orig_host_meta['swap_lv_size'])
|
|
else:
|
|
if host_meta.get('swap_lv_size', None):
|
|
swap_lv_size = host_meta['swap_lv_size']
|
|
elif orig_host_meta.get('swap_lv_size', None):
|
|
swap_lv_size = str(orig_host_meta['swap_lv_size'])
|
|
else:
|
|
if not orig_host_meta.get('memory', None):
|
|
msg = "there is no memory in %s" % orig_host_meta['id']
|
|
raise HTTPNotFound(msg)
|
|
memory_size_b_str = str(orig_host_meta['memory']['total'])
|
|
memory_size_b_int = int(memory_size_b_str.strip().split()[0])
|
|
memory_size_m = memory_size_b_int // 1024
|
|
swap_lv_size_m = self._get_swap_lv_size_m(memory_size_m)
|
|
host_meta['swap_lv_size'] = str(swap_lv_size_m)
|
|
swap_lv_size = host_meta['swap_lv_size']
|
|
if swap_lv_size.isdigit():
|
|
swap_lv_size = int(swap_lv_size)
|
|
disk_storage_size_b = 0
|
|
for key in orig_host_meta['disks']:
|
|
if orig_host_meta['disks'][key]['disk'].find("-fc-") \
|
|
!= -1 or orig_host_meta['disks'][key]['disk'].\
|
|
find("-iscsi-") != -1 \
|
|
or orig_host_meta['disks'][key]['name'].\
|
|
find("mpath") != -1 \
|
|
or orig_host_meta['disks'][key]['name'].\
|
|
find("spath") != -1:
|
|
continue
|
|
stroage_size_str = orig_host_meta['disks'][key]['size']
|
|
stroage_size_b_int = int(
|
|
stroage_size_str.strip().split()[0])
|
|
disk_storage_size_b = \
|
|
disk_storage_size_b + stroage_size_b_int
|
|
disk_storage_size_m = disk_storage_size_b / (1024 * 1024)
|
|
boot_partition_m = 400
|
|
redundant_partiton_m = 600
|
|
if host_meta.get('role', None):
|
|
host_role_names = eval(host_meta['role'])
|
|
elif orig_host_meta.get('role', None):
|
|
host_role_names = orig_host_meta['role']
|
|
else:
|
|
host_role_names = None
|
|
if host_role_names:
|
|
roles_of_host = []
|
|
params = self._get_query_params(req)
|
|
role_lists = registry.get_roles_detail(
|
|
req.context, **params)
|
|
for host_role_name in host_role_names:
|
|
for role in role_lists:
|
|
if host_role_name == role[
|
|
'name'] and role['type'] == 'default':
|
|
roles_of_host.append(role)
|
|
db_lv_size = 0
|
|
nova_lv_size = 0
|
|
glance_lv_size = 0
|
|
for role_of_host in roles_of_host:
|
|
if role_of_host['name'] == 'CONTROLLER_HA':
|
|
if role_of_host.get('glance_lv_size', None):
|
|
glance_lv_size = role_of_host['glance_lv_size']
|
|
if role_of_host.get('db_lv_size', None):
|
|
db_lv_size = role_of_host['db_lv_size']
|
|
if role_of_host['name'] == 'COMPUTER':
|
|
nova_lv_size = role_of_host['nova_lv_size']
|
|
free_disk_storage_size_m = disk_storage_size_m - \
|
|
boot_partition_m - \
|
|
redundant_partiton_m - \
|
|
(root_lv_size / 4) * 4 - (glance_lv_size / 4) * 4 - \
|
|
(nova_lv_size / 4) * 4 - \
|
|
(db_lv_size / 4) * 4
|
|
else:
|
|
free_disk_storage_size_m = disk_storage_size_m - \
|
|
boot_partition_m - redundant_partiton_m - \
|
|
(root_lv_size / 4) * 4
|
|
if (swap_lv_size / 4) * 4 > free_disk_storage_size_m:
|
|
msg = "the sum of swap_lv_size and " \
|
|
"glance_lv_size and nova_lv_size and " \
|
|
"db_lv_size of %s is larger " \
|
|
"than the free_disk_storage_size." % \
|
|
orig_host_meta['id']
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
if (swap_lv_size / 4) * 4 < 2000:
|
|
msg = "swap_lv_size of %s is too small, " \
|
|
"it must be larger than 2000M." % orig_host_meta[
|
|
'id']
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
msg = (
|
|
_("swap_lv_size of %s is wrong,"
|
|
"please input a number and it must be positive number") %
|
|
orig_host_meta['id'])
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
if host_meta.get(
|
|
'os_status',
|
|
None) != 'init' and orig_host_meta.get(
|
|
'os_status',
|
|
None) == 'active':
|
|
if host_meta.get('root_pwd', None) and host_meta[
|
|
'root_pwd'] != orig_host_meta['root_pwd']:
|
|
msg = _(
|
|
"Forbidden to update root_pwd of %s "
|
|
"when os_status is active if "
|
|
"you don't want to install os") % host_meta['name']
|
|
raise HTTPForbidden(explanation=msg)
|
|
else:
|
|
host_meta['root_pwd'] = orig_host_meta['root_pwd']
|
|
else:
|
|
if not host_meta.get(
|
|
'root_pwd',
|
|
None) and not orig_host_meta.get(
|
|
'root_pwd',
|
|
None):
|
|
host_meta['root_pwd'] = 'ossdbg1'
|
|
|
|
if host_meta.get(
|
|
'os_status',
|
|
None) != 'init' and orig_host_meta.get(
|
|
'os_status',
|
|
None) == 'active':
|
|
if host_meta.get('isolcpus', None) and host_meta[
|
|
'isolcpus'] != orig_host_meta['isolcpus']:
|
|
msg = _(
|
|
"Forbidden to update isolcpus of %s "
|
|
"when os_status is active if "
|
|
"you don't want to install os") % host_meta['name']
|
|
raise HTTPForbidden(explanation=msg)
|
|
else:
|
|
host_meta['isolcpus'] = orig_host_meta['isolcpus']
|
|
else:
|
|
if host_meta.get('isolcpus', None):
|
|
isolcpus = host_meta['isolcpus']
|
|
elif orig_host_meta.get('isolcpus', None):
|
|
isolcpus = orig_host_meta['isolcpus']
|
|
else:
|
|
host_meta['isolcpus'] = None
|
|
isolcpus = host_meta['isolcpus']
|
|
if not orig_host_meta.get('cpu', None):
|
|
msg = "there is no cpu in %s" % orig_host_meta['id']
|
|
raise HTTPNotFound(msg)
|
|
cpu_num = orig_host_meta['cpu']['total']
|
|
if isolcpus:
|
|
isolcpus_lists = [value.split('-')
|
|
for value in isolcpus.split(',')]
|
|
isolcpus_list = []
|
|
for value in isolcpus_lists:
|
|
isolcpus_list = isolcpus_list + value
|
|
for value in isolcpus_list:
|
|
if int(value) < 0 or int(value) > cpu_num - 1:
|
|
msg = "isolcpus number must be lager than 0 and " \
|
|
"less than %d" % (
|
|
cpu_num - 1)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
clusters = registry.get_clusters_detail(req.context)
|
|
orig_cluster_name = orig_host_meta.get('cluster', None)
|
|
orig_cluster_id = None
|
|
for cluster in clusters:
|
|
if cluster['name'] == orig_cluster_name:
|
|
orig_cluster_id = cluster['id']
|
|
cluster_id = host_meta.get('cluster', orig_cluster_id)
|
|
|
|
params = self._get_query_params(req)
|
|
role_list = registry.get_roles_detail(req.context, **params)
|
|
if 'role' in host_meta:
|
|
role_id_list = []
|
|
if 'cluster' in host_meta:
|
|
host_roles = list()
|
|
for role_name in role_list:
|
|
if role_name['cluster_id'] == host_meta['cluster']:
|
|
host_roles = list(eval(host_meta['role']))
|
|
for host_role in host_roles:
|
|
if role_name['name'] == host_role:
|
|
role_id_list.append(role_name['id'])
|
|
continue
|
|
if len(role_id_list) != len(
|
|
host_roles) and host_meta['role'] != u"[u'']":
|
|
msg = "The role of params %s is not exist, " \
|
|
"please use the right name" % host_roles
|
|
raise HTTPNotFound(msg)
|
|
host_meta['role'] = role_id_list
|
|
else:
|
|
msg = "cluster params is none"
|
|
raise HTTPNotFound(msg)
|
|
|
|
if 'interfaces' in host_meta:
|
|
host_meta_interfaces = list(eval(host_meta['interfaces']))
|
|
ether_nic_names_list = list()
|
|
bond_nic_names_list = list()
|
|
bond_slaves_lists = list()
|
|
interface_num = 0
|
|
assigned_networks_of_interfaces = []
|
|
for interface in host_meta_interfaces:
|
|
if interface.get('name', None):
|
|
if 'type' in interface and interface['type'] == 'bond':
|
|
bond_nic_names_list.append(interface['name'])
|
|
slave_list = []
|
|
if interface.get('slaves', None):
|
|
bond_slaves_lists.append(interface['slaves'])
|
|
elif interface.get('slave1', None) and \
|
|
interface.get('slave2', None):
|
|
slave_list.append(interface['slave1'])
|
|
slave_list.append(interface['slave2'])
|
|
bond_slaves_lists.append(slave_list)
|
|
else:
|
|
msg = (
|
|
_("Slaves parameter can not be "
|
|
"None when nic type was bond."))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
else: # type == ether or interface without type field
|
|
ether_nic_names_list.append(interface['name'])
|
|
else:
|
|
msg = (_("Nic name can not be None."))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
if 'is_deployment' in interface:
|
|
if interface['is_deployment'] == "True" or interface[
|
|
'is_deployment']:
|
|
interface['is_deployment'] = 1
|
|
else:
|
|
interface['is_deployment'] = 0
|
|
|
|
if ('assigned_networks' in interface and
|
|
interface['assigned_networks'] != [''] and
|
|
interface['assigned_networks']):
|
|
if cluster_id:
|
|
LOG.info(
|
|
"interface['assigned_networks']: %s" %
|
|
interface['assigned_networks'])
|
|
assigned_networks_of_one_interface = self.\
|
|
_check_assigned_networks(req,
|
|
cluster_id,
|
|
interface['assigned_'
|
|
'networks'])
|
|
self._update_networks_phyname(
|
|
req, interface, cluster_id)
|
|
host_meta['cluster'] = cluster_id
|
|
else:
|
|
msg = "cluster must be given first " \
|
|
"when network plane is allocated"
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
assigned_networks_of_interfaces.\
|
|
append(assigned_networks_of_one_interface)
|
|
else:
|
|
assigned_networks_of_interfaces.\
|
|
append([])
|
|
interface_num += 1
|
|
self._compare_assigned_networks_between_interfaces(
|
|
interface_num, assigned_networks_of_interfaces)
|
|
|
|
# check bond slaves validity
|
|
self.check_bond_slaves_validity(
|
|
bond_slaves_lists, ether_nic_names_list)
|
|
nic_name_list = ether_nic_names_list + bond_nic_names_list
|
|
if len(set(nic_name_list)) != len(nic_name_list):
|
|
msg = (_("Nic name must be unique."))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
|
|
if 'os_status' in host_meta:
|
|
if host_meta['os_status'] not in \
|
|
['init', 'installing', 'active', 'failed', 'none']:
|
|
msg = "os_status is not valid."
|
|
raise HTTPNotFound(msg)
|
|
|
|
if (('ipmi_addr' in host_meta and host_meta['ipmi_addr']) or
|
|
orig_host_meta['ipmi_addr']):
|
|
if 'ipmi_user' not in host_meta and not\
|
|
orig_host_meta['ipmi_user']:
|
|
host_meta['ipmi_user'] = 'zteroot'
|
|
if 'ipmi_passwd' not in host_meta and not \
|
|
orig_host_meta['ipmi_passwd']:
|
|
host_meta['ipmi_passwd'] = 'superuser'
|
|
|
|
if host_meta.get('os_status', None) != 'init' and \
|
|
orig_host_meta.get('os_status', None) == 'active':
|
|
if host_meta.get(
|
|
'hugepages', None) and int(
|
|
host_meta['hugepages']) != orig_host_meta['hugepages']:
|
|
msg = _("Forbidden to update hugepages of %s"
|
|
" when os_status is active if "
|
|
"you don't want to install os") % host_meta['name']
|
|
raise HTTPForbidden(explanation=msg)
|
|
else:
|
|
if 'hugepages' in host_meta:
|
|
if not orig_host_meta.get('memory', {}).get('total', None):
|
|
msg = "The host %s has no memory" % id
|
|
raise HTTPNotFound(explanation=msg)
|
|
memory = orig_host_meta.get('memory', {}).get('total', None)
|
|
if host_meta['hugepages'] is None:
|
|
host_meta['hugepages'] = 0
|
|
if int(host_meta['hugepages']) < 0:
|
|
msg = "The parameter hugepages must be zero or " \
|
|
"positive integer."
|
|
raise HTTPBadRequest(explanation=msg)
|
|
if 'hugepagesize' not in host_meta and \
|
|
orig_host_meta.get('hugepagesize', None):
|
|
self._compute_hugepage_memory(host_meta['hugepages'],
|
|
int(memory.strip().split(
|
|
' ')[0]),
|
|
orig_host_meta[
|
|
'hugepagesize'])
|
|
if 'hugepagesize' not in host_meta and \
|
|
not orig_host_meta.get('hugepagesize', None):
|
|
self._compute_hugepage_memory(
|
|
host_meta['hugepages'], int(
|
|
memory.strip().split(' ')[0]))
|
|
|
|
if host_meta.get('os_status', None) != 'init' and \
|
|
orig_host_meta.get('os_status', None) == 'active':
|
|
if host_meta.get('hugepagesize', None) and \
|
|
host_meta['hugepagesize'] != \
|
|
orig_host_meta['hugepagesize']:
|
|
msg = _(
|
|
"Forbidden to update hugepagesize of %s"
|
|
" when os_status is active if you don't "
|
|
"want to install os") % host_meta['name']
|
|
raise HTTPForbidden(explanation=msg)
|
|
else:
|
|
if 'hugepagesize' in host_meta:
|
|
if not orig_host_meta.get('memory', {}).get('total', None):
|
|
msg = "The host %s has no memory" % id
|
|
raise HTTPNotFound(explanation=msg)
|
|
memory = orig_host_meta.get('memory', {}).get('total', None)
|
|
if host_meta['hugepagesize'] is None:
|
|
host_meta['hugepagesize'] = '1G'
|
|
else:
|
|
host_meta['hugepagesize'].upper()
|
|
if host_meta['hugepagesize'] not in SUPPORT_HOST_PAGE_SIZE:
|
|
msg = "The value 0f parameter hugepagesize " \
|
|
"is not supported."
|
|
raise HTTPBadRequest(explanation=msg)
|
|
if host_meta['hugepagesize'] == '2M' and \
|
|
int(host_meta['hugepagesize'][0]) * 1024 > \
|
|
int(memory.strip().split(' ')[0]):
|
|
msg = "The host %s forbid to use hugepage because it's " \
|
|
"memory is too small" % id
|
|
raise HTTPForbidden(explanation=msg)
|
|
if host_meta['hugepagesize'] == '1G' and \
|
|
int(host_meta['hugepagesize'][0]) * 1024 * 1024 > \
|
|
int(memory.strip().split(' ')[0]):
|
|
msg = "The hugepagesize is too big, you can choose 2M " \
|
|
"for a try."
|
|
raise HTTPBadRequest(explanation=msg)
|
|
if 'hugepages' in host_meta:
|
|
self._compute_hugepage_memory(host_meta['hugepages'], int(
|
|
memory.strip().split(' ')[0]),
|
|
host_meta['hugepagesize'])
|
|
if 'hugepages' not in host_meta and \
|
|
orig_host_meta.get('hugepages', None):
|
|
self._compute_hugepage_memory(orig_host_meta['hugepages'],
|
|
int(
|
|
memory.strip().split(' ')[0]),
|
|
host_meta['hugepagesize'])
|
|
|
|
self._check_dvs_huge(host_meta, orig_host_meta)
|
|
|
|
if host_meta.get('os_status', None) != 'init' \
|
|
and orig_host_meta.get('os_status', None) == 'active':
|
|
if host_meta.get('os_version', None) and host_meta['os_version'] \
|
|
!= orig_host_meta['os_version_file']:
|
|
msg = _("Forbidden to update os_version of %s "
|
|
"when os_status is active if "
|
|
"you don't want to install os") % host_meta['name']
|
|
raise HTTPForbidden(explanation=msg)
|
|
|
|
if (host_meta.get('config_set_id') and
|
|
host_meta['config_set_id'] !=
|
|
orig_host_meta.get('config_set_id')):
|
|
self.get_config_set_meta_or_404(req,
|
|
host_meta['config_set_id'])
|
|
|
|
if host_meta.get('os_status', None) == 'init':
|
|
self._ready_to_discover_host(host_meta, orig_host_meta)
|
|
|
|
try:
|
|
if host_meta.get('cluster', None):
|
|
host_detail = self.get_host_meta_or_404(req, id)
|
|
pxe_macs = [interface['mac'] for interface in host_detail[
|
|
'interfaces'] if interface['is_deployment']]
|
|
if not pxe_macs:
|
|
self.add_ssh_host_to_cluster_and_assigned_network(
|
|
req, host_meta['cluster'], id)
|
|
|
|
host_meta = registry.update_host_metadata(req.context, id,
|
|
host_meta)
|
|
|
|
if orig_mac_list:
|
|
orig_min_mac = min(orig_mac_list)
|
|
discover_host = self._get_discover_host_by_mac(req,
|
|
orig_min_mac)
|
|
if discover_host:
|
|
discover_host_params = {
|
|
"mac": orig_min_mac,
|
|
"status": "DISCOVERY_SUCCESSFUL"
|
|
}
|
|
self.update_pxe_host(req, discover_host['id'],
|
|
discover_host_params)
|
|
except exception.Invalid as e:
|
|
msg = (_("Failed to update host metadata. Got error: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find host to update: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to update host: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except (exception.Conflict, exception.Duplicate) as e:
|
|
LOG.error(utils.exception_to_str(e))
|
|
raise HTTPConflict(body=_('Host operation conflicts'),
|
|
request=req,
|
|
content_type='text/plain')
|
|
else:
|
|
self.notifier.info('host.update', host_meta)
|
|
|
|
return {'host_meta': host_meta}
|
|
|
|
def _checker_the_ip_or_hostname_valid(self, ip_str):
|
|
try:
|
|
socket.gethostbyname_ex(ip_str)
|
|
return True
|
|
except Exception:
|
|
if netaddr.IPAddress(ip_str).version == 6:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def check_vlan_nic_and_join_vlan_network(
|
|
self, req, cluster_id, host_list, networks):
|
|
father_vlan_list = []
|
|
for host_id in host_list:
|
|
host_meta_detail = self.get_host_meta_or_404(req, host_id)
|
|
if 'interfaces' in host_meta_detail:
|
|
interfac_list = host_meta_detail.get('interfaces', None)
|
|
for interface_info in interfac_list:
|
|
host_ip = interface_info.get('ip', None)
|
|
if interface_info['type'] == 'vlan' and host_ip:
|
|
check_ip_if_valid =\
|
|
self._checker_the_ip_or_hostname_valid(host_ip)
|
|
if not check_ip_if_valid:
|
|
msg = "Error:The %s is not the right ip!" % host_ip
|
|
LOG.error(msg)
|
|
raise exception.Forbidden(msg)
|
|
nic_name = interface_info['name'].split('.')[0]
|
|
vlan_id = interface_info['name'].split('.')[1]
|
|
for network in networks:
|
|
if vlan_id == network['vlan_id']:
|
|
if network['network_type'] not in \
|
|
['DATAPLANE', 'EXTERNAL']:
|
|
father_vlan_list.append(
|
|
{nic_name: {'name': network['name'],
|
|
'ip': host_ip}})
|
|
interface_info['assigned_networks'].\
|
|
append({'name': network['name'],
|
|
'ip': host_ip})
|
|
LOG.info(
|
|
"add the nic %s of the host %s to"
|
|
" assigned_network %s" %
|
|
(interface_info['name'], host_id,
|
|
interface_info['assigned_networks']))
|
|
return father_vlan_list
|
|
|
|
def check_bond_or_ether_nic_and_join_network(
|
|
self, req, cluster_id, host_list, networks, father_vlan_list):
|
|
for host_id in host_list:
|
|
host_info = self.get_host_meta_or_404(req, host_id)
|
|
if 'interfaces' in host_info:
|
|
update_host_interface = 0
|
|
interfac_meta_list = host_info.get('interfaces', None)
|
|
for interface_info in interfac_meta_list:
|
|
update_flag = 0
|
|
host_info_ip = interface_info.get('ip', None)
|
|
if interface_info['type'] != 'vlan':
|
|
nic_name = interface_info['name']
|
|
for nic in father_vlan_list:
|
|
if nic.keys()[0] == nic_name:
|
|
update_flag = 1
|
|
update_host_interface = 1
|
|
interface_info['assigned_networks']\
|
|
.append(nic.values()[0])
|
|
if update_flag:
|
|
continue
|
|
if host_info_ip:
|
|
check_ip_if_valid =\
|
|
self._checker_the_ip_or_hostname_valid(
|
|
host_info_ip)
|
|
if not check_ip_if_valid:
|
|
msg = "Error:The %s is not the right ip!"\
|
|
% host_info_ip
|
|
LOG.error(msg)
|
|
raise exception.Forbidden(msg)
|
|
for network in networks:
|
|
if network.get('cidr', None):
|
|
ip_in_cidr = utils.is_ip_in_cidr(
|
|
host_info_ip, network['cidr'])
|
|
if ip_in_cidr:
|
|
vlan_id = network['vlan_id']
|
|
if not vlan_id:
|
|
update_host_interface = 1
|
|
if network['network_type'] not in\
|
|
['DATAPLANE', 'EXTERNAL']:
|
|
interface_info[
|
|
'assigned_networks']\
|
|
.append({'name':
|
|
network['name'],
|
|
'ip':
|
|
host_info_ip})
|
|
LOG.info(
|
|
"add the nic %s "
|
|
"of the host"
|
|
" %s to assigned_network"
|
|
" %s" %
|
|
(nic_name, host_id,
|
|
interface_info[
|
|
'assigned_networks']))
|
|
else:
|
|
msg = (
|
|
"the nic %s of ip %s is "
|
|
"in the %s cidr "
|
|
"range,but the network vlan "
|
|
"id is %s " %
|
|
(nic_name, host_info_ip,
|
|
network['name'], vlan_id))
|
|
LOG.error(msg)
|
|
raise exception.Forbidden(msg)
|
|
if update_host_interface:
|
|
host_meta = {}
|
|
host_meta['cluster'] = cluster_id
|
|
host_meta['interfaces'] = str(interfac_meta_list)
|
|
host_meta = registry.update_host_metadata(req.context,
|
|
host_id,
|
|
host_meta)
|
|
LOG.info("add the host %s join the cluster %s and"
|
|
" assigned_network successful" %
|
|
(host_id, cluster_id))
|
|
|
|
def add_ssh_host_to_cluster_and_assigned_network(
|
|
self, req, cluster_id, host_id):
|
|
if cluster_id:
|
|
host_list = []
|
|
father_vlan_list = []
|
|
# cluster_meta = {}
|
|
discover_successful = 0
|
|
host_info = self.get_host_meta_or_404(req, host_id)
|
|
host_status = host_info.get('status', None)
|
|
if host_status != 'init':
|
|
interfac_meta_list = host_info.get('interfaces', None)
|
|
for interface_info in interfac_meta_list:
|
|
assigned_networks = interface_info.get(
|
|
'assigned_networks', None)
|
|
if assigned_networks:
|
|
discover_successful = 1
|
|
if not discover_successful:
|
|
host_list.append(host_id)
|
|
|
|
if host_list:
|
|
# cluster_meta['nodes']=str(host_list)
|
|
# LOG.info("add ssh host %s to cluster %s" %
|
|
# (host_list, cluster_id))
|
|
# cluster_meta = registry.update_cluster_metadata(req.context,
|
|
# cluster_id,
|
|
# cluster_meta)
|
|
params = {'filters': {'cluster_id': cluster_id}}
|
|
networks = registry.get_networks_detail(req.context,
|
|
cluster_id, **params)
|
|
father_vlan_list = self.check_vlan_nic_and_join_vlan_network(
|
|
req, cluster_id, host_list, networks)
|
|
self.check_bond_or_ether_nic_and_join_network(
|
|
req, cluster_id, host_list, networks, father_vlan_list)
|
|
|
|
def update_progress_to_db(self, req, update_info, discover_host_meta):
|
|
discover = {}
|
|
discover['status'] = update_info['status']
|
|
discover['message'] = update_info['message']
|
|
if update_info.get('host_id'):
|
|
discover['host_id'] = update_info['host_id']
|
|
LOG.info("discover:%s", discover)
|
|
registry.update_discover_host_metadata(req.context,
|
|
discover_host_meta['id'],
|
|
discover)
|
|
|
|
def thread_bin(self, req, cluster_id, discover_host_meta):
|
|
cmd = 'mkdir -p /var/log/daisy/discover_host/'
|
|
daisy_cmn.subprocess_call(cmd)
|
|
if not discover_host_meta['passwd']:
|
|
msg = "the passwd of ip %s is none." % discover_host_meta['ip']
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(msg)
|
|
var_log_path = "/var/log/daisy/discover_host/%s_discovery_host.log" \
|
|
% discover_host_meta['ip']
|
|
with open(var_log_path, "w+") as fp:
|
|
try:
|
|
trustme_result = subprocess.check_output(
|
|
'/var/lib/daisy/tecs/trustme.sh %s %s' %
|
|
(discover_host_meta['ip'], discover_host_meta['passwd']),
|
|
shell=True, stderr=subprocess.STDOUT)
|
|
if 'Permission denied' in trustme_result:
|
|
# when passwd was wrong
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = "Passwd was wrong, do" \
|
|
"trustme.sh %s failed!"\
|
|
% discover_host_meta['ip']
|
|
self.update_progress_to_db(req, update_info,
|
|
discover_host_meta)
|
|
msg = (_("Do trustme.sh %s failed!" %
|
|
discover_host_meta['ip']))
|
|
LOG.warn(_(msg))
|
|
fp.write(msg)
|
|
elif 'is unreachable' in trustme_result:
|
|
# when host ip was unreachable
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = "Host ip was unreachable," \
|
|
" do trustme.sh %s failed!" %\
|
|
discover_host_meta['ip']
|
|
self.update_progress_to_db(req, update_info,
|
|
discover_host_meta)
|
|
msg = (_("Do trustme.sh %s failed!" %
|
|
discover_host_meta['ip']))
|
|
LOG.warn(_(msg))
|
|
except subprocess.CalledProcessError as e:
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
msg = "discover host for %s failed! raise CalledProcessError" \
|
|
" when execute trustme.sh." % discover_host_meta['ip']
|
|
update_info['message'] = msg
|
|
self.update_progress_to_db(
|
|
req, update_info, discover_host_meta)
|
|
LOG.error(_(msg))
|
|
fp.write(e.output.strip())
|
|
return
|
|
except:
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = "discover host for %s failed!" \
|
|
% discover_host_meta['ip']
|
|
self.update_progress_to_db(
|
|
req, update_info, discover_host_meta)
|
|
LOG.error(_("discover host for %s failed!"
|
|
% discover_host_meta['ip']))
|
|
fp.write("discover host for %s failed!"
|
|
% discover_host_meta['ip'])
|
|
return
|
|
|
|
try:
|
|
cmd = 'clush -S -b -w %s "rm -rf /home/daisy/discover_host"'\
|
|
% (discover_host_meta['ip'],)
|
|
daisy_cmn.subprocess_call(cmd, fp)
|
|
cmd = 'clush -S -w %s "mkdir -p /home/daisy/discover_host"'\
|
|
% (discover_host_meta['ip'],)
|
|
daisy_cmn.subprocess_call(cmd, fp)
|
|
cmd = 'clush -S -w %s "chmod 777 /home/daisy/discover_host"'\
|
|
% (discover_host_meta['ip'],)
|
|
daisy_cmn.subprocess_call(cmd, fp)
|
|
except subprocess.CalledProcessError as e:
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
msg = "raise CalledProcessError when execute cmd for host %s."\
|
|
% discover_host_meta['ip']
|
|
update_info['message'] = msg
|
|
self.update_progress_to_db(
|
|
req, update_info, discover_host_meta)
|
|
LOG.error(_(msg))
|
|
fp.write(e.output.strip())
|
|
return
|
|
|
|
try:
|
|
subprocess.check_output(
|
|
'clush -S -w %s -c /var/lib/daisy/tecs/getnodeinfo.sh'
|
|
' /var/lib/daisy/tecs/jq-1.3-2.el7.x86_64.rpm '
|
|
'--dest=/home/daisy/discover_host' %
|
|
(discover_host_meta['ip'],),
|
|
shell=True, stderr=subprocess.STDOUT)
|
|
except subprocess.CalledProcessError as e:
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = "scp getnodeinfo.sh and " \
|
|
"jq-1.3-2.el7.x86_64.rpm for %s" \
|
|
" failed!" % discover_host_meta['ip']
|
|
self.update_progress_to_db(req, update_info,
|
|
discover_host_meta)
|
|
LOG.error(_("scp getnodeinfo.sh and"
|
|
" jq-1.3-2.el7.x86_64.rpm for %s failed!"
|
|
% discover_host_meta['ip']))
|
|
fp.write(e.output.strip())
|
|
return
|
|
|
|
try:
|
|
subprocess.check_output(
|
|
'clush -S -w %s rpm -ivh --force '
|
|
'/home/daisy/discover_host/jq-1.3-2.el7.x86_64.rpm'
|
|
% (discover_host_meta['ip'],),
|
|
shell=True, stderr=subprocess.STDOUT)
|
|
except subprocess.CalledProcessError as e:
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = \
|
|
"install jq-1.3-2.el7.x86_64.rpm for %s failed!"\
|
|
% discover_host_meta['ip']
|
|
self.update_progress_to_db(req, update_info,
|
|
discover_host_meta)
|
|
LOG.error(_("install jq-1.3-2.el7.x86_64.rpm for %s failed!"
|
|
% discover_host_meta['ip']))
|
|
fp.write(e.output.strip())
|
|
return
|
|
|
|
try:
|
|
exc_result = subprocess.check_output(
|
|
'clush -S -w %s /home/daisy/discover_host/getnodeinfo.sh'
|
|
% (discover_host_meta['ip'],),
|
|
shell=True, stderr=subprocess.STDOUT)
|
|
if 'Failed connect to' in exc_result:
|
|
# when openstack-ironic-discoverd.service has problem
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = "Do getnodeinfo.sh %s failed!" \
|
|
% discover_host_meta['ip']
|
|
self.update_progress_to_db(req, update_info,
|
|
discover_host_meta)
|
|
msg = (_("Do trustme.sh %s failed!" %
|
|
discover_host_meta['ip']))
|
|
LOG.warn(_(msg))
|
|
fp.write(msg)
|
|
else:
|
|
mac_info = re.search(r'"mac": ([^,\n]*)', exc_result)
|
|
mac = eval(mac_info.group(1))
|
|
filters = {'mac': mac}
|
|
update_info = {}
|
|
host_interfaces =\
|
|
registry.get_all_host_interfaces(req.context, filters)
|
|
if host_interfaces:
|
|
update_info['status'] = 'DISCOVERY_SUCCESSFUL'
|
|
update_info['message'] =\
|
|
"discover host for %s successfully!" %\
|
|
discover_host_meta['ip']
|
|
update_info['host_id'] = host_interfaces[0]['host_id']
|
|
LOG.info("update_info['host_id']:%s",
|
|
update_info['host_id'])
|
|
self.update_progress_to_db(req, update_info,
|
|
discover_host_meta)
|
|
LOG.info(_("discover host for %s successfully!"
|
|
% discover_host_meta['ip']))
|
|
fp.write(exc_result)
|
|
else:
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = \
|
|
"discover host for %s failed!please view" \
|
|
" the daisy api log" % discover_host_meta['ip']
|
|
self.update_progress_to_db(req, update_info,
|
|
discover_host_meta)
|
|
LOG.error(_("discover host for %s failed!" %
|
|
discover_host_meta['ip']))
|
|
fp.write(exc_result)
|
|
return
|
|
except subprocess.CalledProcessError as e:
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = "discover host for %s failed!" %\
|
|
discover_host_meta['ip']
|
|
self.update_progress_to_db(
|
|
req, update_info, discover_host_meta)
|
|
LOG.error(_("discover host for %s failed!" %
|
|
discover_host_meta['ip']))
|
|
fp.write(e.output.strip())
|
|
return
|
|
|
|
discover_host_info = \
|
|
registry.get_discover_host_metadata(req.context,
|
|
discover_host_meta['id'])
|
|
if not discover_host_info['host_id']:
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_FAILED'
|
|
update_info['message'] = "discover host for %s failed!" \
|
|
% discover_host_info['ip']
|
|
self.update_progress_to_db(
|
|
req, update_info, discover_host_info)
|
|
msg = (_("discover host for %s failed!" %
|
|
discover_host_info['ip']))
|
|
LOG.error(msg)
|
|
return
|
|
else:
|
|
self.add_ssh_host_to_cluster_and_assigned_network(
|
|
req, cluster_id, discover_host_info['host_id'])
|
|
|
|
@utils.mutating
|
|
def discover_host_bin(self, req, host_meta):
|
|
params = {}
|
|
cluster_id = host_meta.get('cluster_id', None)
|
|
if cluster_id:
|
|
params = {'cluster_id': cluster_id}
|
|
discover_host_meta_list =\
|
|
registry.get_discover_hosts_detail(req.context, **params)
|
|
filters = {}
|
|
host_interfaces = \
|
|
registry.get_all_host_interfaces(req.context, filters)
|
|
existed_host_ip = [host['ip'] for host in host_interfaces]
|
|
LOG.info('existed_host_ip**: %s', existed_host_ip)
|
|
|
|
for discover_host in discover_host_meta_list:
|
|
if discover_host['status'] != 'DISCOVERY_SUCCESSFUL':
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERING'
|
|
update_info['message'] = 'DISCOVERING'
|
|
update_info['host_id'] = 'None'
|
|
self.update_progress_to_db(req, update_info, discover_host)
|
|
threads = []
|
|
for discover_host_meta in discover_host_meta_list:
|
|
if discover_host_meta['ip'] in existed_host_ip:
|
|
update_info = {}
|
|
update_info['status'] = 'DISCOVERY_SUCCESSFUL'
|
|
update_info['message'] = "discover host for %s successfully!" \
|
|
% discover_host_meta['ip']
|
|
host_id_list = \
|
|
[host['host_id'] for host in host_interfaces
|
|
if discover_host_meta['ip'] == host['ip']]
|
|
update_info['host_id'] = host_id_list[0]
|
|
self.update_progress_to_db(
|
|
req, update_info, discover_host_meta)
|
|
continue
|
|
if discover_host_meta['status'] != 'DISCOVERY_SUCCESSFUL':
|
|
t = threading.Thread(
|
|
target=self.thread_bin, args=(
|
|
req, cluster_id, discover_host_meta))
|
|
t.setDaemon(True)
|
|
t.start()
|
|
threads.append(t)
|
|
LOG.info(_("all host discovery threads have started, "
|
|
"please waiting...."))
|
|
|
|
try:
|
|
for t in threads:
|
|
t.join()
|
|
except:
|
|
LOG.warn(_("Join discover host thread %s failed!" % t))
|
|
|
|
@utils.mutating
|
|
def discover_host(self, req, host_meta):
|
|
cluster_id = host_meta.get('cluster_id', None)
|
|
if cluster_id:
|
|
self.get_cluster_meta_or_404(req, cluster_id)
|
|
|
|
config = ConfigParser.ConfigParser()
|
|
config.read("/home/daisy_install/daisy.conf")
|
|
daisy_management_ip = config.get("DEFAULT", "daisy_management_ip")
|
|
if daisy_management_ip:
|
|
cmd = 'dhcp_linenumber=`grep -n "dhcp_ip="' \
|
|
' /var/lib/daisy/tecs/getnodeinfo.sh|cut -d ":" -f 1` && ' \
|
|
'sed -i "${dhcp_linenumber}c dhcp_ip=\'%s\'" ' \
|
|
'/var/lib/daisy/tecs/getnodeinfo.sh' % (daisy_management_ip,)
|
|
daisy_cmn.subprocess_call(cmd)
|
|
|
|
config_discoverd = ConfigParser.ConfigParser(
|
|
defaults=DISCOVER_DEFAULTS)
|
|
config_discoverd.read("/etc/ironic-discoverd/discoverd.conf")
|
|
listen_port = config_discoverd.get("discoverd", "listen_port")
|
|
if listen_port:
|
|
cmd = 'port_linenumber=`grep -n "listen_port="' \
|
|
' /var/lib/daisy/tecs/getnodeinfo.sh|cut -d ":" -f 1` && ' \
|
|
'sed -i "${port_linenumber}c listen_port=\'%s\'" ' \
|
|
'/var/lib/daisy/tecs/getnodeinfo.sh' % (listen_port,)
|
|
daisy_cmn.subprocess_call(cmd)
|
|
|
|
discovery_host_thread = threading.Thread(
|
|
target=self.discover_host_bin, args=(req, host_meta))
|
|
discovery_host_thread.start()
|
|
return {"status": "begin discover host"}
|
|
|
|
@utils.mutating
|
|
def add_discover_host(self, req, host_meta):
|
|
"""
|
|
Adds a new discover host to Daisy
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:param host_meta: Mapping of metadata about host
|
|
|
|
:raises HTTPBadRequest if x-host-name is missing
|
|
"""
|
|
self._enforce(req, 'add_discover_host')
|
|
LOG.warn("host_meta: %s" % host_meta)
|
|
if not host_meta.get('ip', None):
|
|
msg = "IP parameter can not be None."
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
discover_hosts_ip = self._get_discover_host_ip(req)
|
|
if host_meta['ip'] in discover_hosts_ip:
|
|
host = self._get_host_by_ip(req, host_meta['ip'])
|
|
if host and host['status'] != 'DISCOVERY_SUCCESSFUL':
|
|
host_info = {}
|
|
host_info['ip'] = host_meta.get('ip', host.get('ip'))
|
|
host_info['passwd'] =\
|
|
host_meta.get('passwd', host.get('passwd'))
|
|
host_info['user'] = \
|
|
host_meta.get('user', host.get('user'))
|
|
host_info['status'] = 'init'
|
|
host_info['message'] = 'None'
|
|
host_meta = \
|
|
registry.update_discover_host_metadata(req.context,
|
|
host['id'],
|
|
host_info)
|
|
return {'host_meta': host_meta}
|
|
else:
|
|
msg = (_("ip %s already existed and this host has "
|
|
"been discovered successfully. "
|
|
% host_meta['ip']))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
self.validate_ip_format(host_meta['ip'])
|
|
|
|
if not host_meta.get('user', None):
|
|
host_meta['user'] = 'root'
|
|
|
|
if not host_meta.get('passwd', None):
|
|
msg = "PASSWD parameter can not be None."
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
if not host_meta.get('status', None):
|
|
host_meta['status'] = 'init'
|
|
|
|
try:
|
|
discover_host_info = \
|
|
registry.add_discover_host_metadata(req.context, host_meta)
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
return {'host_meta': discover_host_info}
|
|
|
|
@utils.mutating
|
|
def delete_discover_host(self, req, id):
|
|
"""
|
|
Deletes a discover host from Daisy.
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:param image_meta: Mapping of metadata about host
|
|
|
|
:raises HTTPBadRequest if x-host-name is missing
|
|
"""
|
|
self._enforce(req, 'delete_discover_host')
|
|
try:
|
|
registry.delete_discover_host_metadata(req.context, id)
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find host to delete: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to delete host: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.InUseByStore as e:
|
|
msg = (_("Host %(id)s could not be deleted because it is in use: "
|
|
"%(exc)s") % {"id": id, "exc": utils.exception_to_str(e)})
|
|
LOG.error(msg)
|
|
raise HTTPConflict(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
# self.notifier.info('host.delete', host)
|
|
return Response(body='', status=200)
|
|
|
|
def detail_discover_host(self, req):
|
|
"""
|
|
Returns detailed information for all available nodes
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:retval The response body is a mapping of the following form::
|
|
|
|
{'nodes': [
|
|
{'id': <ID>,
|
|
'name': <NAME>,
|
|
'description': <DESCRIPTION>,
|
|
'created_at': <TIMESTAMP>,
|
|
'updated_at': <TIMESTAMP>,
|
|
'deleted_at': <TIMESTAMP>|<NONE>,}, ...
|
|
]}
|
|
"""
|
|
|
|
self._enforce(req, 'get_discover_hosts')
|
|
params = self._get_query_params(req)
|
|
try:
|
|
nodes = registry.get_discover_hosts_detail(req.context, **params)
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
|
|
return dict(nodes=nodes)
|
|
|
|
def update_discover_host(self, req, id, host_meta):
|
|
'''
|
|
'''
|
|
self._enforce(req, 'update_discover_host')
|
|
orig_host_meta = registry.get_discover_host_metadata(req.context, id)
|
|
if host_meta.get('ip', None):
|
|
discover_hosts_ip = self._get_discover_host_ip(req)
|
|
if host_meta['ip'] in discover_hosts_ip:
|
|
host_status = host_meta.get('status', orig_host_meta['status'])
|
|
if host_status == 'DISCOVERY_SUCCESSFUL':
|
|
msg = (_("Host with ip %s already has been discovered "
|
|
"successfully, can not change host ip to %s " %
|
|
(orig_host_meta['ip'], host_meta['ip'])))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
self.validate_ip_format(host_meta['ip'])
|
|
if orig_host_meta['ip'] != host_meta.get('ip', None):
|
|
host_meta['status'] = 'init'
|
|
try:
|
|
host_meta = registry.update_discover_host_metadata(req.context,
|
|
id,
|
|
host_meta)
|
|
|
|
except exception.Invalid as e:
|
|
msg = (_("Failed to update host metadata. Got error: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find host to update: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to update host: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except (exception.Conflict, exception.Duplicate) as e:
|
|
LOG.error(utils.exception_to_str(e))
|
|
raise HTTPConflict(body=_('Host operation conflicts'),
|
|
request=req,
|
|
content_type='text/plain')
|
|
else:
|
|
self.notifier.info('host.update', host_meta)
|
|
|
|
return {'host_meta': host_meta}
|
|
|
|
def _get_discover_host_ip(self, req):
|
|
params = {}
|
|
hosts_ip = list()
|
|
discover_hosts =\
|
|
registry.get_discover_hosts_detail(req.context, **params)
|
|
for host in discover_hosts:
|
|
if host.get('ip', None):
|
|
hosts_ip.append(host['ip'])
|
|
return hosts_ip
|
|
|
|
def _get_host_by_ip(self, req, host_ip):
|
|
params = {}
|
|
discover_hosts = \
|
|
registry.get_discover_hosts_detail(req.context, **params)
|
|
LOG.info("%s" % discover_hosts)
|
|
for host in discover_hosts:
|
|
if host.get('ip') == host_ip:
|
|
return host
|
|
return
|
|
|
|
def get_discover_host_detail(self, req, discover_host_id):
|
|
'''
|
|
'''
|
|
try:
|
|
host_meta = registry.get_discover_host_metadata(req.context,
|
|
discover_host_id)
|
|
except exception.Invalid as e:
|
|
msg = (_("Failed to update host metadata. Got error: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find host to update: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to update host: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except (exception.Conflict, exception.Duplicate) as e:
|
|
LOG.error(utils.exception_to_str(e))
|
|
raise HTTPConflict(body=_('Host operation conflicts'),
|
|
request=req,
|
|
content_type='text/plain')
|
|
else:
|
|
self.notifier.info('host.update', host_meta)
|
|
|
|
return {'host_meta': host_meta}
|
|
|
|
def _get_discover_host_mac(self, req):
|
|
params = dict()
|
|
hosts_mac = list()
|
|
discover_hosts =\
|
|
registry.get_discover_hosts_detail(req.context, **params)
|
|
for host in discover_hosts:
|
|
if host.get('mac'):
|
|
hosts_mac.append(host['mac'])
|
|
return hosts_mac
|
|
|
|
def _get_discover_host_by_mac(self, req, host_mac):
|
|
params = dict()
|
|
discover_hosts = \
|
|
registry.get_discover_hosts_detail(req.context, **params)
|
|
LOG.info("%s" % discover_hosts)
|
|
for host in discover_hosts:
|
|
if host.get('mac') == host_mac:
|
|
return host
|
|
return
|
|
|
|
@utils.mutating
|
|
def add_pxe_host(self, req, host_meta):
|
|
"""
|
|
Adds a new pxe host to Daisy
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:param host_meta: Mapping of metadata about host
|
|
|
|
:raises HTTPBadRequest if x-host-name is missing
|
|
"""
|
|
self._enforce(req, 'add_pxe_host')
|
|
LOG.warn("host_meta: %s" % host_meta)
|
|
if not host_meta.get('mac'):
|
|
msg = "MAC parameter can not be None."
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
self.validate_mac_format(host_meta['mac'])
|
|
pxe_hosts_mac = self._get_discover_host_mac(req)
|
|
if host_meta['mac'] in pxe_hosts_mac:
|
|
host = self._get_discover_host_by_mac(req, host_meta['mac'])
|
|
host_meta = registry.update_discover_host_metadata(
|
|
req.context, host['id'], host_meta)
|
|
return {'host_meta': host_meta}
|
|
|
|
if not host_meta.get('status', None):
|
|
host_meta['status'] = 'None'
|
|
|
|
try:
|
|
pxe_host_info = \
|
|
registry.add_discover_host_metadata(req.context, host_meta)
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
return {'host_meta': pxe_host_info}
|
|
|
|
@utils.mutating
|
|
def update_pxe_host(self, req, id, host_meta):
|
|
"""
|
|
Update a new pxe host to Daisy
|
|
"""
|
|
self._enforce(req, 'update_pxe_host')
|
|
if not host_meta.get('mac'):
|
|
msg = "MAC parameter can not be None."
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
self.validate_mac_format(host_meta['mac'])
|
|
orig_host_meta = registry.get_discover_host_metadata(req.context, id)
|
|
try:
|
|
if host_meta['mac'] == orig_host_meta['mac']:
|
|
host_meta = registry.update_discover_host_metadata(
|
|
req.context, id, host_meta)
|
|
|
|
except exception.Invalid as e:
|
|
msg = (_("Failed to update discover host metadata. "
|
|
"Got error: %s") % utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find discover host to update: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to update discover host: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except (exception.Conflict, exception.Duplicate) as e:
|
|
LOG.error(utils.exception_to_str(e))
|
|
raise HTTPConflict(body=_('Host operation conflicts'),
|
|
request=req,
|
|
content_type='text/plain')
|
|
else:
|
|
self.notifier.info('host.update', host_meta)
|
|
|
|
return {'host_meta': host_meta}
|
|
|
|
|
|
class HostDeserializer(wsgi.JSONRequestDeserializer):
|
|
"""Handles deserialization of specific controller method requests."""
|
|
|
|
def _deserialize(self, request):
|
|
result = {}
|
|
result["host_meta"] = utils.get_host_meta(request)
|
|
return result
|
|
|
|
def add_host(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def update_host(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def discover_host(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def update_hwm_host(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def add_discover_host(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def update_discover_host(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def add_pxe_host(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def update_pxe_host(self, request):
|
|
return self._deserialize(request)
|
|
|
|
|
|
class HostSerializer(wsgi.JSONResponseSerializer):
|
|
"""Handles serialization of specific controller method responses."""
|
|
|
|
def __init__(self):
|
|
self.notifier = notifier.Notifier()
|
|
|
|
def add_host(self, response, result):
|
|
host_meta = result['host_meta']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host=host_meta))
|
|
return response
|
|
|
|
def delete_host(self, response, result):
|
|
host_meta = result['host_meta']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host=host_meta))
|
|
return response
|
|
|
|
def get_host(self, response, result):
|
|
host_meta = result['host_meta']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host=host_meta))
|
|
return response
|
|
|
|
def discover_host(self, response, result):
|
|
host_meta = result
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host_meta))
|
|
return response
|
|
|
|
def add_discover_host(self, response, result):
|
|
host_meta = result['host_meta']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host=host_meta))
|
|
return response
|
|
|
|
def update_discover_host(self, response, result):
|
|
host_meta = result['host_meta']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host=host_meta))
|
|
|
|
def get_discover_host_detail(self, response, result):
|
|
host_meta = result['host_meta']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host=host_meta))
|
|
return response
|
|
|
|
def add_pxe_host(self, response, result):
|
|
host_meta = result['host_meta']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host=host_meta))
|
|
return response
|
|
|
|
def update_pxe_host(self, response, result):
|
|
host_meta = result['host_meta']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(host=host_meta))
|
|
|
|
|
|
def create_resource():
|
|
"""Hosts resource factory method"""
|
|
deserializer = HostDeserializer()
|
|
serializer = HostSerializer()
|
|
return wsgi.Resource(Controller(), deserializer, serializer)
|