"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)