From 2079c3a06c9ee487190fc7d7141c9777f3a10426 Mon Sep 17 00:00:00 2001 From: Zhou Ya Date: Tue, 20 Sep 2016 11:50:12 +0800 Subject: [PATCH] refactor the class of os installation abstract the installation of os prepare for the new type of installation Change-Id: I8565f5ae8dce5c788e01dede98f12de8be7c018f Signed-off-by: Zhou Ya --- code/daisy/daisy/api/backends/os/__init__.py | 0 .../daisy/api/backends/os/bifrost/__init__.py | 0 .../daisy/api/backends/os/bifrost/install.py | 0 code/daisy/daisy/api/backends/os/osdriver.py | 194 ++++++++++++ .../daisy/api/backends/os/pxe/__init__.py | 0 .../api/backends/{os.py => os/pxe/install.py} | 277 +++++------------- code/daisy/daisy/api/v1/install.py | 13 +- tools/daisy-utils/daisy.conf | 12 +- 8 files changed, 284 insertions(+), 212 deletions(-) create mode 100755 code/daisy/daisy/api/backends/os/__init__.py create mode 100755 code/daisy/daisy/api/backends/os/bifrost/__init__.py create mode 100644 code/daisy/daisy/api/backends/os/bifrost/install.py create mode 100644 code/daisy/daisy/api/backends/os/osdriver.py create mode 100755 code/daisy/daisy/api/backends/os/pxe/__init__.py rename code/daisy/daisy/api/backends/{os.py => os/pxe/install.py} (66%) mode change 100755 => 100644 code/daisy/daisy/api/v1/install.py diff --git a/code/daisy/daisy/api/backends/os/__init__.py b/code/daisy/daisy/api/backends/os/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/code/daisy/daisy/api/backends/os/bifrost/__init__.py b/code/daisy/daisy/api/backends/os/bifrost/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/code/daisy/daisy/api/backends/os/bifrost/install.py b/code/daisy/daisy/api/backends/os/bifrost/install.py new file mode 100644 index 00000000..e69de29b diff --git a/code/daisy/daisy/api/backends/os/osdriver.py b/code/daisy/daisy/api/backends/os/osdriver.py new file mode 100644 index 00000000..f1102cf8 --- /dev/null +++ b/code/daisy/daisy/api/backends/os/osdriver.py @@ -0,0 +1,194 @@ +# Copyright 2011 Justin Santa Barbara +# 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. + +""" +OS_Driver base-classes: + + (Beginning of) the contract that os installation drivers must follow, + and shared types that support that contract +""" +import copy +from oslo_log import log as logging +from webob.exc import HTTPBadRequest +from daisy import i18n +from daisy.common import exception +from daisy.api import common +import daisy.registry.client.v1.api as registry +import daisy.api.backends.common as daisy_cmn +from oslo_utils import importutils + +_ = i18n._ +_LE = i18n._LE +_LI = i18n._LI +_LW = i18n._LW +LOG = logging.getLogger(__name__) + +host_os_status = { + 'INIT': 'init', + 'PRE_INSTALL': 'pre-install', + 'INSTALLING': 'installing', + 'ACTIVE': 'active', + 'INSTALL_FAILED': 'install-failed', + 'UPDATING': 'updating', + 'UPDATE_FAILED': 'update-failed' +} + +LINUX_BOND_MODE = {'balance-rr': '0', 'active-backup': '1', + 'balance-xor': '2', 'broadcast': '3', + '802.3ad': '4', 'balance-tlb': '5', + 'balance-alb': '6'} + + +def _get_network_plat(req, host_config, cluster_networks, dhcp_mac): + host_config['dhcp_mac'] = dhcp_mac + if host_config['interfaces']: + count = 0 + host_config_orig = copy.deepcopy(host_config) + for interface in host_config['interfaces']: + count += 1 + # if (interface.has_key('assigned_networks') and + if ('assigned_networks' in interface and + interface['assigned_networks']): + assigned_networks = copy.deepcopy( + interface['assigned_networks']) + host_config['interfaces'][count - 1]['assigned_networks'] = [] + alias = [] + for assigned_network in assigned_networks: + network_name = assigned_network['name'] + cluster_network = [ + network for network in cluster_networks + if network['name'] in network_name][0] + alias.append(cluster_network['alias']) + # convert cidr to netmask + cidr_to_ip = "" + assigned_networks_ip = daisy_cmn.get_host_network_ip( + req, host_config_orig, cluster_networks, network_name) + if cluster_network.get('cidr', None): + inter_ip = lambda x: '.'.join( + [str(x / (256**i) % 256) for i in + range(3, -1, -1)]) + cidr_to_ip = inter_ip( + 2**32 - 2**(32 - int( + cluster_network['cidr'].split('/')[1]))) + if cluster_network['alias'] is None or len(alias) == 1: + network_type = cluster_network['network_type'] + network_plat = dict(network_type=network_type, + ml2_type=cluster_network[ + 'ml2_type'], + capability=cluster_network[ + 'capability'], + physnet_name=cluster_network[ + 'physnet_name'], + gateway=cluster_network.get( + 'gateway', ""), + ip=assigned_networks_ip, + # ip=cluster_network.get('ip', ""), + netmask=cidr_to_ip, + vlan_id=cluster_network.get( + 'vlan_id', "")) + host_config['interfaces'][ + count - 1][ + 'assigned_networks'].append(network_plat) + interface['ip'] = "" + interface['netmask'] = "" + interface['gateway'] = "" + + return host_config + + +def get_cluster_hosts_config(req, cluster_id): + # params = dict(limit=1000000) + try: + cluster_data = registry.get_cluster_metadata(req.context, cluster_id) + networks = registry.get_networks_detail(req.context, cluster_id) + all_roles = registry.get_roles_detail(req.context) + except exception.Invalid as e: + raise HTTPBadRequest(explanation=e.msg, request=req) + + roles = [role for role in all_roles if role['cluster_id'] == cluster_id] + all_hosts_ids = cluster_data['nodes'] + hosts_config = [] + for host_id in all_hosts_ids: + host_detail = daisy_cmn.get_host_detail(req, host_id) + role_host_db_lv_size_lists = list() + # if host_detail.has_key('role') and host_detail['role']: + if 'role' in host_detail and host_detail['role']: + host_roles = host_detail['role'] + for role in roles: + if role['name'] in host_detail['role'] and\ + role['glance_lv_size']: + host_detail['glance_lv_size'] = role['glance_lv_size'] + if role.get('db_lv_size', None) and host_roles and\ + role['name'] in host_roles: + role_host_db_lv_size_lists.append(role['db_lv_size']) + if role['name'] == 'COMPUTER' and\ + role['name'] in host_detail['role'] and\ + role['nova_lv_size']: + host_detail['nova_lv_size'] = role['nova_lv_size'] + service_disks = daisy_cmn.get_service_disk_list( + req, {'role_id': role['id']}) + for service_disk in service_disks: + if service_disk['disk_location'] == 'local' and\ + service_disk['service'] == 'mongodb': + host_detail['mongodb_lv_size'] = service_disk['size'] + break + if role_host_db_lv_size_lists: + host_detail['db_lv_size'] = max(role_host_db_lv_size_lists) + else: + host_detail['db_lv_size'] = 0 + + for interface in host_detail['interfaces']: + if interface['type'] == 'bond'and\ + interface['mode'] in LINUX_BOND_MODE.keys(): + interface['mode'] = LINUX_BOND_MODE[interface['mode']] + + if (host_detail['os_status'] == host_os_status['INIT'] or + host_detail['os_status'] == host_os_status['PRE_INSTALL'] or + host_detail['os_status'] == host_os_status['INSTALLING'] or + host_detail['os_status'] == host_os_status['INSTALL_FAILED']): + pxe_macs = common.get_pxe_mac(host_detail) + if not pxe_macs: + msg = "cann't find dhcp interface on host %s" % host_detail[ + 'id'] + raise exception.InvalidNetworkConfig(msg) + if len(pxe_macs) > 1: + msg = "dhcp interface should only has one on host %s"\ + % host_detail['id'] + raise exception.InvalidNetworkConfig(msg) + + host_config_detail = copy.deepcopy(host_detail) + host_config = _get_network_plat(req, host_config_detail, + networks, + pxe_macs[0]) + hosts_config.append(daisy_cmn.sort_interfaces_by_pci(networks, + host_config)) + return hosts_config + + +def load_install_os_driver(os_install_type): + """ Load a operating system installation driver. + """ + os_installation_driver = "%s.install.OSInstall" % os_install_type + + LOG.info(_("Loading os driver '%s'") % os_installation_driver) + try: + driver = importutils.import_object_ns( + 'daisy.api.backends.os', os_installation_driver) + return driver + except ImportError: + LOG.exception( + _("Error, unable to load the os driver '%s'" + % os_installation_driver)) + return None diff --git a/code/daisy/daisy/api/backends/os/pxe/__init__.py b/code/daisy/daisy/api/backends/os/pxe/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/code/daisy/daisy/api/backends/os.py b/code/daisy/daisy/api/backends/os/pxe/install.py similarity index 66% rename from code/daisy/daisy/api/backends/os.py rename to code/daisy/daisy/api/backends/os/pxe/install.py index ca716483..6dda76ac 100644 --- a/code/daisy/daisy/api/backends/os.py +++ b/code/daisy/daisy/api/backends/os/pxe/install.py @@ -28,7 +28,6 @@ from webob.exc import HTTPBadRequest from daisy import i18n from daisy.common import exception -from daisy.api import common from daisy.common import utils import daisy.registry.client.v1.api as registry import daisy.api.backends.common as daisy_cmn @@ -62,210 +61,6 @@ host_os_status = { 'UPDATE_FAILED': 'update-failed' } -LINUX_BOND_MODE = {'balance-rr': '0', 'active-backup': '1', - 'balance-xor': '2', 'broadcast': '3', - '802.3ad': '4', 'balance-tlb': '5', - 'balance-alb': '6'} - - -def pxe_server_build(req, install_meta): - params = {'filters': {'type': 'system'}} - try: - networks = registry.get_all_networks(req.context, **params) - except exception.Invalid as e: - raise HTTPBadRequest(explanation=e.msg, request=req) - - ip_inter = lambda x: sum([256 ** j * int(i) - for j, i in enumerate(x.split('.')[::-1])]) - inter_ip = lambda x: '.'.join( - [str(x / (256**i) % 256) for i in range(3, -1, -1)]) - for network in networks: - if 'system' in network['type']: - network_cidr = network.get('cidr') - if not network_cidr: - msg = "Error:The CIDR is blank of pxe server!" - LOG.error(msg) - raise exception.Forbidden(msg) - cidr_end = network_cidr.split('/')[1] - mask = ~(2**(32 - int(cidr_end)) - 1) - net_mask = inter_ip(mask) - pxe_server_ip = network.get('ip') - ip_ranges = network.get('ip_ranges') - for ip_range in ip_ranges: - client_ip_begin = ip_range.get('start') - client_ip_end = ip_range.get('end') - ip_addr = network_cidr.split('/')[0] - ip_addr_int = ip_inter(ip_addr) - ip_addr_min = inter_ip(ip_addr_int & (mask & 0xffffffff)) - ip_addr_max = inter_ip(ip_addr_int | (~mask & 0xffffffff)) - if not client_ip_begin and not client_ip_end: - client_ip_begin = inter_ip((ip_inter(ip_addr_min)) + 2) - client_ip_end = ip_addr_max - if pxe_server_ip: - ip_in_cidr = utils.is_ip_in_cidr(pxe_server_ip, - network_cidr) - if not ip_in_cidr: - msg = "Error:The ip '%s' is not in cidr '%s'" \ - " range." % (pxe_server_ip, network_cidr) - LOG.error(msg) - raise HTTPBadRequest(explanation=msg) - else: - pxe_server_ip = inter_ip((ip_inter(ip_addr_min)) + 1) - - eth_name = install_meta.get('deployment_interface') - if not eth_name: - msg = "Error:The nic name is blank of build pxe server!" - LOG.error(msg) - raise exception.Forbidden(msg) - args = {'build_pxe': 'yes', - 'ethname_l': eth_name, - 'ip_addr_l': pxe_server_ip, - 'net_mask_l': net_mask, - 'client_ip_begin': client_ip_begin, - 'client_ip_end': client_ip_end} - with open('/var/log/ironic/pxe.json', 'w') as f: - json.dump(args, f, indent=2) - f.close() - try: - _PIPE = subprocess.PIPE - cmd = "/usr/bin/pxe_server_install /var/log/ironic/pxe.json && \ - chmod 755 /tftpboot -R" - obj = subprocess.Popen(cmd, - shell=True, - stdout=_PIPE, - stderr=_PIPE) - out, error = obj.communicate() - except Exception as ex: - LOG.error("%s: execute set pxe command failed.", ex) - msg = "build pxe server failed" - raise exception.InvalidNetworkConfig(msg) - - -def _get_network_plat(req, host_config, cluster_networks, dhcp_mac): - host_config['dhcp_mac'] = dhcp_mac - if host_config['interfaces']: - count = 0 - host_config_orig = copy.deepcopy(host_config) - for interface in host_config['interfaces']: - count += 1 - # if (interface.has_key('assigned_networks') and - if ('assigned_networks' in interface and - interface['assigned_networks']): - assigned_networks = copy.deepcopy( - interface['assigned_networks']) - host_config['interfaces'][count - 1]['assigned_networks'] = [] - alias = [] - for assigned_network in assigned_networks: - network_name = assigned_network['name'] - cluster_network = [ - network for network in cluster_networks - if network['name'] in network_name][0] - alias.append(cluster_network['alias']) - # convert cidr to netmask - cidr_to_ip = "" - assigned_networks_ip = daisy_cmn.get_host_network_ip( - req, host_config_orig, cluster_networks, network_name) - if cluster_network.get('cidr', None): - inter_ip = lambda x: '.'.join( - [str(x / (256**i) % 256) for i in - range(3, -1, -1)]) - cidr_to_ip = inter_ip( - 2**32 - 2**(32 - int( - cluster_network['cidr'].split('/')[1]))) - if cluster_network['alias'] is None or len(alias) == 1: - network_type = cluster_network['network_type'] - network_plat = dict(network_type=network_type, - ml2_type=cluster_network[ - 'ml2_type'], - capability=cluster_network[ - 'capability'], - physnet_name=cluster_network[ - 'physnet_name'], - gateway=cluster_network.get( - 'gateway', ""), - ip=assigned_networks_ip, - # ip=cluster_network.get('ip', ""), - netmask=cidr_to_ip, - vlan_id=cluster_network.get( - 'vlan_id', "")) - host_config['interfaces'][ - count - 1][ - 'assigned_networks'].append(network_plat) - interface['ip'] = "" - interface['netmask'] = "" - interface['gateway'] = "" - - return host_config - - -def get_cluster_hosts_config(req, cluster_id): - # params = dict(limit=1000000) - try: - cluster_data = registry.get_cluster_metadata(req.context, cluster_id) - networks = registry.get_networks_detail(req.context, cluster_id) - all_roles = registry.get_roles_detail(req.context) - except exception.Invalid as e: - raise HTTPBadRequest(explanation=e.msg, request=req) - - roles = [role for role in all_roles if role['cluster_id'] == cluster_id] - all_hosts_ids = cluster_data['nodes'] - hosts_config = [] - for host_id in all_hosts_ids: - host_detail = daisy_cmn.get_host_detail(req, host_id) - role_host_db_lv_size_lists = list() - # if host_detail.has_key('role') and host_detail['role']: - if 'role' in host_detail and host_detail['role']: - host_roles = host_detail['role'] - for role in roles: - if role['name'] in host_detail['role'] and\ - role['glance_lv_size']: - host_detail['glance_lv_size'] = role['glance_lv_size'] - if role.get('db_lv_size', None) and host_roles and\ - role['name'] in host_roles: - role_host_db_lv_size_lists.append(role['db_lv_size']) - if role['name'] == 'COMPUTER' and\ - role['name'] in host_detail['role'] and\ - role['nova_lv_size']: - host_detail['nova_lv_size'] = role['nova_lv_size'] - service_disks = daisy_cmn.get_service_disk_list( - req, {'role_id': role['id']}) - for service_disk in service_disks: - if service_disk['disk_location'] == 'local' and\ - service_disk['service'] == 'mongodb': - host_detail['mongodb_lv_size'] = service_disk['size'] - break - if role_host_db_lv_size_lists: - host_detail['db_lv_size'] = max(role_host_db_lv_size_lists) - else: - host_detail['db_lv_size'] = 0 - - for interface in host_detail['interfaces']: - if interface['type'] == 'bond'and\ - interface['mode'] in LINUX_BOND_MODE.keys(): - interface['mode'] = LINUX_BOND_MODE[interface['mode']] - - if (host_detail['os_status'] == host_os_status['INIT'] or - host_detail['os_status'] == host_os_status['PRE_INSTALL'] or - host_detail['os_status'] == host_os_status['INSTALLING'] or - host_detail['os_status'] == host_os_status['INSTALL_FAILED']): - pxe_macs = common.get_pxe_mac(host_detail) - if not pxe_macs: - msg = "cann't find dhcp interface on host %s" % host_detail[ - 'id'] - raise exception.InvalidNetworkConfig(msg) - if len(pxe_macs) > 1: - msg = "dhcp interface should only has one on host %s"\ - % host_detail['id'] - raise exception.InvalidNetworkConfig(msg) - - host_config_detail = copy.deepcopy(host_detail) - host_config = _get_network_plat(req, host_config_detail, - networks, - pxe_macs[0]) - hosts_config.append(daisy_cmn.sort_interfaces_by_pci(networks, - host_config)) - return hosts_config - def update_db_host_status(req, host_id, host_status): """ @@ -642,6 +437,78 @@ class OSInstall(): time.sleep(self.time_step) return hosts_install_status + def pxe_server_build(req, install_meta): + params = {'filters': {'type': 'system'}} + try: + networks = registry.get_all_networks(req.context, **params) + except exception.Invalid as e: + raise HTTPBadRequest(explanation=e.msg, request=req) + + ip_inter = lambda x: sum([256 ** j * int(i) + for j, i in enumerate(x.split('.')[::-1])]) + inter_ip = lambda x: '.'.join( + [str(x / (256**i) % 256) for i in range(3, -1, -1)]) + for network in networks: + if 'system' in network['type']: + network_cidr = network.get('cidr') + if not network_cidr: + msg = "Error:The CIDR is blank of pxe server!" + LOG.error(msg) + raise exception.Forbidden(msg) + cidr_end = network_cidr.split('/')[1] + mask = ~(2**(32 - int(cidr_end)) - 1) + net_mask = inter_ip(mask) + pxe_server_ip = network.get('ip') + ip_ranges = network.get('ip_ranges') + for ip_range in ip_ranges: + client_ip_begin = ip_range.get('start') + client_ip_end = ip_range.get('end') + ip_addr = network_cidr.split('/')[0] + ip_addr_int = ip_inter(ip_addr) + ip_addr_min = inter_ip(ip_addr_int & (mask & 0xffffffff)) + ip_addr_max = inter_ip(ip_addr_int | (~mask & 0xffffffff)) + if not client_ip_begin and not client_ip_end: + client_ip_begin = inter_ip((ip_inter(ip_addr_min)) + 2) + client_ip_end = ip_addr_max + if pxe_server_ip: + ip_in_cidr = utils.is_ip_in_cidr(pxe_server_ip, + network_cidr) + if not ip_in_cidr: + msg = "Error:The ip '%s' is not in cidr '%s'" \ + " range." % (pxe_server_ip, network_cidr) + LOG.error(msg) + raise HTTPBadRequest(explanation=msg) + else: + pxe_server_ip = inter_ip((ip_inter(ip_addr_min)) + 1) + + eth_name = install_meta.get('deployment_interface') + if not eth_name: + msg = "Error:The nic name is blank of build pxe server!" + LOG.error(msg) + raise exception.Forbidden(msg) + args = {'build_pxe': 'yes', + 'ethname_l': eth_name, + 'ip_addr_l': pxe_server_ip, + 'net_mask_l': net_mask, + 'client_ip_begin': client_ip_begin, + 'client_ip_end': client_ip_end} + with open('/var/log/ironic/pxe.json', 'w') as f: + json.dump(args, f, indent=2) + f.close() + try: + _PIPE = subprocess.PIPE + cmd = "/usr/bin/pxe_server_install /var/log/ironic/pxe.json && \ + chmod 755 /tftpboot -R" + obj = subprocess.Popen(cmd, + shell=True, + stdout=_PIPE, + stderr=_PIPE) + out, error = obj.communicate() + except Exception as ex: + LOG.error("%s: execute set pxe command failed.", ex) + msg = "build pxe server failed" + raise exception.InvalidNetworkConfig(msg) + def install_os(self, hosts_detail, role_hosts_ids): if len(hosts_detail) > self.max_parallel_os_num: install_hosts = hosts_detail[:self.max_parallel_os_num] diff --git a/code/daisy/daisy/api/v1/install.py b/code/daisy/daisy/api/v1/install.py old mode 100755 new mode 100644 index d9a6a6ba..5dbfa956 --- a/code/daisy/daisy/api/v1/install.py +++ b/code/daisy/daisy/api/v1/install.py @@ -39,7 +39,8 @@ from daisy.api.v1 import controller from daisy.api.v1 import filters import daisy.api.backends.common as daisy_cmn from daisy.api.backends import driver -from daisy.api.backends import os as os_handle +from daisy.api.backends.os import osdriver as os_handle +import ConfigParser LOG = logging.getLogger(__name__) @@ -58,6 +59,11 @@ BACKENDS_INSTALL_ORDER = ['proton', 'zenic', 'tecs', 'kolla'] BACKENDS_UPGRADE_ORDER = ['proton', 'zenic', 'tecs', 'kolla'] BACKENDS_UNINSTALL_ORDER = [] +config = ConfigParser.ConfigParser() +config.read(daisy_cmn.daisy_conf_file) +OS_INSTALL_TYPE = 'pxe' +OS_INSTALL_TYPE = config.get("OS", "os_install_type") + def get_deployment_backends(req, cluster_id, backends_order): cluster_roles = daisy_cmn.get_cluster_roles_detail(req, cluster_id) @@ -139,9 +145,10 @@ class InstallTask(object): order_hosts_need_os = hosts_with_role_need_os + \ hosts_without_role_need_os while order_hosts_need_os: - os_install = os_handle.OSInstall(self.req, self.cluster_id) # all os will be installed batch by batch with # max_parallel_os_number which was set in daisy-api.conf + os_driver = os_handle.load_install_os_driver(OS_INSTALL_TYPE) + os_install = os_driver(self.req, self.cluster_id) (order_hosts_need_os, role_hosts_need_os) = os_install.install_os( order_hosts_need_os, role_hosts_need_os) # after a batch of os install over, judge if all @@ -240,7 +247,7 @@ class Controller(controller.BaseController): :raises HTTPBadRequest if x-install-cluster is missing """ if 'deployment_interface' in install_meta: - os_handle.pxe_server_build(req, install_meta) + os_install.pxe_server_build(req, install_meta) return {"status": "pxe is installed"} cluster_id = install_meta['cluster_id'] diff --git a/tools/daisy-utils/daisy.conf b/tools/daisy-utils/daisy.conf index 415af6c0..da3337ef 100755 --- a/tools/daisy-utils/daisy.conf +++ b/tools/daisy-utils/daisy.conf @@ -4,12 +4,16 @@ daisy_management_ip= [BACKEND] -#Default backend types of daisy, including tecs, zenic, proton. -#If you want to create a cluster with more than one backend, +#Default backend types of daisy, including tecs, zenic, proton, kolla. +#If you want to create a cluster with more than one backend, #all backend names should be provided for this configuration item, -#such as, default_backend_types=tecs,zenic,proton. +#such as, default_backend_types=tecs,zenic,proton,kolla. default_backend_types=tecs +[OS] +#Default os install types of daisy +os_install_type=pxe + [PXE] #Set to 'yes' if you want to build a PXE server, otherwise to 'no'. build_pxe=no @@ -17,7 +21,7 @@ build_pxe=no #the nic name, to build a PXE server on this nic. eth_name= -#The ip value of PXE server +#The ip value of PXE server ip_address=99.99.1.5 #the net mask of PXE server