-
Notifications
You must be signed in to change notification settings - Fork 33
/
contrailvif.py
243 lines (205 loc) · 9.41 KB
/
contrailvif.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
# 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 gettext
import threading
import time
import eventlet
gettext.install('contrail_vif')
from oslo.config import cfg
from nova.network import linux_net
# Default to _ as this does not work in havana
try:
from nova.openstack.common.gettextutils import _LE
except:
_LE = _
from nova.openstack.common import log as logging
from nova.openstack.common import loopingcall
from nova.openstack.common import processutils
from nova.virt.libvirt import designer
# Support for JUNO - Phase 1
# JUNO release doesn't support libvirt_vif_driver configuration in nova.conf
# vif_driver is set to LibvirtGenericVIFDriver. plug/unplug/get_config api from
# this class doesn't support opencontrail. Till opencontrail vif_driver is
# upstreamed, overwrite the vif_driver with VRouterVIFDriver
# This code will be removed after OpenContrail vif driver is upstreamed
try:
from nova.virt.libvirt.vif import LibvirtBaseVIFDriver as LibVirtVIFDriver
except ImportError:
# JUNO doesn't have LibvirtBaseVIFDriver implementation. So inherit VRouterVIFDriver
# from LibvirtGenericVIFDriver
from nova.virt.libvirt.vif import LibvirtGenericVIFDriver as LibVirtVIFDriver
from contrail_vrouter_api.vrouter_api import ContrailVRouterApi
from thrift.Thrift import TApplicationException
LOG = logging.getLogger(__name__)
from nova.network.neutronv2.api import API
from nova.compute.manager import ComputeManager
from nova.compute import utils as compute_utils
orig_get_nw_info_for_instance = None
compute_mgr = None
# MonkeyPatch the vif_driver with VRouterVIFDriver during restart of nova-compute
def patched_get_nw_info_for_instance(instance):
if not isinstance(compute_mgr.driver.vif_driver, VRouterVIFDriver):
compute_mgr.driver.vif_driver = \
VRouterVIFDriver(compute_mgr.driver._get_connection)
return orig_get_nw_info_for_instance(instance)
class ContrailNetworkAPI(API):
def __init__(self):
# MonkeyPatch the compute_utils.get_nw_info_for_instance with
# patched_get_nw_info_for_instance to enable overwriting vif_driver
if orig_get_nw_info_for_instance is None:
global orig_get_nw_info_for_instance
orig_get_nw_info_for_instance = compute_utils.get_nw_info_for_instance
compute_utils.get_nw_info_for_instance = patched_get_nw_info_for_instance
# Store the compute manager object to overwrite vif_driver
import inspect
global compute_mgr
compute_mgr = inspect.stack()[2][0].f_locals['self']
if not isinstance(compute_mgr, ComputeManager):
compute_mgr = inspect.stack()[5][0].f_locals['self']
if not isinstance(compute_mgr, ComputeManager):
raise BadRequest("Can't get hold of compute manager");
super(ContrailNetworkAPI, self).__init__()
#end __init__
def allocate_for_instance(self, *args, **kwargs):
# Monkey patch the vif_driver if not already set
if not isinstance(compute_mgr.driver.vif_driver, VRouterVIFDriver):
compute_mgr.driver.vif_driver = \
VRouterVIFDriver(compute_mgr.driver._get_connection)
return super(ContrailNetworkAPI, self).allocate_for_instance(*args, **kwargs)
#end
def deallocate_for_instance(self, *args, **kwargs):
# Monkey patch the vif_driver if not already set
if not isinstance(compute_mgr.driver.vif_driver, VRouterVIFDriver):
compute_mgr.driver.vif_driver = \
VRouterVIFDriver(compute_mgr.driver._get_connection)
return super(ContrailNetworkAPI, self).deallocate_for_instance(*args, **kwargs)
#end
#end ContrailNetworkAPI
class VRouterVIFDriver(LibVirtVIFDriver):
"""VIF driver for VRouter when running Neutron."""
PORT_TYPE = 'NovaVMPort'
def __init__(self, get_connection):
super(VRouterVIFDriver, self).__init__(get_connection)
self._vrouter_semaphore = eventlet.semaphore.Semaphore()
self._vrouter_client = ContrailVRouterApi(doconnect=True, semaphore=self._vrouter_semaphore)
timer = loopingcall.FixedIntervalLoopingCall(self._keep_alive)
timer.start(interval=2)
def _keep_alive(self):
self._vrouter_client.periodic_connection_check()
@staticmethod
def _get_br_name(dev):
"""Returns the bridge name for a tap device.
This is lxc related stuff. To work around the fact, that libvirt does
not support direct passthrough of devices to LXC."""
return 'br%s' % dev[3:]
def _create_bridge(self, dev, instance):
"""Creating a bridge and returning its name"""
br_name = self._get_br_name(dev)
try:
linux_net.LinuxBridgeInterfaceDriver.ensure_bridge(br_name, dev)
linux_net._execute('ip', 'link', 'set', br_name, 'promisc', 'on',
run_as_root=True)
except processutils.ProcessExecutionError:
LOG.exception(_LE("Failed while plugging vif"), instance=instance)
return br_name
def get_config(self, instance, vif, image_meta, inst_type, virt_type=None):
try:
conf = super(VRouterVIFDriver, self).get_config(instance, vif,
image_meta, inst_type)
except TypeError:
conf = super(VRouterVIFDriver, self).get_base_config(instance, vif,
image_meta, inst_type, virt_type)
dev = self.get_vif_devname(vif)
if not virt_type:
try:
virt_type = cfg.CONF.libvirt.virt_type
except cfg.NoSuchOptError:
virt_type = cfg.CONF.libvirt_type
if virt_type == 'lxc':
# for lxc we need to pass a bridge to libvirt
br_name = self._get_br_name(dev)
designer.set_vif_host_backend_bridge_config(conf, br_name)
else:
designer.set_vif_host_backend_ethernet_config(conf, dev)
designer.set_vif_bandwidth_config(conf, inst_type)
return conf
def plug(self, instance, vif):
dev = self.get_vif_devname(vif)
try:
linux_net.create_tap_dev(dev)
except processutils.ProcessExecutionError:
LOG.exception(_LE("Failed while plugging vif"), instance=instance)
try:
virt_type = cfg.CONF.libvirt.virt_type
except cfg.NoSuchOptError:
virt_type = cfg.CONF.libvirt_type
if virt_type == 'lxc':
dev = self._create_bridge(dev, instance)
ipv4_address = '0.0.0.0'
ipv6_address = None
subnets = vif['network']['subnets']
for subnet in subnets:
ips = subnet['ips'][0]
if (ips['version'] == 4):
if ips['address'] is not None:
ipv4_address = ips['address']
if (ips['version'] == 6):
if ips['address'] is not None:
ipv6_address = ips['address']
kwargs = {
'ip_address': ipv4_address,
'vn_id': vif['network']['id'],
'display_name': instance['display_name'],
'hostname': instance['hostname'],
'host': instance['host'],
'vm_project_id': instance['project_id'],
'port_type': self.PORT_TYPE,
'ip6_address': ipv6_address,
}
try:
result = self._vrouter_client.add_port(instance['uuid'],
vif['id'],
dev,
vif['address'],
**kwargs)
if not result:
LOG.exception(_LE("Failed while plugging vif"),
instance=instance)
except TApplicationException:
LOG.exception(_LE("Failed while plugging vif"), instance=instance)
def unplug(self, instance, vif):
dev = self.get_vif_devname(vif)
try:
self._vrouter_client.delete_port(vif['id'])
#delegate the deletion of tap device to a deffered thread
worker_thread = threading.Thread(target=self.delete_device, \
name='contrailvif', args=(dev,))
worker_thread.start()
except (TApplicationException, processutils.ProcessExecutionError,\
RuntimeError):
LOG.exception(_LE("Failed while unplugging vif"),
instance=instance)
def delete_device(self, dev):
time.sleep(2)
LOG.debug(dev)
try:
virt_type = cfg.CONF.libvirt.virt_type
except cfg.NoSuchOptError:
virt_type = cfg.CONF.libvirt_type
if virt_type == 'lxc':
linux_net.LinuxBridgeInterfaceDriver.remove_bridge(
self._get_br_name(dev))
linux_net.delete_net_dev(dev)