/
nested_macvlan_vif.py
executable file
·150 lines (123 loc) · 6.18 KB
/
nested_macvlan_vif.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# 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.
import threading
from neutronclient.common import exceptions as n_exc
from oslo_log import log as logging
from kuryr_kubernetes import clients
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 os_vif_util as ovu
LOG = logging.getLogger(__name__)
class NestedMacvlanPodVIFDriver(nested_vif.NestedPodVIFDriver):
"""Manages ports for nested-containers using MACVLAN to provide VIFs."""
def __init__(self):
self.lock = threading.Lock()
def request_vif(self, pod, project_id, subnets, security_groups):
neutron = clients.get_neutron_client()
req = self._get_port_request(pod, project_id, subnets,
security_groups)
vm_port = self._get_parent_port(neutron, pod)
container_port = neutron.create_port(req).get('port')
utils.tag_neutron_resources('ports', [container_port['id']])
container_mac = container_port['mac_address']
container_ips = frozenset(entry['ip_address'] for entry in
container_port['fixed_ips'])
with self.lock:
self._add_to_allowed_address_pairs(neutron, vm_port,
container_ips, container_mac)
return ovu.neutron_to_osvif_vif_nested_macvlan(container_port, subnets)
def request_vifs(self, pod, project_id, subnets, security_groups,
num_ports):
# TODO(mchiappe): provide an implementation
raise NotImplementedError()
def release_vif(self, pod, vif, project_id=None, security_groups=None):
neutron = clients.get_neutron_client()
container_port = neutron.show_port(vif.id).get('port')
container_mac = container_port['mac_address']
container_ips = frozenset(entry['ip_address'] for entry in
container_port['fixed_ips'])
with self.lock:
vm_port = self._get_parent_port(neutron, pod)
self._remove_from_allowed_address_pairs(neutron, vm_port,
container_ips,
container_mac)
try:
neutron.delete_port(vif.id)
except n_exc.PortNotFoundClient:
LOG.warning("Unable to release port %s as it no longer exists.",
vif.id)
def activate_vif(self, pod, vif):
# NOTE(mchiappe): there is no way to get feedback on the actual
# interface creation or activation as no plugging can happen for this
# interface type. However the status of the port is not relevant as
# it is used for IPAM purposes only, thus just set 'active'
# immediately to let the CNI driver make progress.
vif.active = True
def _add_to_allowed_address_pairs(self, neutron, port, ip_addresses,
mac_address=None):
if not ip_addresses:
raise k_exc.IntegrityError(
"Cannot add pair from the "
"allowed_address_pairs of port %s: missing IP address",
port['id'])
mac = mac_address if mac_address else port['mac_address']
address_pairs = port['allowed_address_pairs']
# look for duplicates or near-matches
for pair in address_pairs:
if pair['ip_address'] in ip_addresses:
if pair['mac_address'] is mac:
raise k_exc.AllowedAddressAlreadyPresent(
"Pair %s already "
"present in the 'allowed_address_pair' list. This is "
"due to a misconfiguration or a bug", pair)
else:
LOG.warning(
"A pair with IP %s but different MAC address "
"is already present in the 'allowed_address_pair'. "
"This could indicate a misconfiguration or a "
"bug", pair['ip_address'])
for ip in ip_addresses:
address_pairs.append({'ip_address': ip, 'mac_address': mac})
self._update_port_address_pairs(neutron, port['id'], address_pairs)
def _remove_from_allowed_address_pairs(self, neutron, port, ip_addresses,
mac_address=None):
if not ip_addresses:
raise k_exc.IntegrityError(
"Cannot remove pair from the "
"allowed_address_pairs of port %s: missing IP address",
port['id'])
mac = mac_address if mac_address else port['mac_address']
address_pairs = port['allowed_address_pairs']
updated = False
for ip in ip_addresses:
try:
address_pairs.remove({'ip_address': ip, 'mac_address': mac})
updated = True
except ValueError:
LOG.error("No {'ip_address': %s, 'mac_address': %s} pair "
"found in the 'allowed_address_pair' list while "
"trying to remove it.", ip, mac)
if updated:
self._update_port_address_pairs(neutron, port['id'], address_pairs)
def _update_port_address_pairs(self, neutron, port_id, address_pairs):
try:
neutron.update_port(
port_id,
{'port': {'allowed_address_pairs': address_pairs}}
)
except n_exc.NeutronClientException:
LOG.exception("Error happened during updating Neutron port %s",
port_id)
raise