~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/network/quantum/melange_ipam_lib.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-08-16 14:04:11 UTC
  • mto: This revision was merged to the branch mainline in revision 84.
  • Revision ID: package-import@ubuntu.com-20120816140411-0mr4n241wmk30t9l
Tags: upstream-2012.2~f3
ImportĀ upstreamĀ versionĀ 2012.2~f3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
 
 
3
 
# Copyright 2011 Nicira Networks, Inc
4
 
# All Rights Reserved.
5
 
#
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
9
 
#
10
 
#         http://www.apache.org/licenses/LICENSE-2.0
11
 
#
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
16
 
#    under the License.
17
 
 
18
 
import netaddr
19
 
 
20
 
from nova import db
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
25
 
 
26
 
 
27
 
LOG = logging.getLogger(__name__)
28
 
 
29
 
FLAGS = flags.FLAGS
30
 
 
31
 
 
32
 
def get_ipam_lib(net_man):
33
 
    return QuantumMelangeIPAMLib()
34
 
 
35
 
 
36
 
class QuantumMelangeIPAMLib(object):
37
 
    """Implements Quantum IP Address Management (IPAM) interface
38
 
       using the Melange service, which is access using the Melange
39
 
       web services API.
40
 
    """
41
 
 
42
 
    def __init__(self):
43
 
        """Initialize class used to connect to Melange server"""
44
 
        self.m_conn = melange_connection.MelangeConnection()
45
 
 
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
51
 
           IPv4 or IPv6 subnets.
52
 
 
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)
57
 
        """
58
 
        tenant_id = project_id or FLAGS.quantum_default_tenant_id
59
 
        if cidr:
60
 
            self.m_conn.create_block(quantum_net_id, cidr,
61
 
                                     project_id=tenant_id,
62
 
                                     gateway=gateway,
63
 
                                     dns1=dns1, dns2=dns2)
64
 
        if cidr_v6:
65
 
            self.m_conn.create_block(quantum_net_id, cidr_v6,
66
 
                                     project_id=tenant_id,
67
 
                                     gateway=gateway_v6,
68
 
                                     dns1=dns1, dns2=dns2)
69
 
 
70
 
        net = {"uuid": quantum_net_id,
71
 
               "project_id": tenant_id,
72
 
               "priority": priority,
73
 
               "label": label}
74
 
        if FLAGS.quantum_use_dhcp:
75
 
            if cidr:
76
 
                n = netaddr.IPNetwork(cidr)
77
 
                net['dhcp_start'] = netaddr.IPAddress(n.first + 2)
78
 
        else:
79
 
            net['dhcp_start'] = None
80
 
        admin_context = context.elevated()
81
 
        network = db.network_create_safe(admin_context, net)
82
 
 
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,
88
 
                                      vif_ref['address'])
89
 
        return [ip['address'] for ip in ips]
90
 
 
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.
94
 
        """
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)
101
 
 
102
 
        network = db.network_get_by_uuid(admin_context, net_id)
103
 
        db.network_delete_safe(context, network['id'])
104
 
 
105
 
    def get_networks_by_tenant(self, admin_context, tenant_id):
106
 
        nets = {}
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
112
 
        return nets.values()
113
 
 
114
 
    def get_global_networks(self, admin_context):
115
 
        return self.get_networks_by_tenant(admin_context,
116
 
            FLAGS.quantum_default_tenant_id)
117
 
 
118
 
    def get_project_networks(self, admin_context):
119
 
        try:
120
 
            nets = db.network_get_all(admin_context.elevated())
121
 
        except exception.NoNetworksFound:
122
 
            return []
123
 
        # only return networks with a project_id set
124
 
        return [net for net in nets if net['project_id']]
125
 
 
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).
131
 
        """
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"))
135
 
 
136
 
        admin_context = context.elevated()
137
 
 
138
 
        # Decorate with priority
139
 
        priority_nets = []
140
 
        for tenant_id in (project_id, FLAGS.quantum_default_tenant_id):
141
 
            nets = self.get_networks_by_tenant(admin_context, tenant_id)
142
 
            for network in nets:
143
 
                priority = network['priority']
144
 
                priority_nets.append((priority, network['uuid'], tenant_id))
145
 
 
146
 
        # Sort by priority
147
 
        priority_nets.sort()
148
 
 
149
 
        # Undecorate
150
 
        return [(network_id, tenant_id)
151
 
                for priority, network_id, tenant_id in priority_nets]
152
 
 
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:
159
 
            try:
160
 
                self.m_conn.get_allocated_ips(net_id, vif_id, tid)
161
 
            except Exception:
162
 
                continue
163
 
            ipam_tenant_id = tid
164
 
            break
165
 
        return ipam_tenant_id
166
 
 
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.
173
 
        """
174
 
        subnets = []
175
 
        ips = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id)
176
 
 
177
 
        for ip_address in ips:
178
 
            block = ip_address['ip_block']
179
 
            subnet = {'network_id': block['network_id'],
180
 
                      'id': block['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
189
 
            else:
190
 
                subnet['version'] = 6
191
 
            subnets.append(subnet)
192
 
        return subnets
193
 
 
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)
197
 
 
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.
201
 
        """
202
 
        return self._get_ips_by_interface(context, net_id, vif_id,
203
 
                                                        project_id, 4)
204
 
 
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.
208
 
        """
209
 
        return self._get_ips_by_interface(context, net_id, vif_id,
210
 
                                                        project_id, 6)
211
 
 
212
 
    def _get_ips_by_interface(self, context, net_id, vif_id, project_id,
213
 
                              ip_version):
214
 
        """Helper method to fetch v4 or v6 addresses for a particular
215
 
           virtual interface.
216
 
        """
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]
221
 
 
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]
226
 
 
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.
230
 
        """
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:
239
 
                return True
240
 
        return False
241
 
 
242
 
    def deallocate_ips_by_vif(self, context, project_id, net_id, vif_ref):
243
 
        """Deallocate all fixed IPs associated with the specified
244
 
           virtual interface.
245
 
        """
246
 
        tenant_id = project_id or FLAGS.quantum_default_tenant_id
247
 
        self.m_conn.deallocate_ips(net_id, vif_ref['uuid'], tenant_id)
248
 
 
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]
252
 
 
253
 
    def create_vif(self, vif_id, instance_id, project_id=None):
254
 
        """Create a new vif with the specified information.
255
 
        """
256
 
        tenant_id = project_id or FLAGS.quantum_default_tenant_id
257
 
        return self.m_conn.create_vif(vif_id, instance_id, tenant_id)
258
 
 
259
 
    def get_floating_ips_by_fixed_address(self, context, fixed_address):
260
 
        """This call is not supported in quantum yet"""
261
 
        return []