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 <zhou.ya@zte.com.cn>
This commit is contained in:
Zhou Ya 2016-09-20 11:50:12 +08:00
parent 4981d083c4
commit 2079c3a06c
8 changed files with 284 additions and 212 deletions

View File

View File

@ -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

View File

@ -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]

13
code/daisy/daisy/api/v1/install.py Executable file → Normal file
View File

@ -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']

View File

@ -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