"Fossies" - the Fresh Open Source Software Archive

Member "zun-7.0.0/zun/cni/plugins/zun_cni_registry.py" (14 Apr 2021, 7095 Bytes) of package /linux/misc/openstack/zun-7.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "zun_cni_registry.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.0.0_vs_7.0.0.

    1 # Licensed under the Apache License, Version 2.0 (the "License");
    2 # you may not use this file except in compliance with the License.
    3 # You may obtain a copy of the License at
    4 #
    5 #   http://www.apache.org/licenses/LICENSE-2.0
    6 #
    7 # Unless required by applicable law or agreed to in writing, software
    8 # distributed under the License is distributed on an "AS IS" BASIS,
    9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   10 # See the License for the specific language governing permissions and
   11 # limitations under the License.
   12 
   13 import retrying
   14 
   15 from os_vif import objects as obj_vif
   16 from oslo_concurrency import lockutils
   17 from oslo_config import cfg
   18 from oslo_log import log as logging
   19 
   20 from zun.cni.binding import base as b_base
   21 from zun.cni import utils as cni_utils
   22 from zun.common import consts
   23 from zun.common import context as zun_context
   24 from zun.common import exception
   25 from zun.network import neutron
   26 from zun import objects
   27 
   28 
   29 LOG = logging.getLogger(__name__)
   30 CONF = cfg.CONF
   31 RETRY_DELAY = 1000  # 1 second in milliseconds
   32 TYPE_CAPSULE = 'CAPSULE'
   33 TYPE_CONTAINER = 'CONTAINER'
   34 
   35 
   36 class ZunCNIRegistryPlugin(object):
   37     def __init__(self, registry):
   38         self.registry = registry
   39         self.host = CONF.host
   40         self.context = zun_context.get_admin_context(all_projects=True)
   41         self.neutron_api = neutron.NeutronAPI(self.context)
   42 
   43     def _get_container_uuid(self, params):
   44         # NOTE(hongbin): The runtime should set K8S_POD_NAME as
   45         # capsule/container uuid
   46         return params.args.K8S_POD_NAME
   47 
   48     def _get_container_type(self, params):
   49         return getattr(params.args, 'ZUN_CONTAINER_TYPE', TYPE_CAPSULE)
   50 
   51     def add(self, params):
   52         vifs = self._do_work(params, b_base.connect)
   53 
   54         container_uuid = self._get_container_uuid(params)
   55 
   56         # NOTE(dulek): Saving containerid to be able to distinguish old DEL
   57         #              requests that we should ignore. We need a lock to
   58         #              prevent race conditions and replace whole object in the
   59         #              dict for multiprocessing.Manager to notice that.
   60         with lockutils.lock(container_uuid, external=True):
   61             self.registry[container_uuid] = {
   62                 'containerid': params.CNI_CONTAINERID,
   63                 'vif_unplugged': False,
   64                 'del_received': False,
   65                 'vifs': {ifname: {'active': vif.active, 'id': vif.id}
   66                          for ifname, vif in vifs.items()},
   67             }
   68             LOG.debug('Saved containerid = %s for capsule/container %s',
   69                       params.CNI_CONTAINERID, container_uuid)
   70 
   71         # Wait for VIFs to become active.
   72         timeout = CONF.cni_daemon.vif_active_timeout
   73 
   74         def any_vif_inactive(vifs):
   75             """Return True if there is at least one VIF that's not ACTIVE."""
   76             return any(not vif['active'] for vif in vifs.values())
   77 
   78         # Wait for timeout sec, 1 sec between tries, retry when even one
   79         # vif is not active.
   80         @retrying.retry(stop_max_delay=timeout * 1000, wait_fixed=RETRY_DELAY,
   81                         retry_on_result=any_vif_inactive)
   82         def wait_for_active(container_uuid):
   83             return self.registry[container_uuid]['vifs']
   84 
   85         result = wait_for_active(container_uuid)
   86         for vif in result.values():
   87             if not vif['active']:
   88                 LOG.error("Timed out waiting for vifs to become active")
   89                 raise exception.ResourceNotReady(resource=container_uuid)
   90 
   91         return vifs[consts.DEFAULT_IFNAME]
   92 
   93     def delete(self, params):
   94         container_uuid = self._get_container_uuid(params)
   95         try:
   96             reg_ci = self.registry[container_uuid]['containerid']
   97             LOG.debug('Read containerid = %s for capsule/container %s',
   98                       reg_ci, container_uuid)
   99             if reg_ci and reg_ci != params.CNI_CONTAINERID:
  100                 # NOTE(dulek): This is a DEL request for some older (probably
  101                 #              failed) ADD call. We should ignore it or we'll
  102                 #              unplug a running capsule/container.
  103                 LOG.warning('Received DEL request for unknown ADD call. '
  104                             'Ignoring.')
  105                 return
  106         except KeyError:
  107             pass
  108 
  109         try:
  110             self._do_work(params, b_base.disconnect)
  111         except exception.ContainerNotFound:
  112             LOG.warning('Capsule/Container is not found in DB. Ignoring.')
  113             pass
  114 
  115         # NOTE(ndesh): We need to lock here to avoid race condition
  116         #              with the deletion code in the watcher to ensure that
  117         #              we delete the registry entry exactly once
  118         try:
  119             with lockutils.lock(container_uuid, external=True):
  120                 if self.registry[container_uuid]['del_received']:
  121                     LOG.debug("Remove capsule/container %(container)s from "
  122                               "registry", {'container': container_uuid})
  123                     del self.registry[container_uuid]
  124                 else:
  125                     LOG.debug("unplug vif for capsule/container %(container)s",
  126                               {'container': container_uuid})
  127                     container_dict = self.registry[container_uuid]
  128                     container_dict['vif_unplugged'] = True
  129                     self.registry[container_uuid] = container_dict
  130         except KeyError:
  131             # This means the capsule/container was removed before vif was
  132             # unplugged. This shouldn't happen, but we can't do anything
  133             # about it now
  134             LOG.debug('Capsule/Container %s not found while handling DEL '
  135                       'request. Ignoring.', container_uuid)
  136             pass
  137 
  138     def _do_work(self, params, fn):
  139         container_uuid = self._get_container_uuid(params)
  140         container_type = self._get_container_type(params)
  141 
  142         if container_type == TYPE_CAPSULE:
  143             container = objects.Capsule.get_by_uuid(self.context,
  144                                                     container_uuid)
  145         elif container_type == TYPE_CONTAINER:
  146             container = objects.Container.get_by_uuid(self.context,
  147                                                       container_uuid)
  148         else:
  149             raise exception.CNIError('Unexpected type: %s' % container_type)
  150 
  151         vifs = cni_utils.get_vifs(container)
  152 
  153         for ifname, vif in vifs.items():
  154             is_default_gateway = (ifname == consts.DEFAULT_IFNAME)
  155             if is_default_gateway:
  156                 # NOTE(ygupta): if this is the default interface, we should
  157                 # use the ifname supplied in the CNI ADD request
  158                 ifname = params.CNI_IFNAME
  159 
  160             fn(vif, self._get_inst(container), ifname, params.CNI_NETNS,
  161                is_default_gateway=is_default_gateway,
  162                container_id=params.CNI_CONTAINERID)
  163         return vifs
  164 
  165     def _get_inst(self, container):
  166         return obj_vif.instance_info.InstanceInfo(
  167             uuid=container.uuid, name=container.name)