# Copyright (c) 2016 Mirantis, 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 kuryr.lib import constants as kl_const from openstack import exceptions as os_exc from oslo_config import cfg from oslo_log import log as logging from kuryr_kubernetes import clients from kuryr_kubernetes import config from kuryr_kubernetes import constants from kuryr_kubernetes.controller.drivers import base from kuryr_kubernetes.controller.drivers import utils from kuryr_kubernetes import exceptions as k_exc from kuryr_kubernetes import os_vif_util as ovu LOG = logging.getLogger(__name__) CONF = cfg.CONF class NeutronPodVIFDriver(base.PodVIFDriver): """Manages normal Neutron ports to provide VIFs for Kubernetes Pods.""" def __init__(self): super(NeutronPodVIFDriver, self).__init__() self._tag_on_creation = utils.check_tag_on_creation() if self._tag_on_creation: LOG.info('Neutron supports tagging during bulk port creation.') else: LOG.warning('Neutron does not support tagging during bulk ' 'port creation. Kuryr will tag resources after ' 'port creation.') def request_vif(self, pod, project_id, subnets, security_groups): os_net = clients.get_network_client() rq = self._get_port_request(pod, project_id, subnets, security_groups) port = os_net.create_port(**rq) self._check_port_binding([port]) if not self._tag_on_creation: utils.tag_neutron_resources([port]) return ovu.neutron_to_osvif_vif(port.binding_vif_type, port, subnets) def request_vifs(self, pod, project_id, subnets, security_groups, num_ports): os_net = clients.get_network_client() rq = self._get_port_request(pod, project_id, subnets, security_groups, unbound=True) bulk_port_rq = {'ports': [rq] * num_ports} try: ports = list(os_net.create_ports(bulk_port_rq)) except os_exc.SDKException: LOG.exception("Error creating bulk ports: %s", bulk_port_rq) raise vif_plugin = ports[0].binding_vif_type # NOTE(ltomasbo): Due to the bug (1696051) on neutron bulk port # creation request returning the port objects without binding # information, an additional port show is performed to get the binding # information if vif_plugin == 'unbound': port_info = os_net.get_port(ports[0].id) vif_plugin = port_info.binding_vif_type self._check_port_binding(ports) if not self._tag_on_creation: utils.tag_neutron_resources(ports) vifs = [] for port in ports: vif = ovu.neutron_to_osvif_vif(vif_plugin, port, subnets) vifs.append(vif) return vifs def release_vif(self, pod, vif, project_id=None, security_groups=None): clients.get_network_client().delete_port(vif.id) def activate_vif(self, vif): if vif.active: return os_net = clients.get_network_client() try: port = os_net.get_port(vif.id) except os_exc.SDKException: LOG.debug("Unable to obtain port information, retrying.") raise k_exc.ResourceNotReady(vif) if port['status'] != kl_const.PORT_STATUS_ACTIVE: raise k_exc.ResourceNotReady(vif) vif.active = True def update_vif_sgs(self, pod, security_groups): os_net = clients.get_network_client() vifs = utils.get_vifs(pod) if vifs: # NOTE(ltomasbo): It just updates the default_vif security group port_id = vifs[constants.DEFAULT_IFNAME].id os_net.update_port(port_id, security_groups=list(security_groups)) def _get_port_request(self, pod, project_id, subnets, security_groups, unbound=False): port_req_body = {'project_id': project_id, 'network_id': utils.get_network_id(subnets), 'fixed_ips': ovu.osvif_to_neutron_fixed_ips(subnets), 'device_owner': kl_const.DEVICE_OWNER, 'admin_state_up': True, 'binding_host_id': utils.get_host_id(pod)} # if unbound argument is set to true, it means the port requested # should not be bound and not associated to the pod. Thus the port dict # is filled with a generic name (constants.KURYR_PORT_NAME) if # port_debug is enabled, and without device_id if unbound and config.CONF.kubernetes.port_debug: port_req_body['name'] = constants.KURYR_PORT_NAME else: # only set the name if port_debug is enabled if config.CONF.kubernetes.port_debug: port_req_body['name'] = utils.get_port_name(pod) port_req_body['device_id'] = utils.get_device_id(pod) if security_groups: port_req_body['security_groups'] = security_groups if self._tag_on_creation: tags = CONF.neutron_defaults.resource_tags if tags: port_req_body['tags'] = tags return port_req_body def _check_port_binding(self, ports): if ports[0].binding_vif_type == "binding_failed": for port in ports: clients.get_network_client().delete_port(port.id) LOG.error("Binding failed error for ports: %s." " Please check Neutron for errors.", ports) raise k_exc.ResourceNotReady(ports)