1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2011 Nicira Networks, Inc
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7
# not use this file except in compliance with the License. You may obtain
8
# a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
# License for the specific language governing permissions and limitations
21
from nova import exception
22
from nova import flags
23
from nova.network.quantum import melange_connection
24
from nova.openstack.common import log as logging
27
LOG = logging.getLogger(__name__)
32
def get_ipam_lib(net_man):
33
return QuantumMelangeIPAMLib()
36
class QuantumMelangeIPAMLib(object):
37
"""Implements Quantum IP Address Management (IPAM) interface
38
using the Melange service, which is access using the Melange
43
"""Initialize class used to connect to Melange server"""
44
self.m_conn = melange_connection.MelangeConnection()
46
def create_subnet(self, context, label, project_id,
47
quantum_net_id, priority, cidr=None,
48
gateway=None, gateway_v6=None, cidr_v6=None,
49
dns1=None, dns2=None):
50
"""Contact Melange and create a subnet for any non-NULL
53
Also create an entry in the Nova networks DB, but only
54
to store values not represented in Melange or to
55
temporarily provide compatibility with Nova code that
56
accesses IPAM data directly via the DB (e.g., nova-api)
58
tenant_id = project_id or FLAGS.quantum_default_tenant_id
60
self.m_conn.create_block(quantum_net_id, cidr,
65
self.m_conn.create_block(quantum_net_id, cidr_v6,
70
net = {"uuid": quantum_net_id,
71
"project_id": tenant_id,
74
if FLAGS.quantum_use_dhcp:
76
n = netaddr.IPNetwork(cidr)
77
net['dhcp_start'] = netaddr.IPAddress(n.first + 2)
79
net['dhcp_start'] = None
80
admin_context = context.elevated()
81
network = db.network_create_safe(admin_context, net)
83
def allocate_fixed_ips(self, context, project_id, quantum_net_id,
84
network_tenant_id, vif_ref):
85
"""Pass call to allocate fixed IP on to Melange"""
86
ips = self.m_conn.allocate_ip(quantum_net_id, network_tenant_id,
87
vif_ref['uuid'], project_id,
89
return [ip['address'] for ip in ips]
91
def delete_subnets_by_net_id(self, context, net_id, project_id):
92
"""Find Melange block associated with the Quantum UUID,
93
then tell Melange to delete that block.
95
admin_context = context.elevated()
96
tenant_id = project_id or FLAGS.quantum_default_tenant_id
97
all_blocks = self.m_conn.get_blocks(tenant_id)
98
for b in all_blocks['ip_blocks']:
99
if b['network_id'] == net_id:
100
self.m_conn.delete_block(b['id'], tenant_id)
102
network = db.network_get_by_uuid(admin_context, net_id)
103
db.network_delete_safe(context, network['id'])
105
def get_networks_by_tenant(self, admin_context, tenant_id):
107
blocks = self.m_conn.get_blocks(tenant_id)
108
for ip_block in blocks['ip_blocks']:
109
network_id = ip_block['network_id']
110
network = db.network_get_by_uuid(admin_context, network_id)
111
nets[network_id] = network
114
def get_global_networks(self, admin_context):
115
return self.get_networks_by_tenant(admin_context,
116
FLAGS.quantum_default_tenant_id)
118
def get_project_networks(self, admin_context):
120
nets = db.network_get_all(admin_context.elevated())
121
except exception.NoNetworksFound:
123
# only return networks with a project_id set
124
return [net for net in nets if net['project_id']]
126
def get_project_and_global_net_ids(self, context, project_id):
127
"""Fetches all networks associated with this project, or
128
that are "global" (i.e., have no project set).
129
Returns list sorted by 'priority' (lowest integer value
130
is highest priority).
132
if project_id is None:
133
raise Exception(_("get_project_and_global_net_ids must be called"
134
" with a non-null project_id"))
136
admin_context = context.elevated()
138
# Decorate with priority
140
for tenant_id in (project_id, FLAGS.quantum_default_tenant_id):
141
nets = self.get_networks_by_tenant(admin_context, tenant_id)
143
priority = network['priority']
144
priority_nets.append((priority, network['uuid'], tenant_id))
150
return [(network_id, tenant_id)
151
for priority, network_id, tenant_id in priority_nets]
153
def get_tenant_id_by_net_id(self, context, net_id, vif_id, project_id):
154
ipam_tenant_id = None
155
tenant_ids = [FLAGS.quantum_default_tenant_id, project_id, None]
156
# This is confusing, if there are IPs for the given net, vif,
157
# tenant trifecta we assume that is the tenant for that network
158
for tid in tenant_ids:
160
self.m_conn.get_allocated_ips(net_id, vif_id, tid)
165
return ipam_tenant_id
167
# TODO(bgh): Rename this method .. it's now more of a
168
# "get_subnets_by_net_id_and_vif_id" method, but we could probably just
169
# call it "get_subnets".
170
def get_subnets_by_net_id(self, context, tenant_id, net_id, vif_id):
171
"""Returns information about the IPv4 and IPv6 subnets
172
associated with a Quantum Network UUID.
175
ips = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id)
177
for ip_address in ips:
178
block = ip_address['ip_block']
179
subnet = {'network_id': block['network_id'],
181
'cidr': block['cidr'],
182
'gateway': block['gateway'],
183
'broadcast': block['broadcast'],
184
'netmask': block['netmask'],
185
'dns1': block['dns1'],
186
'dns2': block['dns2']}
187
if ip_address['version'] == 4:
188
subnet['version'] = 4
190
subnet['version'] = 6
191
subnets.append(subnet)
194
def get_routes_by_ip_block(self, context, block_id, project_id):
195
"""Returns the list of routes for the IP block"""
196
return self.m_conn.get_routes(block_id, project_id)
198
def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id):
199
"""Returns a list of IPv4 address strings associated with
200
the specified virtual interface.
202
return self._get_ips_by_interface(context, net_id, vif_id,
205
def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id):
206
"""Returns a list of IPv6 address strings associated with
207
the specified virtual interface.
209
return self._get_ips_by_interface(context, net_id, vif_id,
212
def _get_ips_by_interface(self, context, net_id, vif_id, project_id,
214
"""Helper method to fetch v4 or v6 addresses for a particular
217
tenant_id = project_id or FLAGS.quantum_default_tenant_id
218
ip_list = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id)
219
return [ip['address'] for ip in ip_list
220
if netaddr.IPNetwork(ip['address']).version == ip_version]
222
def get_instance_ids_by_ip_address(self, context, address):
223
ips = self.m_conn.get_allocated_ips_by_address(address)
224
# TODO(aaron.lee): melange should be storing & returning instance_uuid!
225
return [ip.get('used_by_device') for ip in ips]
227
def verify_subnet_exists(self, context, project_id, quantum_net_id):
228
"""Confirms that a subnet exists that is associated with the
229
specified Quantum Network UUID.
231
# TODO(bgh): Would be nice if we could just do something like:
232
# GET /ipam/tenants/{tenant_id}/networks/{network_id}/ instead
233
# of searching through all the blocks. Checking for a 404
234
# will then determine whether it exists.
235
tenant_id = project_id or FLAGS.quantum_default_tenant_id
236
all_blocks = self.m_conn.get_blocks(tenant_id)
237
for b in all_blocks['ip_blocks']:
238
if b['network_id'] == quantum_net_id:
242
def deallocate_ips_by_vif(self, context, project_id, net_id, vif_ref):
243
"""Deallocate all fixed IPs associated with the specified
246
tenant_id = project_id or FLAGS.quantum_default_tenant_id
247
self.m_conn.deallocate_ips(net_id, vif_ref['uuid'], tenant_id)
249
def get_allocated_ips(self, context, subnet_id, project_id):
250
ips = self.m_conn.get_allocated_ips_for_network(subnet_id, project_id)
251
return [(ip['address'], ip['interface_id']) for ip in ips]
253
def create_vif(self, vif_id, instance_id, project_id=None):
254
"""Create a new vif with the specified information.
256
tenant_id = project_id or FLAGS.quantum_default_tenant_id
257
return self.m_conn.create_vif(vif_id, instance_id, tenant_id)
259
def get_floating_ips_by_fixed_address(self, context, fixed_address):
260
"""This call is not supported in quantum yet"""