Add option to tag Neutron resources created by us

If we consider a K8s cluster running on OpenStack VM's, which is a
perfect use case for Kuryr-Kubernetes, we can easily imagine creating
multiple clusters in a single OpenStack public or private cloud. In such
use case those K8s clusters may come and go. As Kuryr is creating some
OpenStack resources, such as ports, networks, subnets, floating IP's or
SG's, it's useful to have a way of identifying those Kuryr-created
resources to delete them along with the K8s cluster that used them.

This commit makes that possible by adding an option to add tag to all
Neutron resources created by Kuryr.

Change-Id: If75028e17d13ec62fb414fa9797ee7ac02d948d1
Implements: blueprint kuryr-resources-tagging
This commit is contained in:
Michał Dulko 2019-02-13 10:04:08 +01:00
parent 2f756d9370
commit 88e38e8e94
12 changed files with 49 additions and 9 deletions

View File

@ -205,6 +205,13 @@ neutron_defaults = [
help=_("A mapping of default subnets for certain driverType " help=_("A mapping of default subnets for certain driverType "
"in a form of <driverType>:<SUBNET-ID>"), "in a form of <driverType>:<SUBNET-ID>"),
default={}), default={}),
cfg.ListOpt('resource_tags',
help=_("List of tags that will be applied to all OpenStack "
"(Neutron and Octavia) resources created by Kuryr. "
"This can be used to identify and garbage-collect "
"them when Kubernetes cluster Kuryr was serving is no "
"longer needed."),
default=[])
] ]
octavia_defaults = [ octavia_defaults = [

View File

@ -29,6 +29,7 @@ from oslo_utils import timeutils
from kuryr_kubernetes import clients from kuryr_kubernetes import clients
from kuryr_kubernetes import config from kuryr_kubernetes import config
from kuryr_kubernetes.controller.drivers import base from kuryr_kubernetes.controller.drivers import base
from kuryr_kubernetes.controller.drivers import utils as c_utils
from kuryr_kubernetes import exceptions as k_exc from kuryr_kubernetes import exceptions as k_exc
from kuryr_kubernetes.objects import lbaas as obj_lbaas from kuryr_kubernetes.objects import lbaas as obj_lbaas
from kuryr_kubernetes import utils from kuryr_kubernetes import utils
@ -119,6 +120,7 @@ class LBaaSv2Driver(base.LBaaSDriver):
}, },
}) })
sg_id = sg['security_group']['id'] sg_id = sg['security_group']['id']
c_utils.tag_neutron_resources('security-groups', [sg_id])
loadbalancer.security_groups.append(sg_id) loadbalancer.security_groups.append(sg_id)
vip_port = self._get_vip_port(loadbalancer) vip_port = self._get_vip_port(loadbalancer)
neutron.update_port( neutron.update_port(
@ -290,6 +292,7 @@ class LBaaSv2Driver(base.LBaaSDriver):
}, },
}) })
sg_id = sg['security_group']['id'] sg_id = sg['security_group']['id']
c_utils.tag_neutron_resources('security-groups', [sg_id])
loadbalancer.security_groups.append(sg_id) loadbalancer.security_groups.append(sg_id)
vip_port = self._get_vip_port(loadbalancer) vip_port = self._get_vip_port(loadbalancer)
neutron.update_port( neutron.update_port(

View File

@ -21,6 +21,7 @@ from kuryr_kubernetes import clients
from kuryr_kubernetes import config from kuryr_kubernetes import config
from kuryr_kubernetes import constants from kuryr_kubernetes import constants
from kuryr_kubernetes.controller.drivers import base from kuryr_kubernetes.controller.drivers import base
from kuryr_kubernetes.controller.drivers import utils
from kuryr_kubernetes import exceptions from kuryr_kubernetes import exceptions
from neutronclient.common import exceptions as n_exc from neutronclient.common import exceptions as n_exc
@ -107,6 +108,7 @@ class NamespacePodSecurityGroupsDriver(base.PodSecurityGroupsDriver):
"project_id": project_id "project_id": project_id
} }
}).get('security_group') }).get('security_group')
utils.tag_neutron_resources('security-groups', [sg['id']])
neutron.create_security_group_rule( neutron.create_security_group_rule(
{ {
"security_group_rule": { "security_group_rule": {

View File

@ -19,6 +19,7 @@ from oslo_log import log as logging
from kuryr_kubernetes import clients from kuryr_kubernetes import clients
from kuryr_kubernetes import constants from kuryr_kubernetes import constants
from kuryr_kubernetes.controller.drivers import default_subnet from kuryr_kubernetes.controller.drivers import default_subnet
from kuryr_kubernetes.controller.drivers import utils as c_utils
from kuryr_kubernetes import exceptions from kuryr_kubernetes import exceptions
from kuryr_kubernetes import utils from kuryr_kubernetes import utils
@ -121,6 +122,7 @@ class NamespacePodSubnetDriver(default_subnet.DefaultPodSubnetDriver):
"project_id": project_id "project_id": project_id
} }
}).get('network') }).get('network')
c_utils.tag_neutron_resources('networks', [neutron_net['id']])
# create a subnet within that network # create a subnet within that network
neutron_subnet = neutron.create_subnet( neutron_subnet = neutron.create_subnet(
@ -134,6 +136,7 @@ class NamespacePodSubnetDriver(default_subnet.DefaultPodSubnetDriver):
"project_id": project_id "project_id": project_id
} }
}).get('subnet') }).get('subnet')
c_utils.tag_neutron_resources('subnets', [neutron_subnet['id']])
# connect the subnet to the router # connect the subnet to the router
neutron.add_interface_router(router_id, neutron.add_interface_router(router_id,

View File

@ -19,6 +19,7 @@ from oslo_log import log as logging
from kuryr_kubernetes import clients from kuryr_kubernetes import clients
from kuryr_kubernetes.controller.drivers import nested_vif from kuryr_kubernetes.controller.drivers import nested_vif
from kuryr_kubernetes.controller.drivers import utils
from kuryr_kubernetes import exceptions as k_exc from kuryr_kubernetes import exceptions as k_exc
from kuryr_kubernetes import os_vif_util as ovu from kuryr_kubernetes import os_vif_util as ovu
@ -37,6 +38,7 @@ class NestedMacvlanPodVIFDriver(nested_vif.NestedPodVIFDriver):
security_groups) security_groups)
vm_port = self._get_parent_port(neutron, pod) vm_port = self._get_parent_port(neutron, pod)
container_port = neutron.create_port(req).get('port') container_port = neutron.create_port(req).get('port')
utils.tag_neutron_resources('ports', [container_port['id']])
container_mac = container_port['mac_address'] container_mac = container_port['mac_address']
container_ips = frozenset(entry['ip_address'] for entry in container_ips = frozenset(entry['ip_address'] for entry in

View File

@ -44,6 +44,7 @@ class NestedVlanPodVIFDriver(nested_vif.NestedPodVIFDriver):
rq = self._get_port_request(pod, project_id, subnets, security_groups) rq = self._get_port_request(pod, project_id, subnets, security_groups)
port = neutron.create_port(rq).get('port') port = neutron.create_port(rq).get('port')
utils.tag_neutron_resources('ports', [port['id']])
vlan_id = self._add_subport(neutron, trunk_id, port['id']) vlan_id = self._add_subport(neutron, trunk_id, port['id'])
return ovu.neutron_to_osvif_vif_nested_vlan(port, subnets, vlan_id) return ovu.neutron_to_osvif_vif_nested_vlan(port, subnets, vlan_id)
@ -78,12 +79,14 @@ class NestedVlanPodVIFDriver(nested_vif.NestedPodVIFDriver):
LOG.error("There are no vlan ids available to create subports") LOG.error("There are no vlan ids available to create subports")
return [] return []
bulk_port_rq = {'ports': [port_rq for _ in range(len(subports_info))]} bulk_port_rq = {'ports': [port_rq] * len(subports_info)}
try: try:
ports = neutron.create_port(bulk_port_rq).get('ports') ports = neutron.create_port(bulk_port_rq).get('ports')
except n_exc.NeutronClientException: except n_exc.NeutronClientException:
LOG.exception("Error creating bulk ports: %s", bulk_port_rq) LOG.exception("Error creating bulk ports: %s", bulk_port_rq)
raise raise
utils.tag_neutron_resources('ports', [port['id'] for port in ports])
for index, port in enumerate(ports): for index, port in enumerate(ports):
subports_info[index]['port_id'] = port['id'] subports_info[index]['port_id'] = port['id']

View File

@ -167,6 +167,7 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver):
# Create initial security group # Create initial security group
sg = self.neutron.create_security_group(body=security_group_body) sg = self.neutron.create_security_group(body=security_group_body)
sg_id = sg['security_group']['id'] sg_id = sg['security_group']['id']
driver_utils.tag_neutron_resources('security-groups', [sg_id])
i_rules, e_rules = self.parse_network_policy_rules(policy, sg_id) i_rules, e_rules = self.parse_network_policy_rules(policy, sg_id)
for i_rule in i_rules: for i_rule in i_rules:
sgr_id = driver_utils.create_security_group_rule(i_rule) sgr_id = driver_utils.create_security_group_rule(i_rule)

View File

@ -37,6 +37,7 @@ class NeutronPodVIFDriver(base.PodVIFDriver):
rq = self._get_port_request(pod, project_id, subnets, security_groups) rq = self._get_port_request(pod, project_id, subnets, security_groups)
port = neutron.create_port(rq).get('port') port = neutron.create_port(rq).get('port')
utils.tag_neutron_resources('ports', [port['id']])
vif_plugin = self._get_vif_plugin(port) vif_plugin = self._get_vif_plugin(port)
return ovu.neutron_to_osvif_vif(vif_plugin, port, subnets) return ovu.neutron_to_osvif_vif(vif_plugin, port, subnets)
@ -48,12 +49,13 @@ class NeutronPodVIFDriver(base.PodVIFDriver):
rq = self._get_port_request(pod, project_id, subnets, security_groups, rq = self._get_port_request(pod, project_id, subnets, security_groups,
unbound=True) unbound=True)
bulk_port_rq = {'ports': [rq for _ in range(num_ports)]} bulk_port_rq = {'ports': [rq] * num_ports}
try: try:
ports = neutron.create_port(bulk_port_rq).get('ports') ports = neutron.create_port(bulk_port_rq).get('ports')
except n_exc.NeutronClientException: except n_exc.NeutronClientException:
LOG.exception("Error creating bulk ports: %s", bulk_port_rq) LOG.exception("Error creating bulk ports: %s", bulk_port_rq)
raise raise
utils.tag_neutron_resources('ports', [port['id'] for port in ports])
vif_plugin = self._get_vif_plugin(ports[0]) vif_plugin = self._get_vif_plugin(ports[0])

View File

@ -13,10 +13,13 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import abc import abc
from kuryr_kubernetes import clients import six
from neutronclient.common import exceptions as n_exc from neutronclient.common import exceptions as n_exc
from oslo_log import log as logging from oslo_log import log as logging
import six
from kuryr_kubernetes import clients
from kuryr_kubernetes.controller.drivers import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -132,13 +135,13 @@ class FipPubIpDriver(BasePubIpDriver):
request['floatingip']['description'] = description request['floatingip']['description'] = description
try: try:
response = neutron.create_floatingip(request) fip = neutron.create_floatingip(request).get('floatingip')
except n_exc.NeutronClientException: except n_exc.NeutronClientException:
LOG.exception("Failed to create floating IP - netid=%s ", LOG.exception("Failed to create floating IP - netid=%s ",
pub_net_id) pub_net_id)
raise raise
return response['floatingip']['id'], response[ utils.tag_neutron_resources('networks', [fip['id']])
'floatingip']['floating_ip_address'] return fip['id'], fip['floating_ip_address']
def free_ip(self, res_id): def free_ip(self, res_id):
neutron = clients.get_neutron_client() neutron = clients.get_neutron_client()

View File

@ -50,6 +50,7 @@ class SriovVIFDriver(neutron_vif.NeutronPodVIFDriver):
subnets, security_groups) subnets, security_groups)
port = neutron.create_port(rq).get('port') port = neutron.create_port(rq).get('port')
c_utils.tag_neutron_resources('ports', [port['id']])
vif = ovu.neutron_to_osvif_vif(vif_plugin, port, subnets) vif = ovu.neutron_to_osvif_vif(vif_plugin, port, subnets)
vif.physnet = physnet vif.physnet = physnet

View File

@ -379,3 +379,16 @@ def get_namespace_subnet_cidr(namespace):
LOG.exception("Kubernetes Client Exception.") LOG.exception("Kubernetes Client Exception.")
raise raise
return net_crd['spec']['subnetCIDR'] return net_crd['spec']['subnetCIDR']
def tag_neutron_resources(resource, res_ids):
tags = CONF.neutron_defaults.resource_tags
if tags:
neutron = clients.get_neutron_client()
for res_id in res_ids:
try:
neutron.replace_tag(resource, res_id, body={"tags": tags})
except n_exc.NeutronClientException:
LOG.warning("Failed to tag %s %s with %s. Ignoring, but this "
"is still unexpected.", resource, res_id, tags,
exc_info=True)

View File

@ -39,7 +39,7 @@ class NeutronPodVIFDriver(test_base.TestCase):
project_id = mock.sentinel.project_id project_id = mock.sentinel.project_id
subnets = mock.sentinel.subnets subnets = mock.sentinel.subnets
security_groups = mock.sentinel.security_groups security_groups = mock.sentinel.security_groups
port = mock.sentinel.port port = {'id': '910b1183-1f4a-450a-a298-0e80ad06ec8b'}
port_request = mock.sentinel.port_request port_request = mock.sentinel.port_request
vif = mock.sentinel.vif vif = mock.sentinel.vif
vif_plugin = mock.sentinel.vif_plugin vif_plugin = mock.sentinel.vif_plugin
@ -72,7 +72,7 @@ class NeutronPodVIFDriver(test_base.TestCase):
port_request = mock.sentinel.port_request port_request = mock.sentinel.port_request
m_driver._get_port_request.return_value = port_request m_driver._get_port_request.return_value = port_request
port = mock.sentinel.port port = {'id': '910b1183-1f4a-450a-a298-0e80ad06ec8b'}
vif_plugin = mock.sentinel.vif_plugin vif_plugin = mock.sentinel.vif_plugin
vif = mock.sentinel.vif vif = mock.sentinel.vif
bulk_rq = {'ports': [port_request for _ in range(num_ports)]} bulk_rq = {'ports': [port_request for _ in range(num_ports)]}