Roman Dobosz ba4cc2b8f0 Use either subnet name or id for Machines.
Currently, we support only subnet id for primarySubnet field for
OpenShift Machines. Even though it's possible to create objects in
OpenStack with the same name, it is more natural to use names instead of
id especially in OpenShift world. In this patch we introduce support
for using names as well.

Change-Id: Ib21646b4b7cf0e3c07ddef15b3569a0fb4539e8a
2022-12-19 07:57:32 +00:00

180 lines
7.2 KiB
Python

# Copyright 2020 Red Hat, Inc.
# 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.
from openstack import exceptions as os_exc
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
from kuryr_kubernetes import clients
from kuryr_kubernetes import constants
from kuryr_kubernetes.controller.drivers import base
from kuryr_kubernetes import exceptions
from kuryr_kubernetes import utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class ConfigNodesSubnets(base.NodesSubnetsDriver):
"""Provides list of nodes subnets from configuration."""
def get_nodes_subnets(self, raise_on_empty=False):
node_subnet_ids = CONF.pod_vif_nested.worker_nodes_subnets
if not node_subnet_ids:
if raise_on_empty:
raise cfg.RequiredOptError(
'worker_nodes_subnets', cfg.OptGroup('pod_vif_nested'))
else:
return []
return node_subnet_ids
def add_node(self, node):
return False
def delete_node(self, node):
return False
class OpenShiftNodesSubnets(base.NodesSubnetsDriver):
"""Provides list of nodes subnets based on OpenShift Machine objects."""
def __init__(self):
super().__init__()
self.subnets = set()
def _get_subnet_from_machine(self, machine):
spec = machine['spec'].get('providerSpec', {}).get('value')
subnet_id = None
trunk = spec.get('trunk')
k8s = clients.get_kubernetes_client()
if 'primarySubnet' in spec:
# NOTE(gryf) in old OpenShift versions, primarySubnet was used for
# selecting primary subnet from multiple networks. In the future
# this field will be deprecated.
os_net = clients.get_network_client()
try:
subnet = os_net.find_subnet(spec['primarySubnet'])
except os_exc.DuplicateResource:
LOG.error('Name "%s" defined in primarySubnet for Machine/'
'MachineSet found in more than one subnets, which '
'may lead to issues. Please, use desired subnet id '
'instead.', spec['primarySubnet'])
k8s.add_event(machine, 'AmbiguousPrimarySubnet',
f'Name "{spec["primarySubnet"]}" defined in '
f'primarySubnet for Machine/MachineSet found in '
f'multiple subnets, which may lead to issues. '
f'Please, use desired subnet id instead.',
'Warning', 'kuryr-controller')
return None
except os_exc.SDKException as ex:
raise exceptions.ResourceNotReady(f'OpenStackSDK threw an '
f'exception {ex}, retrying.')
if not subnet:
LOG.error('Subnet name/id `%s` provided in MachineSet '
'primarySubnet field does not match any subnet. '
'Check the configuration.', spec['primarySubnet'])
k8s.add_event(machine, 'PrimarySubnetNotFound',
f'Name "{spec["primarySubnet"]}" defined in '
f'primarySubnet for Machine/MachineSet does '
f'not match any subnet. Check the configuration.'
'Warning', 'kuryr-controller')
return None
return subnet.id
if trunk and 'networks' in spec and spec['networks']:
subnets = spec['networks'][0].get('subnets')
if not subnets:
LOG.warning('No `subnets` in Machine `providerSpec.values.'
'networks`.')
else:
primary_subnet = subnets[0]
if primary_subnet.get('uuid'):
subnet_id = primary_subnet['uuid']
else:
subnet_filter = primary_subnet['filter']
subnet_id = utils.get_subnet_id(**subnet_filter)
if not subnet_id and 'ports' in spec and spec['ports']:
for port in spec['ports']:
if port.get('trunk', trunk) and port.get('fixedIPs'):
for fip in port['fixedIPs']:
if fip.get('subnetID'):
subnet_id = fip['subnetID']
break
if not subnet_id:
LOG.warning('No `subnets` found in Machine `providerSpec`')
return subnet_id
def get_nodes_subnets(self, raise_on_empty=False):
with lockutils.lock('kuryr-machine-add'):
# We add any hardcoded ones from config anyway.
result = self.subnets
if CONF.pod_vif_nested.worker_nodes_subnets:
result = result.union(
set(CONF.pod_vif_nested.worker_nodes_subnets))
if not result and raise_on_empty:
raise exceptions.ResourceNotReady(
'OpenShift Machines does not exist or are not yet '
'handled. Cannot determine worker nodes subnets.')
return list(result)
def add_node(self, machine):
subnet_id = self._get_subnet_from_machine(machine)
if not subnet_id:
LOG.warning('Could not determine subnet of Machine %s',
machine['metadata']['name'])
return False
with lockutils.lock('kuryr-machine-add'):
if subnet_id not in self.subnets:
LOG.info('Adding subnet %s to the worker nodes subnets as '
'machine %s runs in it.', subnet_id,
machine['metadata']['name'])
self.subnets.add(subnet_id)
return True
return False
def delete_node(self, machine):
k8s = clients.get_kubernetes_client()
affected_subnet_id = self._get_subnet_from_machine(machine)
if not affected_subnet_id:
LOG.warning('Could not determine subnet of Machine %s',
machine['metadata']['name'])
return False
machines = k8s.get(constants.OPENSHIFT_API_CRD_MACHINES)
for existing_machine in machines.get('items', []):
if affected_subnet_id == self._get_subnet_from_machine(
existing_machine):
return False
# We know that subnet is no longer used, so we remove it.
LOG.info('Removing subnet %s from the worker nodes subnets',
affected_subnet_id)
with lockutils.lock('kuryr-machine-add'):
self.subnets.remove(affected_subnet_id)
return True