fuel-devops/devops/helpers/templates.py

354 lines
12 KiB
Python

# Copyright 2015 Mirantis, Inc.
#
# 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.
import os
import yaml
from devops.helpers.helpers import get_file_size
def yaml_template_load(config_file):
def yaml_include(loader, node):
file_name = os.path.join(os.path.dirname(loader.name), node.value)
if not os.path.isfile(file_name):
raise ValueError(
"Cannot load the environment template {0} : include file {1} "
"doesn't exist.".format(config_file, file_name))
with open(file_name) as inputfile:
return yaml.load(inputfile)
def yaml_get_env_variable(loader, node):
if not node.value.strip():
raise ValueError("Environment variable is required after {tag} in "
"{filename}".format(tag=node.tag,
filename=loader.name))
node_value = node.value.split(',', 1)
# Get the name of environment variable
env_variable = node_value[0].strip()
# Get the default value for environment variable if it exists in config
if len(node_value) > 1:
default_val = node_value[1].strip()
else:
default_val = None
value = os.environ.get(env_variable, default_val)
if value is None:
raise ValueError("Environment variable {var} is not set from shell"
" environment! No default value provided in file "
"{filename}".format(var=env_variable,
filename=loader.name))
return yaml.load(value)
if not os.path.isfile(config_file):
raise ValueError(
"Cannot load the environment template {0} : file "
"doesn't exist.".format(config_file))
yaml.add_constructor("!include", yaml_include)
yaml.add_constructor("!os_env", yaml_get_env_variable)
return yaml.load(open(config_file))
def get_devops_config(filename):
"""Read the YAML file and create a 'full_config' object.
:param filename: path to a file that contains a YAML template
:rtype: dict
"""
import devops
config_file = os.path.join(os.path.dirname(devops.__file__),
'templates', filename)
return yaml_template_load(config_file)
def create_admin_config(admin_vcpu, admin_memory, admin_sysvolume_capacity,
admin_iso_path, boot_from, interfaceorder,
networks_bonding=None,
networks_bondinginterfaces=None):
if networks_bonding:
# Translate a dict of lists {net_name: [eth0, eth1],} into a new dict:
# {eth0: net_name, eth1: net_name,}
ifaces = {
label: iname
for iname in networks_bondinginterfaces
for label in networks_bondinginterfaces[iname]
}
admin_interfaces = [
{
'label': label,
'l2_network_device': ifaces[label]
} for label in sorted(ifaces.keys())
]
else:
admin_interfaces = [
{
'label': 'eth' + str(n),
'l2_network_device': iname
} for n, iname in enumerate(interfaceorder)
]
if boot_from == 'usb':
boot_device_order = ['hd']
iso_device = 'disk'
iso_bus = 'usb'
else: # boot_from == 'cdrom':
boot_device_order = ['hd', 'cdrom']
iso_device = 'cdrom'
iso_bus = 'ide'
admin_config = {
'name': 'admin', # Custom name of VM for Fuel admin node
'role': 'fuel_master', # Fixed role for (Fuel admin) node properties
'params': {
'vcpu': admin_vcpu,
'memory': admin_memory,
'boot': boot_device_order,
'volumes': [
{
'name': 'system',
'capacity': admin_sysvolume_capacity,
'format': 'qcow2',
},
{
'name': 'iso',
'source_image': admin_iso_path,
'capacity': get_file_size(admin_iso_path),
'format': 'raw',
'device': iso_device,
'bus': iso_bus,
},
],
'interfaces': admin_interfaces,
},
}
return admin_config
def create_slave_config(slave_name, slave_role, slave_vcpu, slave_memory,
slave_volume_capacity, interfaceorder,
use_all_disks=None,
multipath_count=0,
networks_multiplenetworks=None,
networks_nodegroups=None,
networks_bonding=None,
networks_bondinginterfaces=None,
enable_bootmenu=True):
if networks_multiplenetworks:
nodegroups_idx = 1 - int(slave_name[-2:]) % 2
slave_interfaces = [
{
'label': 'eth' + str(n),
'l2_network_device': iname
} for n, iname in enumerate(
networks_nodegroups[nodegroups_idx]['pools'])
]
elif networks_bonding:
# Translate a dict of lists {net_name: [eth0, eth1],} into a new dict:
# {eth0: net_name, eth1: net_name,}
ifaces = {
label: iname
for iname in networks_bondinginterfaces
for label in networks_bondinginterfaces[iname]
}
slave_interfaces = [
{
'label': label,
'l2_network_device': ifaces[label]
} for label in sorted(ifaces.keys())
]
else:
slave_interfaces = [
{
'label': 'eth' + str(n),
'l2_network_device': iname
} for n, iname in enumerate(interfaceorder)
]
slave_config = {
'name': slave_name,
'role': slave_role,
'params': {
'vcpu': slave_vcpu,
'memory': slave_memory,
'boot': ['network', 'hd'],
'enable_bootmenu': enable_bootmenu,
'volumes': [
{
'name': 'system',
'capacity': slave_volume_capacity,
'multipath_count': multipath_count,
},
],
'interfaces': slave_interfaces,
},
}
if use_all_disks:
slave_config['params']['volumes'].extend([
{
'name': 'cinder',
'capacity': slave_volume_capacity,
'multipath_count': multipath_count,
},
{
'name': 'swift',
'capacity': slave_volume_capacity,
}
])
return slave_config
def create_address_pools(interfaceorder, networks_pools):
address_pools = {
iname: {
'net': ':'.join(networks_pools[iname])
} for iname in interfaceorder
}
return address_pools
def create_l2_network_devices(interfaceorder,
networks_dhcp,
networks_forwarding):
l2_network_devices = {
iname: {
'address_pool': iname,
'dhcp': networks_dhcp[iname],
'forward': {
'mode': networks_forwarding[iname],
}
} for iname in interfaceorder
}
return l2_network_devices
def create_devops_config(boot_from,
env_name,
admin_vcpu,
admin_memory,
admin_sysvolume_capacity,
admin_iso_path,
nodes_count,
slave_vcpu,
slave_memory,
slave_volume_capacity,
use_all_disks,
multipath_count,
ironic_nodes_count,
networks_bonding,
networks_bondinginterfaces,
networks_multiplenetworks,
networks_nodegroups,
networks_interfaceorder,
networks_pools,
networks_forwarding,
networks_dhcp,
enable_bootmenu):
"""Creates devops config object
This method is used for backward compatibility with old-style
environment creation, where most of environment parameters were
passed with shell environment variables.
See models/environment.py and settings.py for details about
input parameters structure.
"""
# If bonding enabled, then a different interfaces order is provided.
if networks_bonding:
interfaceorder = networks_bondinginterfaces.keys()
else:
interfaceorder = networks_interfaceorder
# Create address pools object
address_pools = create_address_pools(interfaceorder, networks_pools)
# Create network devices object
l2_network_devices = create_l2_network_devices(interfaceorder,
networks_dhcp,
networks_forwarding)
# Create admin and slave nodes
config_nodes = []
admin_config = create_admin_config(
admin_vcpu=admin_vcpu,
admin_memory=admin_memory,
admin_sysvolume_capacity=admin_sysvolume_capacity,
admin_iso_path=admin_iso_path,
boot_from=boot_from,
interfaceorder=interfaceorder,
networks_bonding=networks_bonding,
networks_bondinginterfaces=networks_bondinginterfaces)
config_nodes.append(admin_config)
for slave_n in range(1, nodes_count):
slave_name = 'slave-{0:0>2}'.format(slave_n)
slave_config = create_slave_config(
slave_name=slave_name,
slave_role='fuel_slave',
slave_vcpu=slave_vcpu,
slave_memory=slave_memory,
slave_volume_capacity=slave_volume_capacity,
interfaceorder=interfaceorder,
use_all_disks=use_all_disks,
multipath_count=multipath_count,
networks_multiplenetworks=networks_multiplenetworks,
networks_nodegroups=networks_nodegroups,
networks_bonding=networks_bonding,
networks_bondinginterfaces=networks_bondinginterfaces,
enable_bootmenu=enable_bootmenu)
config_nodes.append(slave_config)
for ironic_n in range(1, ironic_nodes_count + 1):
ironic_name = 'ironic-slave-{0:0>2}'.format(ironic_n)
ironic_config = create_slave_config(
slave_name=ironic_name,
slave_role='ironic_slave',
slave_vcpu=slave_vcpu,
slave_memory=slave_memory,
slave_volume_capacity=slave_volume_capacity,
interfaceorder=['ironic'],
use_all_disks=None,
enable_bootmenu=enable_bootmenu)
config_nodes.append(ironic_config)
config = {
'template':
{
'devops_settings':
{
'env_name': env_name,
'address_pools': address_pools,
'groups': [
{
'name': 'rack-01',
'l2_network_devices': l2_network_devices,
'nodes': config_nodes,
},
]
}
}
}
return config