metalsmith/metalsmith/_network_metadata.py
Harald Jensås f98dfa61c1 Write network_data.json metadata to config-dirve
cloud-init will use fall-back config when network metadata
is *not* present. This works fine if the first NIC on the node is
connected to a network with DHCP. But, when the first NIC is not
used for provisioning, cloud-init will only write a fallback config
for the first NIC. This causes the provisioned node to be unavailable.

Extend instance configuration to include network metadata so that
cloud-init can configure node networking.

Story: 2009238
Task: 43378
Change-Id: I70f1a972a6d5a0398cd348f00308957386d66067
2021-09-22 14:50:02 +02:00

94 lines
3.4 KiB
Python

# Copyright 2021 Red Hat, 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 ipaddress
from openstack import exceptions as sdk_exc
from metalsmith import exceptions
def create_network_metadata(connection, attached_ports):
network_data = {}
if not attached_ports:
return network_data
links = network_data.setdefault('links', [])
networks = network_data.setdefault('networks', [])
services = network_data.setdefault('services', [])
for attached_port in attached_ports:
try:
port = connection.network.get_port(attached_port)
net = connection.network.get_network(port.network_id)
subnets = [connection.network.get_subnet(x['subnet_id'])
for x in port.fixed_ips]
subnets_by_id = {x.id: x for x in subnets}
except sdk_exc.SDKException as exc:
raise exceptions.NetworkResourceNotFound(
'Cannot find network resource: %s' % exc)
metadata_add_links(links, port, net)
metadata_add_services(services, subnets)
for idx, fixed_ip in enumerate(port.fixed_ips):
subnet = subnets_by_id[fixed_ip['subnet_id']]
metadata_add_network(networks, idx, fixed_ip, port, net, subnet)
return network_data
def metadata_add_links(links, port, network):
links.append({'id': port.id,
'type': 'phy',
'mtu': network.mtu,
'ethernet_mac_address': port.mac_address})
def metadata_add_services(services, subnets):
for subnet in subnets:
for dns_nameserver in subnet.dns_nameservers:
services.append({'type': 'dns',
'address': dns_nameserver})
def metadata_add_network(networks, idx, fixed_ip, port, network, subnet):
ip_net = ipaddress.ip_network(subnet.cidr)
net_data = {'id': network.name + str(idx),
'network_id': network.id,
'link': port.id,
'ip_address': fixed_ip['ip_address'],
'netmask': str(ip_net.netmask)}
if subnet.ip_version == 4:
net_data['type'] = 'ipv4_dhcp' if subnet.is_dhcp_enabled else 'ipv4'
elif subnet.ip_version == 6:
net_data['type'] = ('ipv6_{}'.format(subnet.ipv6_address_mode)
if subnet.ipv6_address_mode else 'ipv6')
net_routes = net_data.setdefault('routes', [])
for route in subnet.host_routes:
ip_net = ipaddress.ip_network(route['destination'])
net_routes.append({'network': str(ip_net.network_address),
'netmask': str(ip_net.netmask),
'gateway': route['nexthop']})
# Services go in both "network" and toplevel.
# Ref: https://docs.openstack.org/nova/latest/_downloads/9119ca7ac90aa2990e762c08baea3a36/network_data.json # noqa
net_services = net_data.setdefault('services', [])
metadata_add_services(net_services, [subnet])
networks.append(net_data)