def _trigger_return_to_pool(self):
        if not hasattr(self, '_recyclable_ports'):
            LOG.info("Kuryr-controller not yet ready to return ports to "
                     "pools.")
            return
        neutron = clients.get_neutron_client()
        sg_current = {}
        if not config.CONF.kubernetes.port_debug:
            attrs = {'device_owner': ['trunk:subport', kl_const.DEVICE_OWNER]}
            tags = config.CONF.neutron_defaults.resource_tags
            if tags:
                attrs['tags'] = tags
            kuryr_subports = c_utils.get_ports_by_attrs(**attrs)
            for subport in kuryr_subports:
                if subport['id'] in self._recyclable_ports:
                    sg_current[subport['id']] = tuple(
                        sorted(subport['security_groups']))

        for port_id, pool_key in self._recyclable_ports.copy().items():
            if (not oslo_cfg.CONF.vif_pool.ports_pool_max
                    or self._get_pool_size(pool_key) <
                    oslo_cfg.CONF.vif_pool.ports_pool_max):
                port_name = (constants.KURYR_PORT_NAME
                             if config.CONF.kubernetes.port_debug else '')
                if config.CONF.kubernetes.port_debug:
                    try:
                        neutron.update_port(port_id,
                                            {"port": {
                                                'name': port_name,
                                            }})
                    except n_exc.NeutronClientException:
                        LOG.warning(
                            "Error changing name for port %s to be "
                            "reused, put back on the cleanable "
                            "pool.", port_id)
                        continue
                self._available_ports_pools.setdefault(
                    pool_key, {}).setdefault(sg_current.get(port_id),
                                             []).append(port_id)
            else:
                trunk_id = self._get_trunk_id(neutron, pool_key)
                try:
                    self._drv_vif._remove_subport(neutron, trunk_id, port_id)
                    self._drv_vif._release_vlan_id(
                        self._existing_vifs[port_id].vlan_id)
                    del self._existing_vifs[port_id]
                    neutron.delete_port(port_id)
                except n_exc.PortNotFoundClient:
                    LOG.debug(
                        'Unable to release port %s as it no longer '
                        'exists.', port_id)
                except KeyError:
                    LOG.debug('Port %s is not in the ports list.', port_id)
                except n_exc.NeutronClientException:
                    LOG.warning('Error removing the subport %s', port_id)
                    continue
            try:
                del self._recyclable_ports[port_id]
            except KeyError:
                LOG.debug('Port already recycled: %s', port_id)
    def _recover_precreated_ports(self):
        attrs = {'device_owner': kl_const.DEVICE_OWNER}
        tags = config.CONF.neutron_defaults.resource_tags
        if tags:
            attrs['tags'] = tags

        if config.CONF.kubernetes.port_debug:
            attrs['name'] = constants.KURYR_PORT_NAME
            available_ports = c_utils.get_ports_by_attrs(**attrs)
        else:
            kuryr_ports = c_utils.get_ports_by_attrs(**attrs)
            in_use_ports = self._get_in_use_ports()
            available_ports = [port for port in kuryr_ports
                               if port['id'] not in in_use_ports]

        _, available_subports, _ = self._get_trunks_info()
        for port in available_ports:
            # NOTE(ltomasbo): ensure subports are not considered for
            # recovering in the case of multi pools
            if available_subports.get(port['id']):
                continue
            vif_plugin = self._drv_vif._get_vif_plugin(port)
            port_host = port['binding:host_id']
            if not vif_plugin or not port_host:
                # NOTE(ltomasbo): kuryr-controller is running without the
                # rights to get the needed information to recover the ports.
                # Thus, removing the port instead
                neutron = clients.get_neutron_client()
                neutron.delete_port(port['id'])
                continue
            subnet_id = port['fixed_ips'][0]['subnet_id']
            subnet = {
                subnet_id: utils.get_subnet(subnet_id)}
            vif = ovu.neutron_to_osvif_vif(vif_plugin, port, subnet)
            net_obj = subnet[subnet_id]
            pool_key = self._get_pool_key(port_host,
                                          port['project_id'],
                                          net_obj.id, None)

            self._existing_vifs[port['id']] = vif
            self._available_ports_pools.setdefault(
                pool_key, {}).setdefault(
                    tuple(sorted(port['security_groups'])), []).append(
                        port['id'])

        LOG.info("PORTS POOL: pools updated with pre-created ports")
        self._create_healthcheck_file()
Exemple #3
0
    def _delete_namespace_network_resources(self, subnet_id, net_id):
        neutron = clients.get_neutron_client()
        if subnet_id:
            router_id = oslo_cfg.CONF.namespace_subnet.pod_router
            try:
                neutron.remove_interface_router(router_id,
                                                {"subnet_id": subnet_id})
            except n_exc.NotFound:
                LOG.debug("Subnet %(subnet)s not attached to router "
                          "%(router)s", {'subnet': subnet_id,
                                         'router': router_id})
            except n_exc.NeutronClientException:
                LOG.exception("Error deleting subnet %(subnet)s from router "
                              "%(router)s.", {'subnet': subnet_id, 'router':
                                              router_id})
                raise

        try:
            neutron.delete_network(net_id)
        except n_exc.NotFound:
            LOG.debug("Neutron Network not found: %s", net_id)
        except n_exc.NetworkInUseClient:
            LOG.exception("One or more ports in use on the network %s. "
                          "Deleting leftovers ports before retrying", net_id)
            leftover_ports = c_utils.get_ports_by_attrs(status='DOWN',
                                                        network_id=net_id)
            for leftover_port in leftover_ports:
                try:
                    neutron.delete_port(leftover_port['id'])
                except n_exc.PortNotFoundClient:
                    LOG.debug("Port already deleted.")
                except n_exc.NeutronClientException as e:
                    if "currently a subport for trunk" in str(e):
                        LOG.warning("Port %s is in DOWN status but still "
                                    "associated to a trunk. This should not "
                                    "happen. Trying to delete it from the "
                                    "trunk.", leftover_port['id'])
                        # Get the trunk_id from the error message
                        trunk_id = (
                            str(e).split('trunk')[1].split('.')[0].strip())
                        try:
                            neutron.trunk_remove_subports(
                                trunk_id, {'sub_ports': [
                                    {'port_id': leftover_port['id']}]})
                        except n_exc.NotFound:
                            LOG.debug("Port %s already removed from trunk %s",
                                      leftover_port['id'], trunk_id)
                    else:
                        LOG.exception("Unexpected error deleting leftover "
                                      "port %s. Skiping it and continue with "
                                      "the other rest.", leftover_port['id'])
            raise exceptions.ResourceNotReady(net_id)
        except n_exc.NeutronClientException:
            LOG.exception("Error deleting network %s.", net_id)
            raise
Exemple #4
0
    def _delete_namespace_network_resources(self, subnet_id, net_id):
        neutron = clients.get_neutron_client()
        if subnet_id:
            router_id = oslo_cfg.CONF.namespace_subnet.pod_router
            try:
                neutron.remove_interface_router(router_id,
                                                {"subnet_id": subnet_id})
            except n_exc.NotFound:
                LOG.debug(
                    "Subnet %(subnet)s not attached to router "
                    "%(router)s", {
                        'subnet': subnet_id,
                        'router': router_id
                    })
            except n_exc.NeutronClientException:
                LOG.exception(
                    "Error deleting subnet %(subnet)s from router "
                    "%(router)s.", {
                        'subnet': subnet_id,
                        'router': router_id
                    })
                raise

        try:
            neutron.delete_network(net_id)
        except n_exc.NotFound:
            LOG.debug("Neutron Network not found: %s", net_id)
        except n_exc.NetworkInUseClient:
            LOG.exception(
                "One or more ports in use on the network %s. "
                "Deleting leftovers ports before retrying", net_id)
            leftover_ports = c_utils.get_ports_by_attrs(status='DOWN',
                                                        network_id=net_id)
            for leftover_port in leftover_ports:
                try:
                    neutron.delete_port(leftover_port['id'])
                except n_exc.PortNotFoundClient:
                    LOG.debug("Port already deleted.")
                except n_exc.NeutronClientException:
                    LOG.debug(
                        "Unexpected error deleting leftover port %s. "
                        "Skiping it and continue with the other rest.",
                        leftover_port['id'])
                    continue
            raise exceptions.ResourceNotReady(net_id)
        except n_exc.NeutronClientException:
            LOG.exception("Error deleting network %s.", net_id)
            raise
    def _cleanup_leftover_ports(self):
        neutron = clients.get_neutron_client()
        attrs = {'device_owner': kl_const.DEVICE_OWNER, 'status': 'DOWN'}
        existing_ports = c_utils.get_ports_by_attrs(**attrs)

        tags = config.CONF.neutron_defaults.resource_tags
        if tags:
            nets = neutron.list_networks(tags=tags)['networks']
            nets_ids = [n['id'] for n in nets]
            for port in existing_ports:
                net_id = port['network_id']
                if net_id in nets_ids:
                    if port.get('binding:host_id'):
                        for tag in tags:
                            if tag not in port.get('tags', []):
                                # delete the port if it has binding details, it
                                # belongs to the deployment subnet and it does
                                # not have the right tags
                                try:
                                    neutron.delete_port(port['id'])
                                    break
                                except n_exc.NeutronClientException:
                                    LOG.debug(
                                        "Problem deleting leftover port "
                                        "%s. Skipping.", port['id'])
                                    continue
                    else:
                        # delete port if they have no binding but belong to the
                        # deployment networks, regardless of their tagging
                        try:
                            neutron.delete_port(port['id'])
                        except n_exc.NeutronClientException:
                            LOG.debug(
                                "Problem deleting leftover port %s. "
                                "Skipping.", port['id'])
                            continue
        else:
            for port in existing_ports:
                if not port.get('binding:host_id'):
                    neutron.delete_port(port['id'])
    def _get_trunks_info(self):
        """Returns information about trunks and their subports.

        This method searches for parent ports and subports among the active
        neutron ports.
        To find the parent ports it filters the ones that have trunk_details,
        i.e., the ones that are the parent port of a trunk.
        To find the subports to recover, it filters out the ports that are
        already in used by running kubernetes pods. It also filters out the
        ports whose device_owner is not related to subports, i.e., the ports
        that are not attached to trunks, such as active ports allocated to
        running VMs.
        At the same time it collects information about ports subnets to
        minimize the number of interaction with Neutron API.

        It returns three dictionaries with the needed information about the
        parent ports, subports and subnets

        :return: 3 dicts with the trunk details (Key: trunk_id; Value: dict
        containing ip and subports), subport details (Key: port_id; Value:
        port_object), and subnet details (Key: subnet_id; Value: subnet dict)
        """
        # REVISIT(ltomasbo): there is no need to recover the subports
        # belonging to trunk ports whose parent port is DOWN as that means no
        # pods can be scheduled there. We may need to update this if we allow
        # lively extending the kubernetes cluster with VMs that already have
        # precreated subports. For instance by shutting down and up a
        # kubernetes Worker VM with subports already attached, and the
        # controller is restarted in between.
        parent_ports = {}
        subports = {}
        subnets = {}

        attrs = {'status': 'ACTIVE'}
        tags = config.CONF.neutron_defaults.resource_tags
        if tags:
            attrs['tags'] = tags
        all_active_ports = c_utils.get_ports_by_attrs(**attrs)
        in_use_ports = self._get_in_use_ports()

        for port in all_active_ports:
            trunk_details = port.get('trunk_details')
            # Parent port
            if trunk_details:
                parent_ports[trunk_details['trunk_id']] = {
                    'ip': port['fixed_ips'][0]['ip_address'],
                    'subports': trunk_details['sub_ports']}
            else:
                # Filter to only get subports that are not in use
                if (port['id'] not in in_use_ports and
                    port['device_owner'] in ['trunk:subport',
                                             kl_const.DEVICE_OWNER]):
                    subports[port['id']] = port
                    # NOTE(ltomasbo): _get_subnet can be costly as it
                    # needs to call neutron to get network and subnet
                    # information. This ensures it is only called once
                    # per subnet in use
                    subnet_id = port['fixed_ips'][0]['subnet_id']
                    if not subnets.get(subnet_id):
                        subnets[subnet_id] = {subnet_id:
                                              utils.get_subnet(
                                                  subnet_id)}
        return parent_ports, subports, subnets