fuel-web/nailgun/nailgun/orchestrator/nova_serializers.py

429 lines
16 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2013 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.
"""Nova network deployment serializers for orchestrator"""
from collections import defaultdict
from netaddr import IPNetwork
from nailgun import consts
from nailgun.objects import Cluster
from nailgun.objects import Node
from nailgun.orchestrator.base_serializers import NetworkDeploymentSerializer
class NovaNetworkDeploymentSerializer(NetworkDeploymentSerializer):
@classmethod
def network_provider_cluster_attrs(cls, cluster):
return {
'novanetwork_parameters': cls.novanetwork_attrs(cluster),
'dns_nameservers': cluster.network_config.dns_nameservers,
'fixed_network_range': cluster.network_config.fixed_networks_cidr,
'floating_network_range': [
"{0}-{1}".format(ip_range[0], ip_range[1])
for ip_range in cluster.network_config.floating_ranges
]
}
@classmethod
def network_provider_node_attrs(cls, cluster, node):
network_data = node.network_data
interfaces = cls.configure_interfaces(node)
cls.__add_hw_interfaces(interfaces, node.meta['interfaces'])
# Interfaces assignment
attrs = {'network_data': interfaces}
attrs.update(cls.interfaces_list(network_data))
if cluster.network_config.net_manager == 'VlanManager':
attrs.update(cls.add_vlan_interfaces(node))
return attrs
@classmethod
def novanetwork_attrs(cls, cluster):
"""Network configuration"""
attrs = {'network_manager': cluster.network_config.net_manager}
# network_size is required for all managers, otherwise
# puppet will use default (255)
if attrs['network_manager'] == consts.NOVA_NET_MANAGERS.VlanManager:
attrs['num_networks'] = \
cluster.network_config.fixed_networks_amount
attrs['vlan_start'] = \
cluster.network_config.fixed_networks_vlan_start
attrs['network_size'] = cluster.network_config.fixed_network_size
elif (attrs['network_manager'] ==
consts.NOVA_NET_MANAGERS.FlatDHCPManager):
# We need set maximum available size for specific mask for FlatDHCP
# because default 256 caused problem
net_cidr = IPNetwork(cluster.network_config.fixed_networks_cidr)
attrs['network_size'] = net_cidr.size
attrs['num_networks'] = 1
return attrs
@classmethod
def add_vlan_interfaces(cls, node):
"""Assign fixed_interfaces and vlan_interface.
They should be equal.
"""
net_manager = Cluster.get_network_manager(node.cluster)
fixed_interface = net_manager._get_interface_by_network_name(
node, 'fixed')
attrs = {'fixed_interface': fixed_interface.name,
'vlan_interface': fixed_interface.name}
return attrs
@classmethod
def configure_interfaces(cls, node):
"""Configure interfaces"""
network_data = node.network_data
interfaces = {}
for network in network_data:
network_name = network['name']
name = cls.__make_interface_name(network.get('dev'),
network.get('vlan'))
interfaces.setdefault(name, {'interface': name, 'ipaddr': []})
interface = interfaces[name]
if network.get('ip'):
interface['ipaddr'].append(network.get('ip'))
if network_name == 'fuelweb_admin':
admin_ip_addr = cls.get_admin_ip_w_prefix(node)
interface['ipaddr'].append(admin_ip_addr)
elif network_name == 'public' and network.get('gateway'):
interface['gateway'] = network['gateway']
interface['default_gateway'] = True
for if_name, if_data in interfaces.iteritems():
if len(if_data['ipaddr']) == 0:
if_data['ipaddr'] = 'none'
interfaces['lo'] = {'interface': 'lo', 'ipaddr': ['127.0.0.1/8']}
return interfaces
@classmethod
def __make_interface_name(cls, name, vlan):
"""Make interface name"""
if name and vlan:
return '.'.join([name, str(vlan)])
return name
@classmethod
def __add_hw_interfaces(cls, interfaces, hw_interfaces):
"""Add hardware interfaces
Add interfaces which not represents in
interfaces list but they are represented on node
"""
for hw_interface in hw_interfaces:
if hw_interface['name'] not in interfaces:
interfaces[hw_interface['name']] = {
'interface': hw_interface['name'],
'ipaddr': "none"
}
@classmethod
def interfaces_list(cls, network_data):
"""Generate list of interfaces"""
interfaces = {}
for network in network_data:
if_name = cls.__make_interface_name(
network.get('dev'),
network.get('vlan'))
interfaces['%s_interface' % network['name']] = if_name
if network['name'] == 'public':
interfaces['floating_interface'] = if_name
return interfaces
class NovaNetworkDeploymentSerializer61(NovaNetworkDeploymentSerializer):
@classmethod
def network_provider_node_attrs(cls, cluster, node):
nm = Cluster.get_network_manager(cluster)
networks = nm.get_node_networks(node)
return {'network_scheme': cls.generate_network_scheme(node, networks)}
@classmethod
def subiface_name(cls, iface_name, net_descr):
if not net_descr['vlan_id']:
return iface_name
else:
return "{0}.{1}".format(iface_name, net_descr['vlan_id'])
@classmethod
def generate_transformations(cls, node, nm, nets_by_ifaces,
fixed_sub_iface):
iface_types = consts.NETWORK_INTERFACE_TYPES
brnames = ['br-fw-admin', 'br-storage', 'br-mgmt', 'br-ex']
transformations = []
# add bridges for network roles
for brname in brnames:
transformations.append(cls.add_bridge(brname))
# fill up ports and bonds
for iface in node.interfaces:
if iface.type == iface_types.ether:
# add ports for all networks on every unbonded NIC
if not iface.bond and iface.name in nets_by_ifaces:
tagged = []
for net in nets_by_ifaces[iface.name]:
sub_iface = cls.subiface_name(iface.name, net)
# Interface must go prior to subinterfaces.
if not net['vlan_id']:
transformations.append(
cls.add_port(sub_iface, net['br_name']))
else:
tagged.append(
cls.add_port(sub_iface, net['br_name']))
# we should avoid adding the port twice in case of
# VlanManager
if fixed_sub_iface == sub_iface:
fixed_sub_iface = None
transformations.extend(tagged)
elif iface.type == iface_types.bond:
# Add bonds and connect untagged networks' bridges to them.
# There can be no more than one untagged network on each bond.
bond_params = {
'bond_properties': nm.get_lnx_bond_properties(iface),
'interface_properties': nm.get_iface_properties(iface)
}
bond_ports = []
if iface.name in nets_by_ifaces:
for net in nets_by_ifaces[iface.name]:
if net['vlan_id']:
bond_ports.append(cls.add_port(
cls.subiface_name(iface.name, net),
net['br_name']))
else:
bond_params['bridge'] = net['br_name']
transformations.append(cls.add_bond(iface, bond_params))
transformations.extend(bond_ports)
# we should avoid adding the port twice in case of
# VlanManager
if fixed_sub_iface == iface.name:
fixed_sub_iface = None
# add manager-related ports
if fixed_sub_iface:
transformations.append(cls.add_port(fixed_sub_iface, None))
return transformations
@classmethod
def generate_network_scheme(cls, node, networks):
# create network scheme structure and fill it with static values
attrs = {
'version': '1.1',
'provider': 'lnx',
'interfaces': {},
'endpoints': {},
'roles': {
'fw-admin': 'br-fw-admin',
'storage': 'br-storage',
'management': 'br-mgmt',
'ex': 'br-ex',
},
}
netgroup_mapping = [
('fuelweb_admin', 'br-fw-admin'),
('storage', 'br-storage'),
('management', 'br-mgmt'),
('public', 'br-ex'),
('fixed', '') # will be determined in code below
]
nm = Cluster.get_network_manager(node.cluster)
# populate IP address information to endpoints
netgroups = {}
nets_by_ifaces = defaultdict(list)
fixed_sub_iface = None
for ngname, brname in netgroup_mapping:
# Here we get a dict with network description for this particular
# node with its assigned IPs and device names for each network.
netgroup = nm.get_network_by_netname(ngname, networks)
if ngname == 'fixed':
vlan_id = None
if node.cluster.network_config.net_manager == \
consts.NOVA_NET_MANAGERS.FlatDHCPManager:
vlan_id = \
node.cluster.network_config.fixed_networks_vlan_start
net = {'vlan_id': vlan_id}
fixed_sub_iface = cls.subiface_name(netgroup['dev'], net)
attrs['endpoints'][fixed_sub_iface] = {'IP': 'none'}
else:
nets_by_ifaces[netgroup['dev']].append({
'br_name': brname,
'vlan_id': netgroup['vlan']
})
if netgroup.get('ip'):
attrs['endpoints'][brname] = {'IP': [netgroup['ip']]}
netgroups[ngname] = netgroup
attrs['endpoints']['br-ex']['gateway'] = \
netgroups['public']['gateway']
# add manager-related roles
if node.cluster.network_config.net_manager == \
consts.NOVA_NET_MANAGERS.VlanManager:
attrs['roles']['novanetwork/vlan'] = fixed_sub_iface
else:
attrs['roles']['novanetwork/fixed'] = fixed_sub_iface
for iface in node.nic_interfaces:
if iface.bond:
attrs['interfaces'][iface.name] = {}
else:
attrs['interfaces'][iface.name] = \
nm.get_iface_properties(iface)
attrs['transformations'] = \
cls.generate_transformations(node, nm, nets_by_ifaces,
fixed_sub_iface)
return attrs
class NovaNetworkDeploymentSerializer70(NovaNetworkDeploymentSerializer61):
@classmethod
def network_provider_node_attrs(cls, cluster, node):
"""Serialize node, then it will be merged with common attributes"""
node_attrs = super(NovaNetworkDeploymentSerializer70,
cls).network_provider_node_attrs(cluster, node)
node_attrs['network_metadata'] = cls.generate_network_metadata(cluster)
return node_attrs
@classmethod
def generate_network_scheme(cls, node, networks):
attrs = super(NovaNetworkDeploymentSerializer70,
cls).generate_network_scheme(node, networks)
attrs['roles']['mgmt/corosync'] = 'br-mgmt'
attrs['roles']['mgmt/database'] = 'br-mgmt'
attrs['roles']['mgmt/messaging'] = 'br-mgmt'
attrs['roles']['mgmt/memcache'] = 'br-mgmt'
attrs['roles']['mgmt/vip'] = 'br-mgmt'
attrs['roles']['nova/api'] = 'br-mgmt'
attrs['roles']['murano/api'] = 'br-mgmt'
attrs['roles']['sahara/api'] = 'br-mgmt'
attrs['roles']['ceilometer/api'] = 'br-mgmt'
attrs['roles']['heat/api'] = 'br-mgmt'
attrs['roles']['keystone/api'] = 'br-mgmt'
attrs['roles']['horizon'] = 'br-mgmt'
attrs['roles']['glance/api'] = 'br-mgmt'
attrs['roles']['public/vip'] = 'br-ex'
attrs['roles']['ceph/radosgw'] = 'br-ex'
attrs['roles']['admin/pxe'] = 'br-fw-admin'
attrs['roles']['ceph/replication'] = 'br-storage'
attrs['roles']['ceph/public'] = 'br-mgmt'
attrs['roles']['swift/replication'] = 'br-storage'
attrs['roles']['swift/api'] = 'br-mgmt'
attrs['roles']['cinder/iscsi'] = 'br-storage'
attrs['roles']['cinder/api'] = 'br-mgmt'
attrs['roles']['mongo/db'] = 'br-mgmt'
return attrs
@classmethod
def generate_network_metadata(cls, cluster):
nodes = dict()
nm = Cluster.get_network_manager(cluster)
for n in Cluster.get_nodes_not_for_deletion(cluster):
name = Node.get_slave_name(n)
node_roles = Node.all_roles(n)
ip_by_net = {
'fuelweb_admin': None,
'storage': None,
'management': None,
'public': None
}
networks = nm.get_node_networks(n)
for net in ip_by_net:
netgroup = nm.get_network_by_netname(net, networks)
if netgroup.get('ip'):
ip_by_net[net] = netgroup['ip'].split('/')[0]
netw_roles = {
'admin/pxe': ip_by_net['fuelweb_admin'],
'fw-admin': ip_by_net['fuelweb_admin'],
'keystone/api': ip_by_net['management'],
'swift/api': ip_by_net['management'],
'sahara/api': ip_by_net['management'],
'ceilometer/api': ip_by_net['management'],
'cinder/api': ip_by_net['management'],
'glance/api': ip_by_net['management'],
'heat/api': ip_by_net['management'],
'nova/api': ip_by_net['management'],
'murano/api': ip_by_net['management'],
'horizon': ip_by_net['management'],
'management': ip_by_net['management'],
'mgmt/database': ip_by_net['management'],
'mgmt/messaging': ip_by_net['management'],
'mgmt/corosync': ip_by_net['management'],
'mgmt/memcache': ip_by_net['management'],
'mgmt/vip': ip_by_net['management'],
'mongo/db': ip_by_net['management'],
'ceph/public': ip_by_net['management'],
'storage': ip_by_net['storage'],
'ceph/replication': ip_by_net['storage'],
'swift/replication': ip_by_net['storage'],
'cinder/iscsi': ip_by_net['storage'],
'ex': ip_by_net['public'],
'public/vip': ip_by_net['public'],
'ceph/radosgw': ip_by_net['public'],
}
nodes[name] = {
"uid": n.uid,
"fqdn": Node.get_node_fqdn(n),
"name": name,
"user_node_name": n.name,
"swift_zone": n.uid,
"node_roles": node_roles,
"network_roles": netw_roles
}
return dict(
nodes=nodes,
vips=nm.assign_vips_for_net_groups(cluster)
)