Merge lp:~midokura/nova/network-service into lp:~ntt-pf-lab/nova/network-service

Proposed by Ryu Ishimoto
Status: Merged
Merge reported by: Ryu Ishimoto
Merged at revision: not available
Proposed branch: lp:~midokura/nova/network-service
Merge into: lp:~ntt-pf-lab/nova/network-service
Diff against target: 1969 lines (+633/-357)
13 files modified
bin/nova-net-flat-vlan-manage (+192/-0)
nova/api/ec2/cloud.py (+31/-9)
nova/compute/api.py (+0/-6)
nova/network/flat_vlan/api/__init__.py (+93/-7)
nova/network/flat_vlan/common.py (+12/-0)
nova/network/flat_vlan/compute.py (+7/-18)
nova/network/flat_vlan/db/__init__.py (+1/-0)
nova/network/flat_vlan/db/api.py (+84/-51)
nova/network/flat_vlan/db/sqlalchemy/api.py (+14/-64)
nova/network/flat_vlan/db/sqlalchemy/models.py (+48/-84)
nova/network/flat_vlan/network.py (+87/-66)
nova/network/service.py (+63/-48)
nova/virt/libvirt_conn.py (+1/-4)
To merge this branch: bzr merge lp:~midokura/nova/network-service
Reviewer Review Type Date Requested Status
NTT PF Lab. Pending
Review via email: mp+56616@code.launchpad.net

Commit message

Added EC2 floating IP API support.

Description of the change

Added EC2 floating IP API support.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'bin/nova-net-flat-vlan-manage'
--- bin/nova-net-flat-vlan-manage 1970-01-01 00:00:00 +0000
+++ bin/nova-net-flat-vlan-manage 2011-04-06 17:14:53 +0000
@@ -0,0 +1,192 @@
1#!/usr/bin/env python
2# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
4# Copyright 2011 Midokura KK
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19# Interactive shell based on Django:
20#
21# Copyright (c) 2005, the Lawrence Journal-World
22# All rights reserved.
23#
24# Redistribution and use in source and binary forms, with or without
25# modification, are permitted provided that the following conditions are met:
26#
27# 1. Redistributions of source code must retain the above copyright notice,
28# this list of conditions and the following disclaimer.
29#
30# 2. Redistributions in binary form must reproduce the above copyright
31# notice, this list of conditions and the following disclaimer in the
32# documentation and/or other materials provided with the distribution.
33#
34# 3. Neither the name of Django nor the names of its contributors may be
35# used to endorse or promote products derived from this software without
36# specific prior written permission.
37#
38# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
39# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
40# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
41# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
42# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
44# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
48# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49
50
51"""
52 CLI interface for nova flat vlan network management.
53"""
54
55import gettext
56import os
57import sys
58
59import IPy
60
61# If ../nova/__init__.py exists, add ../ to Python search path, so that
62# it will override what happens to be installed in /usr/(local/)lib/python...
63possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
64 os.pardir,
65 os.pardir))
66if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
67 sys.path.insert(0, possible_topdir)
68
69gettext.install('nova', unicode=1)
70
71from nova import context
72from nova import db as nova_db
73from nova import exception
74from nova import flags as nova_flags
75from nova import log as logging
76from nova import utils
77
78from nova.network.flat_vlan import db
79
80NOVA_FLAGS = nova_flags.FLAGS
81
82class FloatingIpCommands(object):
83 """Class for managing floating ip."""
84
85 def create(self, host, range):
86 """Creates floating ips for host by range
87 arguments: host ip_range"""
88 for address in IPy.IP(range):
89 db.floating_ip_create(context.get_admin_context(),
90 {'address': str(address),
91 'host': host})
92
93 def delete(self, ip_range):
94 """Deletes floating ips by range
95 arguments: range"""
96 for address in IPy.IP(ip_range):
97 db.floating_ip_destroy(context.get_admin_context(),
98 str(address))
99
100 def list(self, host=None):
101 """Lists all floating ips (optionally by host)
102 arguments: [host]"""
103 ctxt = context.get_admin_context()
104 if host == None:
105 floating_ips = db.floating_ip_get_all(ctxt)
106 else:
107 floating_ips = db.floating_ip_get_all_by_host(ctxt, host)
108 for floating_ip in floating_ips:
109 instance_id = None
110 fixed_ip = floating_ip['fixed_ip']
111 if fixed_ip and fixed_ip['ethernet_card_id']:
112 vnic_id = fixed_ip['ethernet_card_id']
113 instance = nova_db.instance_get_by_virtual_nic(ctxt,
114 vnic_id)
115 instance_id = instance['ec2_id']
116 print "%s\t%s\t%s" % (floating_ip['host'],
117 floating_ip['address'],
118 instance_id)
119
120
121CATEGORIES = [
122 ('floating', FloatingIpCommands)]
123
124def lazy_match(name, key_value_tuples):
125 """Finds all objects that have a key that case insensitively contains
126 [name] key_value_tuples is a list of tuples of the form (key, value)
127 returns a list of tuples of the form (key, value)"""
128 result = []
129 for (k, v) in key_value_tuples:
130 if k.lower().find(name.lower()) == 0:
131 result.append((k, v))
132 if len(result) == 0:
133 print "%s does not match any options:" % name
134 for k, _v in key_value_tuples:
135 print "\t%s" % k
136 sys.exit(2)
137 if len(result) > 1:
138 print "%s matched multiple options:" % name
139 for k, _v in result:
140 print "\t%s" % k
141 sys.exit(2)
142 return result
143
144def methods_of(obj):
145 """Get all callable methods of an object that don't start with underscore
146 returns a list of tuples of the form (method_name, method)"""
147 result = []
148 for i in dir(obj):
149 if callable(getattr(obj, i)) and not i.startswith('_'):
150 result.append((i, getattr(obj, i)))
151 return result
152
153
154def main():
155 """Parse options and call the appropriate class/method."""
156 utils.default_flagfile()
157 argv = NOVA_FLAGS(sys.argv)
158 logging.setup()
159
160 script_name = argv.pop(0)
161 if len(argv) < 1:
162 print script_name + " category action [<args>]"
163 print "Available categories:"
164 for k, _ in CATEGORIES:
165 print "\t%s" % k
166 sys.exit(2)
167 category = argv.pop(0)
168 matches = lazy_match(category, CATEGORIES)
169 # instantiate the command group object
170 category, fn = matches[0]
171 command_object = fn()
172 actions = methods_of(command_object)
173 if len(argv) < 1:
174 print script_name + " category action [<args>]"
175 print "Available actions for %s category:" % category
176 for k, _v in actions:
177 print "\t%s" % k
178 sys.exit(2)
179 action = argv.pop(0)
180 matches = lazy_match(action, actions)
181 action, fn = matches[0]
182 # call the action with the remaining arguments
183 try:
184 fn(*argv)
185 sys.exit(0)
186 except TypeError:
187 print "Possible wrong number of arguments supplied"
188 print "%s %s: %s" % (category, action, fn.__doc__)
189 raise
190
191if __name__ == '__main__':
192 main()
0193
=== modified file 'nova/api/ec2/cloud.py'
--- nova/api/ec2/cloud.py 2011-03-24 20:20:15 +0000
+++ nova/api/ec2/cloud.py 2011-04-06 17:14:53 +0000
@@ -36,7 +36,8 @@
36from nova import exception36from nova import exception
37from nova import flags37from nova import flags
38from nova import log as logging38from nova import log as logging
39from nova import network39from nova.network import service as net_service
40from nova import quota
40from nova import utils41from nova import utils
41from nova import volume42from nova import volume
42from nova.api.ec2 import ec2utils43from nova.api.ec2 import ec2utils
@@ -82,10 +83,9 @@
82"""83"""
83 def __init__(self):84 def __init__(self):
84 self.image_service = s3.S3ImageService()85 self.image_service = s3.S3ImageService()
85 self.network_api = network.API()
86 self.volume_api = volume.API()86 self.volume_api = volume.API()
87 self.compute_api = compute.API(87 self.compute_api = compute.API(
88 network_api=self.network_api,88 network_api=None,
89 volume_api=self.volume_api,89 volume_api=self.volume_api,
90 hostname_factory=ec2utils.id_to_ec2_id)90 hostname_factory=ec2utils.id_to_ec2_id)
91 self.setup()91 self.setup()
@@ -767,26 +767,48 @@
767767
768 def allocate_address(self, context, **kwargs):768 def allocate_address(self, context, **kwargs):
769 LOG.audit(_("Allocate address"), context=context)769 LOG.audit(_("Allocate address"), context=context)
770 public_ip = self.network_api.allocate_floating_ip(context)770 _, net_factory = net_service.get_service_factory(context,
771 context.project_id)
772 net_api_service = net_factory.get_api_service()
773 ip_quota = quota.get_quota(context, context.project_id)['floating_ips']
774 try:
775 public_ip = net_api_service.allocate_address(context,
776 context.project_id,
777 ip_quota)
778 except quota.QuotaError, ex:
779 LOG.warn(_("Quota exceeded for %s, tried to allocate "
780 "address"),
781 context.project_id)
782 raise ex
783
771 return {'addressSet': [{'publicIp': public_ip}]}784 return {'addressSet': [{'publicIp': public_ip}]}
772785
773 def release_address(self, context, public_ip, **kwargs):786 def release_address(self, context, public_ip, **kwargs):
774 LOG.audit(_("Release address %s"), public_ip, context=context)787 LOG.audit(_("Release address %s"), public_ip, context=context)
775 self.network_api.release_floating_ip(context, address=public_ip)788 _, net_factory = net_service.get_service_factory(context,
789 context.project_id)
790 net_api_service = net_factory.get_api_service()
791
792 net_api_service.deallocate_address(context, public_ip)
776 return {'releaseResponse': ["Address released."]}793 return {'releaseResponse': ["Address released."]}
777794
778 def associate_address(self, context, instance_id, public_ip, **kwargs):795 def associate_address(self, context, instance_id, public_ip, **kwargs):
779 LOG.audit(_("Associate address %(public_ip)s to"796 LOG.audit(_("Associate address %(public_ip)s to"
780 " instance %(instance_id)s") % locals(), context=context)797 " instance %(instance_id)s") % locals(), context=context)
781 instance_id = ec2utils.ec2_id_to_id(instance_id)798 instance_id = ec2utils.ec2_id_to_id(instance_id)
782 self.compute_api.associate_floating_ip(context,799 vnic_ids = db.virtual_nics_get_by_instance(context, instance_id)
783 instance_id=instance_id,800 _, net_factory = net_service.get_service_factory(context,
784 address=public_ip)801 context.project_id)
802 net_api_service = net_factory.get_api_service()
803 net_api_service.associate_address(context, vnic_ids[0], public_ip)
785 return {'associateResponse': ["Address associated."]}804 return {'associateResponse': ["Address associated."]}
786805
787 def disassociate_address(self, context, public_ip, **kwargs):806 def disassociate_address(self, context, public_ip, **kwargs):
788 LOG.audit(_("Disassociate address %s"), public_ip, context=context)807 LOG.audit(_("Disassociate address %s"), public_ip, context=context)
789 self.network_api.disassociate_floating_ip(context, address=public_ip)808 _, net_factory = net_service.get_service_factory(context,
809 context.project_id)
810 net_api_service = net_factory.get_api_service()
811 net_api_service.disassociate_address(context, public_ip)
790 return {'disassociateResponse': ["Address disassociated."]}812 return {'disassociateResponse': ["Address disassociated."]}
791813
792 def run_instances(self, context, **kwargs):814 def run_instances(self, context, **kwargs):
793815
=== modified file 'nova/compute/api.py'
--- nova/compute/api.py 2011-03-30 08:30:38 +0000
+++ nova/compute/api.py 2011-04-06 17:14:53 +0000
@@ -681,12 +681,6 @@
681 "volume_id": volume_id}})681 "volume_id": volume_id}})
682 return instance682 return instance
683683
684 def associate_floating_ip(self, context, instance_id, address):
685 instance = self.get(context, instance_id)
686 self.network_api.associate_floating_ip(context,
687 floating_ip=address,
688 fixed_ip=instance['fixed_ip'])
689
690 def get_instance_metadata(self, context, instance_id):684 def get_instance_metadata(self, context, instance_id):
691 """Get all metadata associated with an instance."""685 """Get all metadata associated with an instance."""
692 rv = self.db.instance_metadata_get(context, instance_id)686 rv = self.db.instance_metadata_get(context, instance_id)
693687
=== modified file 'nova/network/flat_vlan/api/__init__.py'
--- nova/network/flat_vlan/api/__init__.py 2011-03-31 07:15:23 +0000
+++ nova/network/flat_vlan/api/__init__.py 2011-04-06 17:14:53 +0000
@@ -16,19 +16,26 @@
16# under the License.16# under the License.
17from zope import interface17from zope import interface
1818
19from nova import exception
20from nova import quota
21from nova import rpc
19from nova.network import service22from nova.network import service
23from nova.network.flat_vlan import common
24from nova.network.flat_vlan import flags
20from nova.network.flat_vlan import manager25from nova.network.flat_vlan import manager
21from nova.network.flat_vlan.db import api as db_api26from nova.network.flat_vlan import db
27
28FLAGS = flags.FlagAccessor()
2229
23class NetworkApiService(object):30class NetworkApiService(object):
24 """Network API Service for this plugin."""31 """Network API Service for this plugin."""
25 32
26 interface.implements(service.INetworkApiService)33 interface.implements(service.INetworkApiService)
2734
28 def create_vnic(self, context):35 def create_vnic(self, context):
29 """Generic API to retrieve the default VNIC ID.36 """Generic API to retrieve the default VNIC ID.
30 For flat simple network, create a new vNIC and return its ID.37 For flat simple network, create a new vNIC and return its ID.
31 38
32 Args:39 Args:
33 context: Nova context object needed to access the DB.40 context: Nova context object needed to access the DB.
3441
@@ -38,20 +45,99 @@
38 # Create a new VNIC45 # Create a new VNIC
39 vnic = manager.ethernet_card_create_with_random_mac(context)46 vnic = manager.ethernet_card_create_with_random_mac(context)
40 return vnic.id47 return vnic.id
41 48
42 def get_project_vpn_address_and_port(self, context, project_id):49 def get_project_vpn_address_and_port(self, context, project_id):
43 """Generic API to get network for a given project.50 """Generic API to get network for a given project.
44 51
45 Args:52 Args:
46 context: Nova context object needed to access the DB.53 context: Nova context object needed to access the DB.
47 project_id: project to get the VPN IP and port for.54 project_id: project to get the VPN IP and port for.
48 55
49 Returns:56 Returns:
50 A tuple of VPN IP address and port number.57 A tuple of VPN IP address and port number.
51 """58 """
52 network = db_api.network_get_by_project(context, project_id)59 network = db.network_get_by_project(context, project_id)
53 if network:60 if network:
54 return (network['vpn_public_address'], network['vpn_public_port'])61 return (network['vpn_public_address'], network['vpn_public_port'])
55 else:62 else:
56 return (None, None)63 return (None, None)
5764
65 def allocate_address(self, context, project_id, ip_quota):
66 """Gets the number of floating IPs associated with a project.
67
68 Args:
69 context: Nova context object needed to access the DB.
70 project_id: Project to allocate the address from.
71 ip_quota: Quota for IP addresses.
72
73 Returns:
74 An IP address.
75
76 Raises:
77 quota.QuotaError: Over the quota limit.
78 """
79 used_floating_ips = db.floating_ip_count_by_project(context,
80 project_id)
81 allowed_floating_ips = min(1, ip_quota - used_floating_ips)
82 if allowed_floating_ips < 1:
83 raise quota.QuotaError(_("Quota exceeeded for %s, tried to allocate"
84 " address"), project_id)
85
86 # Let the network service handle this because the DB update requires the
87 # network service host name.
88 host = rpc.call(context,
89 FLAGS.net_flat_vlan_network_topic,
90 {"method": "get_host"})
91
92 ip = db.floating_ip_allocate_address(context, host, project_id)
93 return ip.address
94
95 def deallocate_address(self, context, floating_address):
96 """Deallocates the public IP address by removing it from any
97 project.
98
99 Args:
100 context: nova context object needed to access the DB.
101 address: Public IP address to deallocate.
102 """
103 db.floating_ip_deallocate(context, floating_address)
104
105 def associate_address(self, context, vnic_id, floating_address):
106 """Associates a floating address to the fixed IP address of the vnic.
107
108 Args:
109 context: Nova context object
110 vnic_id: virtual NIC ID
111 floating_address: public IP address to assign to the VNIC.
112 """
113 fixed_ip = db.fixed_ip_get_by_ethernet_card(context, vnic_id)
114 floating_ip = db.floating_ip_get_by_address(context, floating_address)
115 db.floating_ip_fixed_ip_associate(context, floating_ip['address'],
116 fixed_ip['address'])
117
118 host = fixed_ip['network']['host']
119 rpc.cast(context,
120 common.queue_get_for(FLAGS.net_flat_vlan_network_topic,
121 host),
122 {"method": "activate_public_ip",
123 "args": {"floating_address": floating_ip['address'],
124 "fixed_address": fixed_ip['address']}})
125
126 def disassociate_address(self, context, floating_address):
127 """Diassociates public IP.
128
129 Args:
130 context: Nova context object
131 floating_address: public IP address
132 """
133 floating_ip = db.floating_ip_get_by_address(context, floating_address)
134 if not floating_ip.get('fixed_ip'):
135 raise exception.ApiError('Address is not associated.')
136 fixed_ip = db.floating_ip_disassociate(context, floating_address)
137 host = fixed_ip['network']['host']
138 rpc.cast(context,
139 common.queue_get_for(FLAGS.net_flat_vlan_network_topic,
140 host),
141 {"method": "deactivate_public_ip",
142 "args": {"floating_address": floating_ip['address'],
143 "fixed_address": fixed_ip['address']}})
58144
=== modified file 'nova/network/flat_vlan/common.py'
--- nova/network/flat_vlan/common.py 2011-03-31 09:36:57 +0000
+++ nova/network/flat_vlan/common.py 2011-04-06 17:14:53 +0000
@@ -44,3 +44,15 @@
44 """44 """
45 net = IPy.IP(cidr)45 net = IPy.IP(cidr)
46 return str(net.net()), str(net.netmask())46 return str(net.net()), str(net.netmask())
47
48def queue_get_for(topic, physical_node_id):
49 """Gets the queue name for RPC.
50
51 Args:
52 topic: Topic to listen to
53 physical_node_id: Node ID to listen to.
54
55 Returns:
56 The queue name to send the RPC to.
57 """
58 return "%s.%s" % (topic, physical_node_id)
47\ No newline at end of file59\ No newline at end of file
4860
=== modified file 'nova/network/flat_vlan/compute.py'
--- nova/network/flat_vlan/compute.py 2011-04-04 09:52:18 +0000
+++ nova/network/flat_vlan/compute.py 2011-04-06 17:14:53 +0000
@@ -26,18 +26,6 @@
2626
27FLAGS = flags.FlagAccessor()27FLAGS = flags.FlagAccessor()
2828
29def _queue_get_for(topic, physical_node_id):
30 """Gets the queue name for RPC.
31
32 Args:
33 topic: Topic to listen to
34 physical_node_id: Node ID to listen to.
35
36 Returns:
37 The queue name to send the RPC to.
38 """
39 return "%s.%s" % (topic, physical_node_id)
40
41def _get_network_topic(context, **kwargs):29def _get_network_topic(context, **kwargs):
42 """Retrieves the network host for a project on this host30 """Retrieves the network host for a project on this host
43 31
@@ -51,8 +39,8 @@
51 host = FLAGS.net_flat_vlan_network_host39 host = FLAGS.net_flat_vlan_network_host
52 else:40 else:
53 host = _get_network_host(context)41 host = _get_network_host(context)
54 return _queue_get_for(FLAGS.net_flat_vlan_network_topic,42 return common.queue_get_for(FLAGS.net_flat_vlan_network_topic,
55 host)43 host)
5644
57def _set_network_host(context, network_id):45def _set_network_host(context, network_id):
58 """Safely sets the host of the network.46 """Safely sets the host of the network.
@@ -87,8 +75,8 @@
87 FLAGS.net_flat_vlan_network_bridge)75 FLAGS.net_flat_vlan_network_bridge)
88 host = network_ref['host']76 host = network_ref['host']
89 if not host:77 if not host:
90 topic = _queue_get_for(FLAGS.net_flat_vlan_network_topic,78 topic = common.queue_get_for(FLAGS.net_flat_vlan_network_topic,
91 FLAGS.net_flat_vlan_network_host)79 FLAGS.net_flat_vlan_network_host)
92 if FLAGS.net_flat_vlan_network_fake_call:80 if FLAGS.net_flat_vlan_network_fake_call:
93 return _set_network_host(context, network_ref['id'])81 return _set_network_host(context, network_ref['id'])
94 host = rpc.call(context,82 host = rpc.call(context,
@@ -146,7 +134,8 @@
146 vnic_ids: list of VNIC IDs134 vnic_ids: list of VNIC IDs
147 """135 """
148 for vnic_id in vnic_ids:136 for vnic_id in vnic_ids:
149 fixed_ip = vnic_id.get('fixed_ip')137 vnic_ref = db_api.ethernet_card_get(context, vnic_id)
138 fixed_ip = vnic_ref.get('fixed_ip')
150 if not FLAGS.stub_network and fixed_ip:139 if not FLAGS.stub_network and fixed_ip:
151 140
152 if (FLAGS.net_flat_vlan_use_vlan or 141 if (FLAGS.net_flat_vlan_use_vlan or
@@ -156,7 +145,7 @@
156 for floating_ip in floating_ips:145 for floating_ip in floating_ips:
157 address = floating_ip['address']146 address = floating_ip['address']
158 147
159 network_topic = _queue_get_for(context,148 network_topic = common.queue_get_for(context,
160 FLAGS.net_flat_vlan_network_topic,149 FLAGS.net_flat_vlan_network_topic,
161 floating_ip['host'])150 floating_ip['host'])
162 151
163152
=== modified file 'nova/network/flat_vlan/db/__init__.py'
--- nova/network/flat_vlan/db/__init__.py 2011-03-31 06:18:29 +0000
+++ nova/network/flat_vlan/db/__init__.py 2011-04-06 17:14:53 +0000
@@ -18,3 +18,4 @@
18DB abstraction for Flat VLAN network service 18DB abstraction for Flat VLAN network service
19"""19"""
2020
21from nova.network.flat_vlan.db.api import *
2122
=== modified file 'nova/network/flat_vlan/db/api.py'
--- nova/network/flat_vlan/db/api.py 2011-04-02 19:01:17 +0000
+++ nova/network/flat_vlan/db/api.py 2011-04-06 17:14:53 +0000
@@ -19,85 +19,95 @@
1919
20The underlying driver is loaded as a :class:`LazyPluggable`.20The underlying driver is loaded as a :class:`LazyPluggable`.
21"""21"""
22from nova import exception
22from nova import utils23from nova import utils
24from nova.db.sqlalchemy import api as nova_api
23from nova.network.flat_vlan import flags25from nova.network.flat_vlan import flags
2426
25FLAGS = flags.FlagAccessor()27FLAGS = flags.FlagAccessor()
2628
27IMPL = utils.LazyPluggable(FLAGS.get('net_flat_vlan_db_backend'),29IMPL = utils.LazyPluggable(
28 sqlalchemy='nova.network.flat_vlan.db.sqlalchemy.api')30 FLAGS.get('net_flat_vlan_db_backend'),
31 sqlalchemy='nova.network.flat_vlan.db.sqlalchemy.api')
2932
30###################33###################
3134
32def ethernet_card_get(context, id):35def ethernet_card_get(context, id):
33 """Get ethernet card by id."""36 """Get ethernet card by id."""
34 return IMPL.dao_factory.get_dao(context).\37 return IMPL.dao_factory.get_dao().\
35 ethernet_card_get(id)38 ethernet_card_get(id)
3639
37def ethernet_card_create(context, values):40def ethernet_card_create(context, values):
38 """Create a new ethernet card."""41 """Create a new ethernet card."""
39 return IMPL.dao_factory.get_dao(context).\42 return IMPL.dao_factory.get_dao().\
40 ethernet_card_create(values)43 ethernet_card_create(values)
4144
42def ethernet_card_get_all(context):45def ethernet_card_get_all(context):
43 """Get all ethernet cards."""46 """Get all ethernet cards."""
44 return IMPL.dao_factory.get_dao(context).\47 can_read_deleted = nova_api.can_read_deleted(context)
45 ethernet_card_get_all()48 return IMPL.dao_factory.get_dao().\
49 ethernet_card_get_all(read_deleted=can_read_deleted)
4650
47def ethernet_card_update(context, ethernet_card_id, values):51def ethernet_card_update(context, ethernet_card_id, values):
48 """Update ethernet card.""" 52 """Update ethernet card."""
49 return IMPL.dao_factory.get_dao(context).\53 return IMPL.dao_factory.get_dao().\
50 ethernet_card_update(ethernet_card_id, values)54 ethernet_card_update(ethernet_card_id, values)
5155
52def ethernet_card_delete(context, ethernet_card_id):56def ethernet_card_delete(context, ethernet_card_id):
53 """Delete ethernet card."""57 """Delete ethernet card."""
54 return IMPL.dao_factory.get_dao(context).\58 return IMPL.dao_factory.get_dao().\
55 ethernet_card_delete(ethernet_card_id)59 ethernet_card_delete(ethernet_card_id)
5660
57def network_get(context, id):61def network_get(context, id):
58 """Get network by id."""62 """Get network by id."""
59 return IMPL.dao_factory.get_dao(context).\63 return IMPL.dao_factory.get_dao().\
60 network_get(id)64 network_get(id)
61 65
66@nova_api.require_admin_context
62def network_get_by_ethernet_card(context, ethernet_card_id):67def network_get_by_ethernet_card(context, ethernet_card_id):
63 """Get network by ethernet card."""68 """Get network by ethernet card."""
64 return IMPL.dao_factory.get_dao(context).\69 return IMPL.dao_factory.get_dao().\
65 network_get_by_ethernet_card(ethernet_card_id)70 network_get_by_ethernet_card(ethernet_card_id)
6671
72@nova_api.require_admin_context
67def network_get_associated_fixed_ips(context, network_id):73def network_get_associated_fixed_ips(context, network_id):
68 """Get fixed IPs that are associated with ethernet for a given network."""74 """Get fixed IPs that are associated with ethernet for a given network."""
69 return IMPL.dao_factory.get_dao(context).\75 return IMPL.dao_factory.get_dao().\
70 network_get_associated_fixed_ips(network_id)76 network_get_associated_fixed_ips(network_id)
7177
72def host_get_networks(context, host):78def host_get_networks(context, host):
73 """Get networks for a given host."""79 """Get networks for a given host."""
74 return IMPL.dao_factory.get_dao(context).\80 return IMPL.dao_factory.get_dao().\
75 host_get_networks(host)81 host_get_networks(host)
7682
83@nova_api.require_admin_context
77def network_get_by_bridge(context, bridge): 84def network_get_by_bridge(context, bridge):
78 """Get a network by bridge or raise if it does not exist."""85 """Get a network by bridge or raise if it does not exist."""
79 return IMPL.dao_factory.get_dao(context).\86 return IMPL.dao_factory.get_dao().\
80 network_get_by_bridge(bridge)87 network_get_by_bridge(bridge)
81 88
89@nova_api.require_admin_context
82def network_get_by_cidr(context, cidr):90def network_get_by_cidr(context, cidr):
83 """Get a network by cidr."""91 """Get a network by cidr."""
84 return IMPL.dao_factory.get_dao(context).\92 return IMPL.dao_factory.get_dao().\
85 network_get_by_cidr(cidr)93 network_get_by_cidr(cidr)
8694
87def network_create(context, values):95def network_create(context, values):
88 """Create a new network."""96 """Create a new network."""
89 return IMPL.dao_factory.get_dao(context).\97 return IMPL.dao_factory.get_dao().\
90 network_create(values)98 network_create(values)
9199
100@nova_api.require_admin_context
92def network_create_safe(context, values):101def network_create_safe(context, values):
93 """Create a new network. Returns None when there is an exception"""102 """Create a new network. Returns None when there is an exception"""
94 return IMPL.dao_factory.get_dao(context).\103 return IMPL.dao_factory.get_dao().\
95 network_create_safe(values)104 network_create_safe(values)
96105
97def network_get_all(context):106def network_get_all(context):
98 """Get all networks."""107 """Get all networks."""
99 return IMPL.dao_factory.get_dao(context).\108 can_read_deleted = nova_api.can_read_deleted(context)
100 network_get_all()109 return IMPL.dao_factory.get_dao().\
110 network_get_all(read_deleted=can_read_deleted)
101111
102def network_get_by_project(context, project_id, associate=True):112def network_get_by_project(context, project_id, associate=True):
103 """Return the network associated with the project.113 """Return the network associated with the project.
@@ -106,22 +116,25 @@
106 network if one is not found, otherwise it returns None.116 network if one is not found, otherwise it returns None.
107117
108 """118 """
109 return IMPL.dao_factory.get_dao(context).\119 if associate and not nova_api.is_admin_context(context):
120 raise exception.NotAuthorized()
121 return IMPL.dao_factory.get_dao().\
110 network_get_by_project(project_id, associate)122 network_get_by_project(project_id, associate)
111123
112def network_update(context, network_id, values):124def network_update(context, network_id, values):
113 """Update network.""" 125 """Update network."""
114 return IMPL.dao_factory.get_dao(context).\126 return IMPL.dao_factory.get_dao().\
115 network_update(network_id, values)127 network_update(network_id, values)
116128
117def network_delete(context, network_id):129def network_delete(context, network_id):
118 """Delete network."""130 """Delete network."""
119 return IMPL.dao_factory.get_dao(context).\131 return IMPL.dao_factory.get_dao().\
120 network_delete(network_id)132 network_delete(network_id)
121133
134@nova_api.require_admin_context
122def network_associate(context, project_id):135def network_associate(context, project_id):
123 """Associate a free network to a project."""136 """Associate a free network to a project."""
124 return IMPL.dao_factory.get_dao(context).\137 return IMPL.dao_factory.get_dao().\
125 network_associate(project_id)138 network_associate(project_id)
126139
127def floating_ip_allocate_address(context, host, project_id):140def floating_ip_allocate_address(context, host, project_id):
@@ -130,32 +143,38 @@
130 Raises if one is not available.143 Raises if one is not available.
131144
132 """145 """
133 return IMPL.dao_factory.get_dao(context).\146 nova_api.authorize_project_context(context, project_id)
147 return IMPL.dao_factory.get_dao().\
134 floating_ip_allocate_address(host, project_id)148 floating_ip_allocate_address(host, project_id)
135149
136def floating_ip_create(context, values):150def floating_ip_create(context, values):
137 """Create a floating ip from the values dictionary."""151 """Create a floating ip from the values dictionary."""
138 return IMPL.dao_factory.get_dao(context).\152 return IMPL.dao_factory.get_dao().\
139 floating_ip_create(values)153 floating_ip_create(values)
140154
141def floating_ip_count_by_project(context, project_id):155def floating_ip_count_by_project(context, project_id):
142 """Count floating ips used by project."""156 """Count floating ips used by project."""
143 return IMPL.dao_factory.get_dao(context).\157 nova_api.authorize_project_context(context, project_id)
158 return IMPL.dao_factory.get_dao().\
144 floating_ip_count_by_project(project_id)159 floating_ip_count_by_project(project_id)
145160
146def floating_ip_deallocate(context, address):161def floating_ip_deallocate(context, address):
147 """Deallocate an floating ip by address"""162 """Deallocate an floating ip by address"""
148 return IMPL.dao_factory.get_dao(context).\163 # TODO(devcamcar): How to ensure floating id belongs to user?
164 return IMPL.dao_factory.get_dao().\
149 floating_ip_deallocate(address)165 floating_ip_deallocate(address)
150166
151def floating_ip_destroy(context, address):167def floating_ip_destroy(context, address):
152 """Destroy the floating_ip or raise if it does not exist."""168 """Destroy the floating_ip or raise if it does not exist."""
153 return IMPL.dao_factory.get_dao(context).\169 # TODO(devcamcar): Ensure address belongs to user.
170 return IMPL.dao_factory.get_dao().\
154 floating_ip_destroy(address)171 floating_ip_destroy(address)
155172
156def floating_ip_disassociate(context, floating_ip_id):173def floating_ip_disassociate(context, floating_ip_id):
157 """Diassociate the floating_ip."""174 """Diassociate the floating_ip."""
158 return IMPL.dao_factory.get_dao(context).\175 # TODO(devcamcar): Ensure address belongs to user.
176 # Does get_floating_ip_by_address handle this?
177 return IMPL.dao_factory.get_dao().\
159 floating_ip_disassociate(floating_ip_id)178 floating_ip_disassociate(floating_ip_id)
160179
161def floating_ip_disassociate_by_address(context, address):180def floating_ip_disassociate_by_address(context, address):
@@ -164,91 +183,105 @@
164 Returns the address of the existing fixed ip.183 Returns the address of the existing fixed ip.
165184
166 """185 """
167 return IMPL.dao_factory.get_dao(context).\186 # TODO(devcamcar): Ensure address belongs to user.
187 # Does get_floating_ip_by_address handle this?
188 return IMPL.dao_factory.get_dao().\
168 floating_ip_disassociate_by_address(address)189 floating_ip_disassociate_by_address(address)
169190
170def floating_ip_fixed_ip_associate(context, floating_address, fixed_address):191def floating_ip_fixed_ip_associate(context, floating_address, fixed_address):
171 """Associate an floating ip to a fixed_ip by address."""192 """Associate an floating ip to a fixed_ip by address."""
172 return IMPL.dao_factory.get_dao(context).\193 # TODO(devcamcar): How to ensure floating_id belongs to user?
194 return IMPL.dao_factory.get_dao().\
173 floating_ip_fixed_ip_associate(floating_address, fixed_address)195 floating_ip_fixed_ip_associate(floating_address, fixed_address)
174196
197@nova_api.require_admin_context
175def floating_ip_get_all(context):198def floating_ip_get_all(context):
176 """Get all floating ips."""199 """Get all floating ips."""
177 return IMPL.dao_factory.get_dao(context).\200 return IMPL.dao_factory.get_dao().\
178 floating_ip_get_all()201 floating_ip_get_all()
179202
203@nova_api.require_admin_context
180def floating_ip_get_all_by_host(context, host):204def floating_ip_get_all_by_host(context, host):
181 """Get all floating ips by host."""205 """Get all floating ips by host."""
182 return IMPL.dao_factory.get_dao(context).\206 return IMPL.dao_factory.get_dao().\
183 floating_ip_get_all_by_host(host)207 floating_ip_get_all_by_host(host)
184208
185def floating_ip_get_all_by_project(context, project_id):209def floating_ip_get_all_by_project(context, project_id):
186 """Get all floating ips by project."""210 """Get all floating ips by project."""
187 return IMPL.dao_factory.get_dao(context).\211 nova_api.authorize_project_context(context, project_id)
212 return IMPL.dao_factory.get_dao().\
188 floating_ip_get_all_by_project(project_id)213 floating_ip_get_all_by_project(project_id)
189214
190def floating_ip_get_by_address(context, address):215def floating_ip_get_by_address(context, address):
191 """Get a floating ip by address or raise if it doesn't exist."""216 """Get a floating ip by address or raise if it doesn't exist."""
192 return IMPL.dao_factory.get_dao(context).\217 # TODO(devcamcar): Ensure the address belongs to user.
193 floating_ip_get_by_address(address)218 can_read_deleted = nova_api.can_read_deleted(context)
219 return IMPL.dao_factory.get_dao().\
220 floating_ip_get_by_address(address, read_deleted=can_read_deleted)
194221
195def floating_ip_update(context, address, values):222def floating_ip_update(context, address, values):
196 """Update a floating ip by address or raise if it doesn't exist."""223 """Update a floating ip by address or raise if it doesn't exist."""
197 return IMPL.dao_factory.get_dao(context).\224 return IMPL.dao_factory.get_dao().\
198 floating_ip_update(address, values)225 floating_ip_update(address, values)
199226
200def fixed_ip_create(context, values):227def fixed_ip_create(context, values):
201 """Creates a fixed IP."""228 """Creates a fixed IP."""
202 return IMPL.dao_factory.get_dao(context).\229 return IMPL.dao_factory.get_dao().\
203 fixed_ip_create(values)230 fixed_ip_create(values)
204231
205def fixed_ip_get(context, fixed_ip_id):232def fixed_ip_get(context, fixed_ip_id):
206 """Gets fixed IP with ID."""233 """Gets fixed IP with ID."""
207 return IMPL.dao_factory.get_dao(context).\234 return IMPL.dao_factory.get_dao().\
208 fixed_ip_get(fixed_ip_id)235 fixed_ip_get(fixed_ip_id)
209236
210def fixed_ip_get_all(context):237def fixed_ip_get_all(context):
211 """Gets all the fixed IPs."""238 """Gets all the fixed IPs."""
212 return IMPL.dao_factory.get_dao(context).\239 return IMPL.dao_factory.get_dao().\
213 fixed_ip_get_all()240 fixed_ip_get_all()
214241
215def fixed_ip_get_by_address(context, address):242def fixed_ip_get_by_address(context, address):
216 """Get fixed IP by address."""243 """Get fixed IP by address."""
217 return IMPL.dao_factory.get_dao(context).\244 can_read_deleted = nova_api.can_read_deleted(context)
218 fixed_ip_get_by_address(address)245 return IMPL.dao_factory.get_dao().\
246 fixed_ip_get_by_address(address, read_deleted=can_read_deleted)
219247
220def fixed_ip_get_by_ethernet_card(context, ethernet_card_id):248def fixed_ip_get_by_ethernet_card(context, ethernet_card_id):
221 """Get IP address by ethernet card id."""249 """Get IP address by ethernet card id."""
222 return IMPL.dao_factory.get_dao(context).\250 can_read_deleted = nova_api.can_read_deleted(context)
223 fixed_ip_get_by_ethernet_card(ethernet_card_id)251 return IMPL.dao_factory.get_dao().\
252 fixed_ip_get_by_ethernet_card(ethernet_card_id,
253 read_deleted=can_read_deleted)
224254
255@nova_api.require_admin_context
225def fixed_ip_get_network(context, address):256def fixed_ip_get_network(context, address):
226 """Gets network of an IP address."""257 """Gets network of an IP address."""
227 return IMPL.dao_factory.get_dao(context).\258 can_read_deleted = nova_api.can_read_deleted(context)
228 fixed_ip_get_network(address)259 return IMPL.dao_factory.get_dao().\
260 fixed_ip_get_network(address, read_deleted=can_read_deleted)
229261
230def fixed_ip_update(context, fixed_ip_id, values):262def fixed_ip_update(context, fixed_ip_id, values):
231 """Update fixed IP."""263 """Update fixed IP."""
232 return IMPL.dao_factory.get_dao(context).\264 return IMPL.dao_factory.get_dao().\
233 fixed_ip_update(fixed_ip_id, values)265 fixed_ip_update(fixed_ip_id, values)
234266
235def fixed_ip_disassociate(context, fixed_ip_id):267def fixed_ip_disassociate(context, fixed_ip_id):
236 """Removes reference to ethernet card from IP."""268 """Removes reference to ethernet card from IP."""
237 return IMPL.dao_factory.get_dao(context).\269 return IMPL.dao_factory.get_dao().\
238 fixed_ip_disassociate(fixed_id_ip)270 fixed_ip_disassociate(fixed_id_ip)
239271
240def fixed_ip_associate(context, fixed_ip_id, ethernet_card_id):272def fixed_ip_associate(context, fixed_ip_id, ethernet_card_id):
241 """Associates an IP address to an ethernet card."""273 """Associates an IP address to an ethernet card."""
242 return IMPL.dao_factory.get_dao(context).\274 return IMPL.dao_factory.get_dao().\
243 fixed_ip_associate(fixed_ip_id, ethernet_card_id)275 fixed_ip_associate(fixed_ip_id, ethernet_card_id)
244276
277@nova_api.require_admin_context
245def fixed_ip_associate_pool(context, network_id, ethernet_card_id):278def fixed_ip_associate_pool(context, network_id, ethernet_card_id):
246 """Associates an IP to an ethernet card."""279 """Associates an IP to an ethernet card."""
247 return IMPL.dao_factory.get_dao(context).\280 return IMPL.dao_factory.get_dao().\
248 fixed_ip_associate_pool(network_id, ethernet_card_id)281 fixed_ip_associate_pool(network_id, ethernet_card_id)
249282
283@nova_api.require_admin_context
250def fixed_ip_disassociate_all_by_timeout(context, host, time):284def fixed_ip_disassociate_all_by_timeout(context, host, time):
251 """Disassociates fixed IP by timeout."""285 """Disassociates fixed IP by timeout."""
252 return IMPL.dao_factory.get_dao(context).\286 return IMPL.dao_factory.get_dao().\
253 fixed_ip_disassociate_all_by_timeout(host, time)287 fixed_ip_disassociate_all_by_timeout(host, time)
254
255\ No newline at end of file288\ No newline at end of file
256289
=== modified file 'nova/network/flat_vlan/db/sqlalchemy/api.py'
--- nova/network/flat_vlan/db/sqlalchemy/api.py 2011-04-02 17:11:15 +0000
+++ nova/network/flat_vlan/db/sqlalchemy/api.py 2011-04-06 17:14:53 +0000
@@ -17,81 +17,31 @@
17"""17"""
18Implementation of SQLAlchemy backend.18Implementation of SQLAlchemy backend.
19"""19"""
20
21from sqlalchemy import or_
22from sqlalchemy.exc import IntegrityError
23from sqlalchemy.orm import joinedload_all
24import warnings
25
26from nova import exception
27from nova.db import api as nova_db
28from nova.db.sqlalchemy import api as nova_api
29from nova.network.flat_vlan.db.sqlalchemy import models20from nova.network.flat_vlan.db.sqlalchemy import models
30from nova.network.flat_vlan.db.sqlalchemy.session import get_session21from nova.network.flat_vlan.db.sqlalchemy.session import get_session
3122
3223
33class DataAccessCache(object):24class DefaultSessionFlatVlanNetworkDataAccessFactory(object):
34 """A cache of Data Access Objects specific to sessions and contexts.25 """A DAO factory that lazily creates and uses the default session.
35
36 Creates one DAO object per session and set of context parameter
37 values, and caches it. The parameters currently used in the
38 context are:
39 - nova.db.sqlalchemy.api.is_admin_context(context)
40 - nova.db.sqlalchemy.api.can_read_deleted(context)
41 """
42
43 def __init__(self, dao_class):
44 self._dao_class = dao_class
45 self._cached_daos = {}
46
47 def get_dao(self, context, session):
48 """Get a DataAccess object.
49
50 If no cached DAO has been created for this session and this
51 context's parameter, a new DAO is created and
52 cached. Otherwise, the cached DAO is returned.
53
54 :param context: The request context.
55 :param session: The SQLAlchemy session to use as the Data
56 Access Object's data source.
57 """
58 is_admin = nova_api.is_admin_context(context)
59 can_read_deleted = nova_api.can_read_deleted(context)
60 key = (session, is_admin, can_read_deleted)
61 dao = self._cached_daos.get(key)
62 if dao is None:
63 dao = self._dao_class(session, is_admin, can_read_deleted)
64 self._cached_daos[key] = dao
65 return dao
66
67
68class DefaultSessionNetworkDataAccessFactory(object):
69 """A DAO factory that lazily uses the default session.
7026
71 This factory uses the default session for the flat VLAN network27 This factory uses the default session for the flat VLAN network
72 service, which it creates lazily when creating the first DAO.28 service, which it creates lazily when creating the first DAO.
73 """29 """
7430
75 def __init__(self):31 def __init__(self):
76 self._dao_cache = DataAccessCache(models.FlatVlanNetworkDataAccess)32 self._dao = None
77 self._session = None33
7834 def get_dao(self):
79 def get_session(self):
80 if self._session is None:
81 self._session = get_session()
82 return self._session
83
84 def get_dao(self, context):
85 """Get a DataAccess object.35 """Get a DataAccess object.
8636
87 If no cached DAO has been created for this context's37 If no cached DAO has been created for the default session, a
88 parameter, a new DAO is created and cached. Otherwise, the38 new DAO is created and cached. Otherwise, the cached DAO is
89 cached DAO is returned.39 returned.
90
91 :param context: The request context.
92 """40 """
93 return self._dao_cache.get_dao(context, self.get_session())41 if self._dao is None:
9442 self._dao = models.FlatVlanNetworkDataAccess(get_session())
9543 return self._dao
96dao_factory = DefaultSessionNetworkDataAccessFactory()44
45
46dao_factory = DefaultSessionFlatVlanNetworkDataAccessFactory()
9747
9848
=== modified file 'nova/network/flat_vlan/db/sqlalchemy/models.py'
--- nova/network/flat_vlan/db/sqlalchemy/models.py 2011-04-04 09:52:18 +0000
+++ nova/network/flat_vlan/db/sqlalchemy/models.py 2011-04-06 17:14:53 +0000
@@ -22,6 +22,7 @@
22from sqlalchemy.orm import joinedload, joinedload_all22from sqlalchemy.orm import joinedload, joinedload_all
23from sqlalchemy import Column, Boolean, Integer, String23from sqlalchemy import Column, Boolean, Integer, String
24from sqlalchemy import ForeignKey24from sqlalchemy import ForeignKey
25from sqlalchemy.exc import IntegrityError
25from sqlalchemy.ext.declarative import declarative_base26from sqlalchemy.ext.declarative import declarative_base
2627
27from nova import exception28from nova import exception
@@ -91,26 +92,20 @@
91 'FloatingIp.fixed_ip_id == FixedIp.id,'92 'FloatingIp.fixed_ip_id == FixedIp.id,'
92 'FloatingIp.deleted == False)')93 'FloatingIp.deleted == False)')
93 project_id = Column(String(255))94 project_id = Column(String(255))
94 host = Column(String(255)) # , ForeignKey('hosts.id')) 95 host = Column(String(255)) # , ForeignKey('hosts.id'))
9596
9697
97class DataAccess(object):98class DataAccess(object):
98 """The base class to implement Data Access Objects.99 """The base class to implement Data Access Objects.
99 """100 """
100101
101 def __init__(self, session, is_admin, can_read_deleted):102 def __init__(self, session):
102 """Initialize this Data Access Object.103 """Initialize this Data Access Object.
103104
104 :param session: The SQLAlchemy session to use as this Data105 :param session: The SQLAlchemy session to use as this Data
105 Access Object's data source.106 Access Object's data source.
106 :param is_admin: Indicates if the request context is an
107 administrator.
108 :param can_read_deleted: Indicates if the request context has
109 access to deleted objects.
110 """107 """
111 self._session = session108 self._session = session
112 self._is_admin = is_admin
113 self._can_read_deleted = can_read_deleted
114109
115110
116class EthernetCardDataAccess(DataAccess):111class EthernetCardDataAccess(DataAccess):
@@ -130,9 +125,9 @@
130125
131 return result126 return result
132127
133 def ethernet_card_get_all(self):128 def ethernet_card_get_all(self, read_deleted=False):
134 return self._session.query(self.ethernet_card_dto_class).\129 return self._session.query(self.ethernet_card_dto_class).\
135 filter_by(deleted=self._can_read_deleted).\130 filter_by(deleted=read_deleted).\
136 all()131 all()
137132
138 def ethernet_card_create(self, values):133 def ethernet_card_create(self, values):
@@ -171,8 +166,6 @@
171 return result166 return result
172167
173 def network_get_by_bridge(self, bridge):168 def network_get_by_bridge(self, bridge):
174 if not self._is_admin:
175 raise exception.NotAuthorized()
176 result = self._session.query(self.network_dto_class).\169 result = self._session.query(self.network_dto_class).\
177 filter_by(bridge=bridge).\170 filter_by(bridge=bridge).\
178 filter_by(deleted=False).\171 filter_by(deleted=False).\
@@ -184,8 +177,6 @@
184 return result177 return result
185178
186 def network_get_by_ethernet_card(self, ethernet_card_id):179 def network_get_by_ethernet_card(self, ethernet_card_id):
187 if not self._is_admin:
188 raise exception.NotAuthorized()
189 rv = self._session.query(self.network_dto_class).\180 rv = self._session.query(self.network_dto_class).\
190 filter_by(deleted=False).\181 filter_by(deleted=False).\
191 join(self.network_dto_class.fixed_ips).\182 join(self.network_dto_class.fixed_ips).\
@@ -193,7 +184,7 @@
193 filter_by(deleted=False).\184 filter_by(deleted=False).\
194 first()185 first()
195 if not rv:186 if not rv:
196 raise exception.NotFound(_('No network for ethernet card %s') % 187 raise exception.NotFound(_('No network for ethernet card %s') %
197 ethernet_card_id)188 ethernet_card_id)
198 return rv189 return rv
199190
@@ -204,9 +195,9 @@
204 filter_by(host=host).\195 filter_by(host=host).\
205 all()196 all()
206197
207 def network_get_all(self):198 def network_get_all(self, read_deleted=False):
208 return self._session.query(self.network_dto_class).\199 return self._session.query(self.network_dto_class).\
209 filter_by(deleted=self._can_read_deleted).\200 filter_by(deleted=read_deleted).\
210 all()201 all()
211202
212 def network_create(self, values):203 def network_create(self, values):
@@ -216,8 +207,6 @@
216 return network_ref207 return network_ref
217208
218 def network_create_safe(self, values):209 def network_create_safe(self, values):
219 if not self._is_admin:
220 raise exception.NotAuthorized()
221 try:210 try:
222 return self.network_create(values)211 return self.network_create(values)
223 except IntegrityError:212 except IntegrityError:
@@ -272,8 +261,6 @@
272 return result261 return result
273262
274 def network_get_associated_fixed_ips(self, network_id):263 def network_get_associated_fixed_ips(self, network_id):
275 if not self._is_admin:
276 raise exception.NotAuthorized()
277 return self._session.query(self.fixed_ip_dto_class).\264 return self._session.query(self.fixed_ip_dto_class).\
278 options(joinedload_all('ethernet_card')).\265 options(joinedload_all('ethernet_card')).\
279 filter_by(network_id=network_id).\266 filter_by(network_id=network_id).\
@@ -283,10 +270,10 @@
283270
284 network_get_associated_ips = network_get_associated_fixed_ips271 network_get_associated_ips = network_get_associated_fixed_ips
285272
286 def fixed_ip_get_by_address(self, address):273 def fixed_ip_get_by_address(self, address, read_deleted=False):
287 result = self._session.query(self.fixed_ip_dto_class).\274 result = self._session.query(self.fixed_ip_dto_class).\
288 filter_by(address=address).\275 filter_by(address=address).\
289 filter_by(deleted=self._can_read_deleted).\276 filter_by(deleted=read_deleted).\
290 options(joinedload('network')).\277 options(joinedload('network')).\
291 options(joinedload('ethernet_card')).\278 options(joinedload('ethernet_card')).\
292 first()279 first()
@@ -301,16 +288,15 @@
301 fixed_ip_ref.ethernet_card = None288 fixed_ip_ref.ethernet_card = None
302 fixed_ip_ref.save(session=self._session)289 fixed_ip_ref.save(session=self._session)
303290
304 def fixed_ip_get_by_ethernet_card(self, ethernet_card_id):291 def fixed_ip_get_by_ethernet_card(self, ethernet_card_id,
292 read_deleted=False):
305 result = self._session.query(self.fixed_ip_dto_class).\293 result = self._session.query(self.fixed_ip_dto_class).\
306 filter_by(ethernet_card_id=ethernet_card_id).\294 filter_by(ethernet_card_id=ethernet_card_id).\
307 filter_by(deleted=self._can_read_deleted).\295 filter_by(deleted=read_deleted).\
308 first()296 first()
309 return result297 return result
310298
311 def fixed_ip_disassociate_all_by_timeout(self, host, time):299 def fixed_ip_disassociate_all_by_timeout(self, host, time):
312 if not self._is_admin:
313 raise exception.NotAuthorized()
314 inner_q = self._session.query(Network.id).\300 inner_q = self._session.query(Network.id).\
315 filter_by(host=host).\301 filter_by(host=host).\
316 subquery()302 subquery()
@@ -330,53 +316,46 @@
330316
331 floating_ip_dto_class = FloatingIp317 floating_ip_dto_class = FloatingIp
332318
319 def floating_ip_count_by_project(self, project_id):
320 return self._session.query(self.floating_ip_dto_class).\
321 filter_by(project_id=project_id).\
322 filter_by(deleted=False).\
323 count()
324
333 def floating_ip_allocate_address(self, host, project_id):325 def floating_ip_allocate_address(self, host, project_id):
334 # TODO: Check authorizations much earlier before calling
335 # this. Doing that in DB layer is way too late.
336 # nova_api.authorize_project_context(context, project_id)
337 with self._session.begin():326 with self._session.begin():
338 floating_ip_ref = self._session.query(self.floating_ip_dto_class).\327 floating_ip_ref = self._session.query(self.floating_ip_dto_class).\
339 filter_by(host=host).\328 filter_by(host=host).\
340 filter_by(fixed_ip_id=None).\329 filter_by(fixed_ip_id=None).\
341 filter_by(project_id=None).\330 filter_by(project_id=None).\
342 filter_by(deleted=False).\331 filter_by(deleted=False).\
343 with_lockmode('update').\332 with_lockmode('update').\
344 first()333 first()
345 # NOTE(vish): if with_lockmode isn't supported, as in sqlite,334 # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
346 # then this has concurrency issues335 # then this has concurrency issues
347 if not floating_ip_ref:336 if not floating_ip_ref:
348 raise nova_db.NoMoreAddresses()337 raise nova_db.NoMoreAddresses()
349 floating_ip_ref['project_id'] = project_id338 floating_ip_ref['project_id'] = project_id
350 self._session.add(floating_ip_ref)339 self._session.add(floating_ip_ref)
351 return floating_ip_ref['address']340 return floating_ip_ref
352
353 def floating_ip_count_by_project(self, project_id):
354 # TODO: Check authorizations much earlier before calling
355 # this. Doing that in DB layer is way too late.
356 # nova_api.authorize_project_context(context, project_id)
357 return self._session.query(self.floating_ip_dto_class).\
358 filter_by(project_id=project_id).\
359 filter_by(deleted=False).\
360 count()
361341
362 def floating_ip_deallocate(self, address):342 def floating_ip_deallocate(self, address):
363 with self._session.begin():343 with self._session.begin():
364 # TODO(devcamcar): How to ensure floating id belongs to user?344 floating_ip_ref = self.floating_ip_get_by_address(
365 floating_ip_ref = self.floating_ip_get_by_address(address)345 address, read_deleted=False)
366 floating_ip_ref['project_id'] = None346 floating_ip_ref['project_id'] = None
367 floating_ip_ref.save(session=self._session)347 floating_ip_ref.save(session=self._session)
368348
369 def floating_ip_destroy(self, address):349 def floating_ip_destroy(self, address):
370 with self._session.begin():350 with self._session.begin():
371 # TODO(devcamcar): Ensure address belongs to user.351 floating_ip_ref = self.floating_ip_get_by_address(
372 floating_ip_ref = self.floating_ip_get_by_address(address)352 address, read_deleted=False)
373 floating_ip_ref.delete(session=self._session)353 floating_ip_ref.delete(session=self._session)
374354
375 def floating_ip_disassociate_by_address(self, address):355 def floating_ip_disassociate_by_address(self, address):
376 with self._session.begin():356 with self._session.begin():
377 # TODO(devcamcar): Ensure address belongs to user.357 floating_ip_ref = self.floating_ip_get_by_address(
378 # Does get_floating_ip_by_address handle this?358 address, read_deleted=False)
379 floating_ip_ref = self.floating_ip_get_by_address(address)
380 fixed_ip_ref = floating_ip_ref.fixed_ip359 fixed_ip_ref = floating_ip_ref.fixed_ip
381 if fixed_ip_ref:360 if fixed_ip_ref:
382 fixed_ip_address = fixed_ip_ref['address']361 fixed_ip_address = fixed_ip_ref['address']
@@ -387,38 +366,32 @@
387 return fixed_ip_address366 return fixed_ip_address
388367
389 def floating_ip_get_all(self):368 def floating_ip_get_all(self):
390 if not self._is_admin:
391 raise exception.NotAuthorized()
392 return self._session.query(self.floating_ip_dto_class).\369 return self._session.query(self.floating_ip_dto_class).\
393 options(joinedload_all('fixed_ip.ethernet_card')).\370 options(joinedload_all('fixed_ip.ethernet_card')).\
394 filter_by(deleted=False).\371 filter_by(deleted=False).\
395 all()372 all()
396373
397 def floating_ip_get_all_by_project(self, project_id):374 def floating_ip_get_all_by_project(self, project_id):
398 # TODO: Check authorizations much earlier before calling
399 # this. Doing that in DB layer is way too late.
400 # nova_api.authorize_project_context(context, project_id)
401 return self._session.query(self.floating_ip_dto_class).\375 return self._session.query(self.floating_ip_dto_class).\
402 options(joinedload_all('fixed_ip.ethernet_card')).\376 options(joinedload_all('fixed_ip.ethernet_card')).\
403 filter_by(project_id=project_id).\377 filter_by(project_id=project_id).\
404 filter_by(deleted=False).\378 filter_by(deleted=False).\
405 all()379 all()
406380
407 def floating_ip_get_by_address(self, address):381 def floating_ip_get_by_address(self, address, read_deleted=False):
408 # TODO(devcamcar): Ensure the address belongs to user.382 result = self._session.query(self.floating_ip_dto_class).\
409 result = session.query(self.floating_ip_dto_class).\
410 options(joinedload_all('fixed_ip.network')).\383 options(joinedload_all('fixed_ip.network')).\
411 filter_by(address=address).\384 filter_by(address=address).\
412 filter_by(deleted=self._can_read_deleted).\385 filter_by(deleted=read_deleted).\
413 first()386 first()
414 if not result:387 if not result:
415 raise exception.NotFound('No floating ip for address %s' % address)388 raise exception.NotFound('No floating ip for address %s' % address)
416
417 return result389 return result
418390
419 def floating_ip_update(self, address, values):391 def floating_ip_update(self, address, values):
420 with self._session.begin():392 with self._session.begin():
421 floating_ip_ref = self.floating_ip_get_by_address(address)393 floating_ip_ref = self.floating_ip_get_by_address(
394 address, read_deleted=False)
422 for (key, value) in values.iteritems():395 for (key, value) in values.iteritems():
423 floating_ip_ref[key] = value396 floating_ip_ref[key] = value
424 floating_ip_ref.save(session=self._session)397 floating_ip_ref.save(session=self._session)
@@ -431,9 +404,8 @@
431404
432 def floating_ip_disassociate(self, address):405 def floating_ip_disassociate(self, address):
433 with self._session.begin():406 with self._session.begin():
434 # TODO(devcamcar): Ensure address belongs to user.407 floating_ip_ref = self.floating_ip_get_by_address(
435 # Does get_floating_ip_by_address handle this?408 address, read_deleted=False)
436 floating_ip_ref = self.floating_ip_get_by_address(address)
437 fixed_ip_ref = floating_ip_ref.fixed_ip409 fixed_ip_ref = floating_ip_ref.fixed_ip
438 if fixed_ip_ref:410 if fixed_ip_ref:
439 fixed_ip_address = fixed_ip_ref['address']411 fixed_ip_address = fixed_ip_ref['address']
@@ -441,11 +413,9 @@
441 fixed_ip_address = None413 fixed_ip_address = None
442 floating_ip_ref.fixed_ip = None414 floating_ip_ref.fixed_ip = None
443 floating_ip_ref.save(session=self._session)415 floating_ip_ref.save(session=self._session)
444 return fixed_ip_address416 return fixed_ip_ref
445417
446 def floating_ip_get_all_by_host(self, host):418 def floating_ip_get_all_by_host(self, host):
447 if not self._is_admin:
448 raise exception.NotAuthorized()
449 return self._session.query(self.floating_ip_dto_class).\419 return self._session.query(self.floating_ip_dto_class).\
450 options(joinedload_all('fixed_ip.ethernet_card')).\420 options(joinedload_all('fixed_ip.ethernet_card')).\
451 filter_by(host=host).\421 filter_by(host=host).\
@@ -495,8 +465,6 @@
495 return result465 return result
496466
497 def network_get_by_cidr(self, cidr):467 def network_get_by_cidr(self, cidr):
498 if not self._is_admin:
499 raise exception.NotAuthorized()
500 result = self._session.query(self.network_dto_class).\468 result = self._session.query(self.network_dto_class).\
501 filter_by(cidr=cidr).first()469 filter_by(cidr=cidr).first()
502470
@@ -506,8 +474,6 @@
506 return result474 return result
507475
508 def network_associate(self, project_id):476 def network_associate(self, project_id):
509 if not self._is_admin:
510 raise exception.NotAuthorized()
511 with self._session.begin():477 with self._session.begin():
512 network_ref = self._session.query(self.network_dto_class).\478 network_ref = self._session.query(self.network_dto_class).\
513 filter_by(deleted=False).\479 filter_by(deleted=False).\
@@ -522,17 +488,17 @@
522 self._session.add(network_ref)488 self._session.add(network_ref)
523 return network_ref489 return network_ref
524490
525 def fixed_ip_get_network(self, address):491 def fixed_ip_get_network(self, address, read_deleted=False):
526 if not self._is_admin:492 fixed_ip_ref = self.fixed_ip_get_by_address(
527 raise exception.NotAuthorized()493 address, read_deleted=read_deleted)
528 fixed_ip_ref = self.fixed_ip_get_by_address(address)
529 return fixed_ip_ref.network494 return fixed_ip_ref.network
530495
531 def floating_ip_fixed_ip_associate(self, floating_address, fixed_address):496 def floating_ip_fixed_ip_associate(self, floating_address, fixed_address):
532 with self._session.begin():497 with self._session.begin():
533 # TODO(devcamcar): How to ensure floating_id belongs to user?498 floating_ip_ref = self.floating_ip_get_by_address(
534 floating_ip_ref = self.floating_ip_get_by_address(floating_address)499 floating_address, read_deleted=False)
535 fixed_ip_ref = self.fixed_ip_get_by_address(fixed_address)500 fixed_ip_ref = self.fixed_ip_get_by_address(
501 fixed_address, read_deleted=False)
536 floating_ip_ref.fixed_ip = fixed_ip_ref502 floating_ip_ref.fixed_ip = fixed_ip_ref
537 floating_ip_ref.save(session=self._session)503 floating_ip_ref.save(session=self._session)
538504
@@ -553,8 +519,6 @@
553 self._session.add(fixed_ip_ref)519 self._session.add(fixed_ip_ref)
554520
555 def fixed_ip_associate_pool(self, network_id, ethernet_card_id):521 def fixed_ip_associate_pool(self, network_id, ethernet_card_id):
556 if not self._is_admin:
557 raise exception.NotAuthorized()
558 with self._session.begin():522 with self._session.begin():
559 network_or_none = or_(523 network_or_none = or_(
560 self.fixed_ip_dto_class.network_id == network_id,524 self.fixed_ip_dto_class.network_id == network_id,
@@ -572,4 +536,4 @@
572 ip_ref.network = self.network_get(network_id)536 ip_ref.network = self.network_get(network_id)
573 ip_ref.ethernet_card = self.ethernet_card_get(ethernet_card_id)537 ip_ref.ethernet_card = self.ethernet_card_get(ethernet_card_id)
574 self._session.add(ip_ref)538 self._session.add(ip_ref)
575 return ip_ref
576\ No newline at end of file539\ No newline at end of file
540 return ip_ref
577541
=== modified file 'nova/network/flat_vlan/network.py'
--- nova/network/flat_vlan/network.py 2011-04-04 09:52:18 +0000
+++ nova/network/flat_vlan/network.py 2011-04-06 17:14:53 +0000
@@ -32,18 +32,18 @@
32LOG = logging.getLogger("nova.net_flat_vlan")32LOG = logging.getLogger("nova.net_flat_vlan")
33LOG_DHCP = logging.getLogger('nova.dhcpbridge-flat-vlan')33LOG_DHCP = logging.getLogger('nova.dhcpbridge-flat-vlan')
3434
35DhcpHost = collections.namedtuple('DhcpHost', 35DhcpHost = collections.namedtuple('DhcpHost',
36 'mac_address, hostname, ip_address, timestamp')36 'mac_address, hostname, ip_address, timestamp')
3737
38class NetworkService(nova_manager.SchedulerDependentManager):38class NetworkService(nova_manager.SchedulerDependentManager):
3939
40 timeout_fixed_ips = (FLAGS.net_flat_vlan_use_vlan or 40 timeout_fixed_ips = (FLAGS.net_flat_vlan_use_vlan or
41 FLAGS.net_flat_vlan_use_dhcp)41 FLAGS.net_flat_vlan_use_dhcp)
4242
43 def __init__(self, network_driver=None, dhcp_driver=None, ra_driver=None,43 def __init__(self, network_driver=None, dhcp_driver=None, ra_driver=None,
44 filter_driver=None, *args, **kwargs):44 filter_driver=None, *args, **kwargs):
45 """Initializes vlan network service.45 """Initializes vlan network service.
46 46
47 Args:47 Args:
48 network_driver: The network driver for the OS.48 network_driver: The network driver for the OS.
49 dhcp_driver: The DHCP driver to use for DHCP service.49 dhcp_driver: The DHCP driver to use for DHCP service.
@@ -57,8 +57,8 @@
57 ra_driver = FLAGS.net_flat_vlan_ra_driver57 ra_driver = FLAGS.net_flat_vlan_ra_driver
58 if not filter_driver:58 if not filter_driver:
59 filter_driver = FLAGS.net_flat_vlan_filter_driver59 filter_driver = FLAGS.net_flat_vlan_filter_driver
60 60
61 kwargs['db_driver'] = 'nova.network.flat_vlan.db.api' 61 kwargs['db_driver'] = 'nova.network.flat_vlan.db.api'
62 self.driver = utils.import_object(network_driver)62 self.driver = utils.import_object(network_driver)
63 self.dhcp_driver = utils.import_object(dhcp_driver)63 self.dhcp_driver = utils.import_object(dhcp_driver)
64 self.ra_driver = utils.import_object(ra_driver)64 self.ra_driver = utils.import_object(ra_driver)
@@ -86,13 +86,14 @@
86 # NOTE(vish): The False here is because we ignore the case86 # NOTE(vish): The False here is because we ignore the case
87 # that the ip is already bound.87 # that the ip is already bound.
88 self.driver.bind_floating_ip(floating_ip['address'], False)88 self.driver.bind_floating_ip(floating_ip['address'], False)
89 self.driver.ensure_floating_forward(floating_ip['address'],89 self.filter_driver.ensure_floating_forward(
90 fixed_address)90 floating_ip['address'], fixed_address)
91 self.filter_driver.metadata_forward() 91
92 self.filter_driver.metadata_forward()
9293
93 def periodic_tasks(self, context=None):94 def periodic_tasks(self, context=None):
94 """Tasks to be run at a periodic interval.95 """Tasks to be run at a periodic interval.
95 96
96 Args:97 Args:
97 context: Nova context98 context: Nova context
98 """99 """
@@ -109,7 +110,7 @@
109110
110 def _set_vlan_host(self, context, network_id):111 def _set_vlan_host(self, context, network_id):
111 """Sets up VLAN networking service host.112 """Sets up VLAN networking service host.
112 113
113 Args:114 Args:
114 context: Nova context115 context: Nova context
115 network_id: ID of the network116 network_id: ID of the network
@@ -136,11 +137,11 @@
136 network_ref['vpn_private_address'])137 network_ref['vpn_private_address'])
137138
138 if not FLAGS.fake_network:139 if not FLAGS.fake_network:
139 hosts = self._get_dhcp_hosts(context, network_id) 140 hosts = self._get_dhcp_hosts(context, network_id)
140 bridge = network_ref['bridge']141 bridge = network_ref['bridge']
141 self.dhcp_driver.update_dhcp(bridge, 142 self.dhcp_driver.update_dhcp(bridge,
142 network_ref['gateway'], 143 network_ref['gateway'],
143 network_ref['dhcp_start'], 144 network_ref['dhcp_start'],
144 hosts,145 hosts,
145 dhcp_script=FLAGS.net_flat_vlan_dhcpbridge)146 dhcp_script=FLAGS.net_flat_vlan_dhcpbridge)
146 if(FLAGS.net_flat_vlan_use_ipv6):147 if(FLAGS.net_flat_vlan_use_ipv6):
@@ -148,11 +149,11 @@
148 network_ref['cidr_v6'])149 network_ref['cidr_v6'])
149 self.db.network_update(context, network_id,150 self.db.network_update(context, network_id,
150 {"gateway_v6":151 {"gateway_v6":
151 utils.get_my_linklocal(bridge)}) 152 utils.get_my_linklocal(bridge)})
152153
153 def _set_dhcp_host(self, context, network_id):154 def _set_dhcp_host(self, context, network_id):
154 """Sets up DHCP(no VLAN) networking service host.155 """Sets up DHCP(no VLAN) networking service host.
155 156
156 Args:157 Args:
157 context: Nova context158 context: Nova context
158 network_id: ID of the network159 network_id: ID of the network
@@ -171,22 +172,22 @@
171172
172 if not FLAGS.fake_network:173 if not FLAGS.fake_network:
173 hosts = self._get_dhcp_hosts(context, network_id)174 hosts = self._get_dhcp_hosts(context, network_id)
174 self.dhcp_driver.update_dhcp(bridge, 175 self.dhcp_driver.update_dhcp(bridge,
175 network_ref['gateway'], 176 network_ref['gateway'],
176 network_ref['dhcp_start'], 177 network_ref['dhcp_start'],
177 hosts,178 hosts,
178 dhcp_script=FLAGS.net_flat_vlan_dhcpbridge)179 dhcp_script=FLAGS.net_flat_vlan_dhcpbridge)
179 180
180 if(FLAGS.net_flat_vlan_use_ipv6):181 if(FLAGS.net_flat_vlan_use_ipv6):
181 self.ra_driver.update_ra(bridge,182 self.ra_driver.update_ra(bridge,
182 network_ref['cidr_v6'])183 network_ref['cidr_v6'])
183 self.db.network_update(context, network_id,184 self.db.network_update(context, network_id,
184 {"gateway_v6":185 {"gateway_v6":
185 utils.get_my_linklocal(bridge)}) 186 utils.get_my_linklocal(bridge)})
186 187
187 def set_network_host(self, context, network_id):188 def set_network_host(self, context, network_id):
188 """Called when this host becomes the host for a network.189 """Called when this host becomes the host for a network.
189 190
190 Args:191 Args:
191 context: Nova context to access DB.192 context: Nova context to access DB.
192 network_id: ID of the network193 network_id: ID of the network
@@ -201,11 +202,11 @@
201 net['host'] = self.host202 net['host'] = self.host
202 self.db.network_update(context, network_id, net)203 self.db.network_update(context, network_id, net)
203204
204 return self.host 205 return self.host
205 206
206 def _get_dhcp_hosts(self, context, network_id):207 def _get_dhcp_hosts(self, context, network_id):
207 """Get a list containing a network's hosts208 """Get a list containing a network's hosts
208 209
209 Args:210 Args:
210 context: Nova context211 context: Nova context
211 network_id: Network ID to get the DHCP hosts for.212 network_id: Network ID to get the DHCP hosts for.
@@ -224,10 +225,10 @@
224 ip_address=fixed_ip_ref['address'],225 ip_address=fixed_ip_ref['address'],
225 timestamp=None))226 timestamp=None))
226 return hosts227 return hosts
227 228
228 def allocate_fixed_ips(self, context, vnic_ids, is_vpn):229 def allocate_fixed_ips(self, context, vnic_ids, is_vpn):
229 """Gets a fixed ip from the pool.230 """Gets a fixed ip from the pool.
230 231
231 Args:232 Args:
232 context: Nova context needed to access DB.233 context: Nova context needed to access DB.
233 vnic_ids: List of VNIC IDs.234 vnic_ids: List of VNIC IDs.
@@ -236,29 +237,29 @@
236 for vnic_id in vnic_ids:237 for vnic_id in vnic_ids:
237 if FLAGS.net_flat_vlan_use_vlan:238 if FLAGS.net_flat_vlan_use_vlan:
238 ip_address = manager.bind_ip_to_ethernet_card_by_project(239 ip_address = manager.bind_ip_to_ethernet_card_by_project(
239 context, 240 context,
240 vnic_id,241 vnic_id,
241 is_vpn)242 is_vpn)
242 else:243 else:
243 ip_address = manager.bind_ip_to_ethernet_card_by_bridge(244 ip_address = manager.bind_ip_to_ethernet_card_by_bridge(
244 context, 245 context,
245 vnic_id) 246 vnic_id)
246247
247 if not FLAGS.fake_network and (FLAGS.net_flat_vlan_use_vlan or248 if not FLAGS.fake_network and (FLAGS.net_flat_vlan_use_vlan or
248 FLAGS.net_flat_vlan_use_dhcp):249 FLAGS.net_flat_vlan_use_dhcp):
249 network_id = ip_address['network_id']250 network_id = ip_address['network_id']
250 network_ref = self.db.network_get(context, network_id)251 network_ref = self.db.network_get(context, network_id)
251 hosts = self._get_dhcp_hosts(context, network_id) 252 hosts = self._get_dhcp_hosts(context, network_id)
252 self.dhcp_driver.update_dhcp(network_ref['bridge'], 253 self.dhcp_driver.update_dhcp(network_ref['bridge'],
253 network_ref['gateway'],254 network_ref['gateway'],
254 network_ref['dhcp_start'],255 network_ref['dhcp_start'],
255 hosts, 256 hosts,
256 dhcp_script=FLAGS.net_flat_vlan_dhcpbridge)257 dhcp_script=FLAGS.net_flat_vlan_dhcpbridge)
257258
258259
259 def lease_fixed_ip(self, context, mac, address):260 def lease_fixed_ip(self, context, mac, address):
260 """Called by dhcp-bridge when ip is leased.261 """Called by dhcp-bridge when ip is leased.
261 262
262 Args:263 Args:
263 context: Nova context264 context: Nova context
264 mac: MAC address to lease the IP address against.265 mac: MAC address to lease the IP address against.
@@ -267,19 +268,19 @@
267 LOG.debug(_("Leasing IP %s"), address, context=context)268 LOG.debug(_("Leasing IP %s"), address, context=context)
268 fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)269 fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
269 ethernet_card = fixed_ip_ref['ethernet_card']270 ethernet_card = fixed_ip_ref['ethernet_card']
270 instance = nova_db_api.instance_get_by_virtual_nic(context, 271 instance = nova_db_api.instance_get_by_virtual_nic(context,
271 ethernet_card['id'])272 ethernet_card['id'])
272 if not instance:273 if not instance:
273 raise exception.Error(_("IP %s leased that isn't associated") %274 raise exception.Error(_("IP %s leased that isn't associated") %
274 address)275 address)
275 276
276 if nova_db_api.is_user_context(context):277 if nova_db_api.is_user_context(context):
277 # Get the project for this IP278 # Get the project for this IP
278 nova_db_api.authorize_project_context(context, instance.project_id)279 nova_db_api.authorize_project_context(context, instance.project_id)
279 280
280 if ethernet_card['mac_address'] != mac:281 if ethernet_card['mac_address'] != mac:
281 raise exception.Error(_("IP %(address)s leased to bad"282 raise exception.Error(_("IP %(address)s leased to bad"
282 " mac %s vs %s") % (ethernet_card['mac_address'], mac)) 283 " mac %s vs %s") % (ethernet_card['mac_address'], mac))
283 now = datetime.datetime.utcnow()284 now = datetime.datetime.utcnow()
284 self.db.fixed_ip_update(context,285 self.db.fixed_ip_update(context,
285 fixed_ip_ref['id'],286 fixed_ip_ref['id'],
@@ -288,13 +289,13 @@
288 if not fixed_ip_ref['allocated']:289 if not fixed_ip_ref['allocated']:
289 LOG.warn(_("IP %s leased that was already deallocated"), address,290 LOG.warn(_("IP %s leased that was already deallocated"), address,
290 context=context)291 context=context)
291 292
292 def get_dhcp_host_leases(self, context, interface):293 def get_dhcp_host_leases(self, context, interface):
293 """Gets a list of leased hosts.294 """Gets a list of leased hosts.
294 295
295 Args:296 Args:
296 context: Nova context297 context: Nova context
297 interface: network interface to get the DHCP leases. 298 interface: network interface to get the DHCP leases.
298 """299 """
299 network_ref = self.db.network_get_by_bridge(context, interface)300 network_ref = self.db.network_get_by_bridge(context, interface)
300 hosts = []301 hosts = []
@@ -305,21 +306,21 @@
305 instance = nova_db_api.\306 instance = nova_db_api.\
306 instance_get_by_virtual_nic(context,307 instance_get_by_virtual_nic(context,
307 ethernet_card['id'])308 ethernet_card['id'])
308 309
309 if instance['updated_at']:310 if instance['updated_at']:
310 timestamp = instance['updated_at']311 timestamp = instance['updated_at']
311 else:312 else:
312 timestamp = instance['created_at']313 timestamp = instance['created_at']
313 314
314 hosts.append(DhcpHost(mac_address=ethernet_card['mac_address'],315 hosts.append(DhcpHost(mac_address=ethernet_card['mac_address'],
315 hostname=instance['hostname'],316 hostname=instance['hostname'],
316 ip_address=fixed_ip_ref['address'],317 ip_address=fixed_ip_ref['address'],
317 timestamp=timestamp))318 timestamp=timestamp))
318 return self.dhcp_driver.get_dhcp_leases(hosts)319 return self.dhcp_driver.get_dhcp_leases(hosts)
319 320
320 def release_fixed_ip(self, context, mac, address):321 def release_fixed_ip(self, context, mac, address):
321 """Called by dhcp-bridge when ip is released.322 """Called by dhcp-bridge when ip is released.
322 323
323 Args:324 Args:
324 context: Nova context325 context: Nova context
325 mac: MAC address to release the IP against.326 mac: MAC address to release the IP against.
@@ -328,7 +329,7 @@
328 LOG.debug(_("Releasing IP %s"), address, context=context)329 LOG.debug(_("Releasing IP %s"), address, context=context)
329 fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)330 fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
330 ethernet_card = fixed_ip_ref['ethernet_card']331 ethernet_card = fixed_ip_ref['ethernet_card']
331 instance = nova_db_api.instance_get_by_virtual_nic(context, 332 instance = nova_db_api.instance_get_by_virtual_nic(context,
332 ethernet_card['id'])333 ethernet_card['id'])
333 if not instance:334 if not instance:
334 raise exception.Error(_("IP %s released that isn't associated") %335 raise exception.Error(_("IP %s released that isn't associated") %
@@ -340,7 +341,7 @@
340341
341 if ethernet_card['mac_address'] != mac:342 if ethernet_card['mac_address'] != mac:
342 raise exception.Error(_("IP %(address)s leased to bad"343 raise exception.Error(_("IP %(address)s leased to bad"
343 " mac %s vs %s") % (ethernet_card['mac_address'], mac)) 344 " mac %s vs %s") % (ethernet_card['mac_address'], mac))
344345
345 if not fixed_ip_ref['leased']:346 if not fixed_ip_ref['leased']:
346 LOG.warn(_("IP %s released that was not leased"), address,347 LOG.warn(_("IP %s released that was not leased"), address,
@@ -355,25 +356,45 @@
355 # the code below will update the file if necessary356 # the code below will update the file if necessary
356 if FLAGS.net_flat_vlan_update_dhcp_on_disassociate:357 if FLAGS.net_flat_vlan_update_dhcp_on_disassociate:
357 network_ref = fixed_ip['network']358 network_ref = fixed_ip['network']
358 hosts = self._get_dhcp_hosts(context, network_ref['id']) 359 hosts = self._get_dhcp_hosts(context, network_ref['id'])
359 self.dhcp_driver.update_dhcp(network_ref['bridge'], 360 self.dhcp_driver.update_dhcp(network_ref['bridge'],
360 network_ref['gateway'], 361 network_ref['gateway'],
361 network_ref['dhcp_start'], 362 network_ref['dhcp_start'],
362 hosts,363 hosts,
363 dhcp_script=FLAGS.\364 dhcp_script=FLAGS.\
364 net_flat_vlan_dhcpbridge)365 net_flat_vlan_dhcpbridge)
365366
366 def disassociate_floating_ip(self, context, floating_address):367 def get_host(self, context):
367 """Disassociates a floating ip.368 """Gets the host of the network service.
368 369
369 Args:370 Args:
370 context: Nova context371 context: Nova context object. Unused here.
371 floating_address: Floating IP address to diassociate.372 """
372 """373 return self.host
373 fixed_address = self.db.floating_ip_disassociate(context,374
374 floating_address)375 def activate_public_ip(self, context, floating_address, fixed_address):
376 """Associates an floating ip to a fixed ip.
377
378 Args:
379 context: Nova context
380 floating_address: Floating IP address to map to fixed IP address.
381 fixed_address: Fixed IP address to map the floating IP address to.
382 """
383 self.driver.bind_floating_ip(floating_address)
384 self.filter_driver.ensure_floating_forward(floating_address,
385 fixed_address)
386
387 def deactivate_public_ip(self, context, floating_address, fixed_address):
388 """Disassociates floating address and fixed address.
389
390 Args:
391 context: Nova context
392 floating_address: Floating IP address to map to fixed IP address.
393 fixed_address: Fixed IP address to map the floating IP address to.
394 """
375 self.driver.unbind_floating_ip(floating_address)395 self.driver.unbind_floating_ip(floating_address)
376 self.driver.remove_floating_forward(floating_address, fixed_address)396 self.filter_driver.remove_floating_forward(floating_address,
397 fixed_address)
377398
378399
379class DHCPEventHandler(object):400class DHCPEventHandler(object):
@@ -388,7 +409,7 @@
388 return NetworkService().get_dhcp_host_leases(ctxt, interface)409 return NetworkService().get_dhcp_host_leases(ctxt, interface)
389 else:410 else:
390 return rpc.cast(ctxt,411 return rpc.cast(ctxt,
391 "%s.%s" % (FLAGS.net_flat_vlan_network_topic, 412 "%s.%s" % (FLAGS.net_flat_vlan_network_topic,
392 FLAGS.host),413 FLAGS.host),
393 {"method": "get_dhcp_host_leases",414 {"method": "get_dhcp_host_leases",
394 "args": {"interface": interface}})415 "args": {"interface": interface}})
@@ -402,7 +423,7 @@
402 return NetworkService().lease_fixed_ip(ctxt, mac, ip_address)423 return NetworkService().lease_fixed_ip(ctxt, mac, ip_address)
403 else:424 else:
404 rpc.cast(ctxt,425 rpc.cast(ctxt,
405 "%s.%s" % (FLAGS.net_flat_vlan_network_topic, 426 "%s.%s" % (FLAGS.net_flat_vlan_network_topic,
406 FLAGS.host),427 FLAGS.host),
407 {"method": "lease_fixed_ip",428 {"method": "lease_fixed_ip",
408 "args": {"mac": mac,429 "args": {"mac": mac,
@@ -423,9 +444,9 @@
423 NetworkService().release_fixed_ip(ctxt, mac, ip_address)444 NetworkService().release_fixed_ip(ctxt, mac, ip_address)
424 else:445 else:
425 rpc.cast(ctxt,446 rpc.cast(ctxt,
426 "%s.%s" % (FLAGS.net_flat_vlan_network_topic, 447 "%s.%s" % (FLAGS.net_flat_vlan_network_topic,
427 FLAGS.host),448 FLAGS.host),
428 {"method": "release_fixed_ip",449 {"method": "release_fixed_ip",
429 "args": {"mac": mac,450 "args": {"mac": mac,
430 "address": ip_address}})451 "address": ip_address}})
431 452
432453
=== modified file 'nova/network/service.py'
--- nova/network/service.py 2011-04-04 09:52:18 +0000
+++ nova/network/service.py 2011-04-06 17:14:53 +0000
@@ -19,14 +19,14 @@
19 service management."""19 service management."""
2020
21from nova import db21from nova import db
22from nova import flags 22from nova import flags
23from nova import utils23from nova import utils
2424
25from zope import interface25from zope import interface
2626
27# Flag definitions27# Flag definitions
28FLAGS = flags.FLAGS28FLAGS = flags.FLAGS
29flags.DEFINE_string('network_service_conf', 29flags.DEFINE_string('network_service_conf',
30 '/etc/nova/nova-network-service.conf',30 '/etc/nova/nova-network-service.conf',
31 'Path to the network service configuration file.')31 'Path to the network service configuration file.')
32flags.DEFINE_string('default_network_service', 'nova.network.flat_vlan',32flags.DEFINE_string('default_network_service', 'nova.network.flat_vlan',
@@ -37,102 +37,117 @@
3737
38 def get_net_agent():38 def get_net_agent():
39 """Gets the Net agent service object.39 """Gets the Net agent service object.
40 40
41 Returns:41 Returns:
42 Net agent object.42 Net agent object.
43 """43 """
44 44
45 def get_api_service():45 def get_api_service():
46 """Gets the API service object.46 """Gets the API service object.
47 47
48 Returns:48 Returns:
49 Network API service object.49 Network API service object.
50 """50 """
51 51
52 def get_os_api_service():52 def get_os_api_service():
53 """Gets the OpenStack API service object.53 """Gets the OpenStack API service object.
54 54
55 Returns:55 Returns:
56 Network OpenStack API service object.56 Network OpenStack API service object.
57 """57 """
5858
59class INetworkApiService(interface.Interface):59class INetworkApiService(interface.Interface):
60 """A Network API service object."""60 """A Network API service object."""
61 61
62 def create_vnic():62 def create_vnic():
63 """Creates a new VNIC.63 """Creates a new VNIC.
64 64
65 Returns:65 Returns:
66 The ID of the new VNIC.66 The ID of the new VNIC.
67 """67 """
68 68
69 def get_project_vpn_address_and_port(context, project_id):69 def get_project_vpn_address_and_port(context, project_id):
70 """Gets the VPN IP address and port for a project70 """Gets the VPN IP address and port for a project
71 71
72 Args:72 Args:
73 context: Nova context object needed to access DB.73 context: Nova context object needed to access DB.
74 project_id: Project to get the VPN IP and port for.74 project_id: Project to get the VPN IP and port for.
75 75
76 Returns:76 Returns:
77 A tuple of VPN IP address and port number.77 A tuple of VPN IP address and port number.
78 """78 """
7979
80 def allocate_address(context, project_id, ip_quota):
81 """Gets the number of floating IPs associated with a project.
82
83 Args:
84 context: Nova context object needed to access the DB.
85 project_id: Project to allocate the address from.
86 ip_quota: Quota for IP addresses.
87
88 Returns:
89 An IP address.
90
91 Raises:
92 quota.QuotaError: Over the quota limit.
93 """
94
80class INetworkOpenStackApiService(interface.Interface):95class INetworkOpenStackApiService(interface.Interface):
81 """An OpenStack Network API service object."""96 """An OpenStack Network API service object."""
82 97
83 def set_routes(route_map):98 def set_routes(route_map):
84 """Set necessary routes for the plugin.99 """Set necessary routes for the plugin.
85 100
86 Args:101 Args:
87 route_map: NetworkServiceRouteMap object to add the routes to.102 route_map: NetworkServiceRouteMap object to add the routes to.
88 """ 103 """
89104
90class INetworkAgentService(interface.Interface):105class INetworkAgentService(interface.Interface):
91 """An OpenStack network agent service object."""106 """An OpenStack network agent service object."""
92 107
93 def bind_vnics_to_ports(context, vnic_ids, is_vpn):108 def bind_vnics_to_ports(context, vnic_ids, is_vpn):
94 """Gets a fixed ips from the pool.109 """Gets a fixed ips from the pool.
95 110
96 Args:111 Args:
97 context: Nova context needed for DB access.112 context: Nova context needed for DB access.
98 vnic_ids: list of VNIC IDs113 vnic_ids: list of VNIC IDs
99 is_vpn: Boolean to check if the call is for VPN. 114 is_vpn: Boolean to check if the call is for VPN.
100 """115 """
101 116
102 def setup_compute_network(context, vnic_ids, is_vpn):117 def setup_compute_network(context, vnic_ids, is_vpn):
103 """Set up the compute node for networking.118 """Set up the compute node for networking.
104 119
105 Args:120 Args:
106 context: Nova context needed for DB access.121 context: Nova context needed for DB access.
107 vnic_ids: list of VNIC IDs122 vnic_ids: list of VNIC IDs
108 is_vpn: Boolean to check if the call is for VPN.123 is_vpn: Boolean to check if the call is for VPN.
109 """124 """
110 125
111 def teardown_compute_network(context, vnic_ids):126 def teardown_compute_network(context, vnic_ids):
112 """Clean up the compute node.127 """Clean up the compute node.
113 128
114 Args:129 Args:
115 context: Nova context needed for DB access.130 context: Nova context needed for DB access.
116 vnic_ids: list of VNIC IDs131 vnic_ids: list of VNIC IDs
117 """132 """
118 133
119 def requires_file_injection():134 def requires_file_injection():
120 """Indicates whether the plugin requires file injection.135 """Indicates whether the plugin requires file injection.
121 136
122 Returns:137 Returns:
123 True if the plugin requires file injection.138 True if the plugin requires file injection.
124 """139 """
125 140
126 def get_network_info(context, vnic_id):141 def get_network_info(context, vnic_id):
127 """Gets network data for a given VNIC.142 """Gets network data for a given VNIC.
128 143
129 Args:144 Args:
130 context: Nova context needed for DB access.145 context: Nova context needed for DB access.
131 vnic_id: VNIC ID to get the network information for.146 vnic_id: VNIC ID to get the network information for.
132 147
133 Returns:148 Returns:
134 A dictionary containing the following keys:149 A dictionary containing the following keys:
135 cidr, cidrv6, netmask, netmask_v6 gateway, gateway_v6, 150 cidr, cidrv6, netmask, netmask_v6 gateway, gateway_v6,
136 dhcp_server, broadcast, dns, mac_address,151 dhcp_server, broadcast, dns, mac_address,
137 ip_address, bridge, gateway_v6, and netmast_v6152 ip_address, bridge, gateway_v6, and netmast_v6
138 """153 """
@@ -142,7 +157,7 @@
142157
143 def __init__(self, mapper, service):158 def __init__(self, mapper, service):
144 """Initialize a NetworkServiceRouteMap object.159 """Initialize a NetworkServiceRouteMap object.
145 160
146 Args:161 Args:
147 mapper: python-routes mapper object to wrap.162 mapper: python-routes mapper object to wrap.
148 service: module of the network plugin service.163 service: module of the network plugin service.
@@ -152,34 +167,34 @@
152167
153 def resource(self, member_name, collection_name, **kwargs):168 def resource(self, member_name, collection_name, **kwargs):
154 """Wrapper method for 'resource' method in python-routes mapper object.169 """Wrapper method for 'resource' method in python-routes mapper object.
155 170
156 This method does the same thing 'resource' method does for python-routes171 This method does the same thing 'resource' method does for python-routes
157 module, but it also adds a route prefix from the network service name. 172 module, but it also adds a route prefix from the network service name.
158 173
159 Args:174 Args:
160 member_name: REST member routes175 member_name: REST member routes
161 collection_name: REST collection routes176 collection_name: REST collection routes
162 """177 """
163 _, _, package_name = str(self._service.__package__).rpartition('.')178 _, _, package_name = str(self._service.__package__).rpartition('.')
164 kwargs.pop('path_prefix', None)179 kwargs.pop('path_prefix', None)
165 self._mapper.resource(member_name, collection_name, 180 self._mapper.resource(member_name, collection_name,
166 path_prefix='/%s' % package_name, **kwargs)181 path_prefix='/%s' % package_name, **kwargs)
167182
168class NetworkServiceManager(object):183class NetworkServiceManager(object):
169 """Implements Borg design pattern to have a shared state of network 184 """Implements Borg design pattern to have a shared state of network
170 services.185 services.
171 """186 """
172187
173 __shared_state = {}188 __shared_state = {}
174 _services = {}189 _services = {}
175 190
176 def __init__(self):191 def __init__(self):
177 """Initialize services member dictionary variable.192 """Initialize services member dictionary variable.
178 """193 """
179 self.__dict__ = self.__shared_state194 self.__dict__ = self.__shared_state
180195
181 def _import_services(self):196 def _import_services(self):
182 """Loads the content of the configuration file and imports the 197 """Loads the content of the configuration file and imports the
183 modules.198 modules.
184 """199 """
185 self._services = {}200 self._services = {}
@@ -190,11 +205,11 @@
190 continue205 continue
191 self._services[line] = utils.import_object(line)206 self._services[line] = utils.import_object(line)
192 f.close()207 f.close()
193 208
194 @property209 @property
195 def _services_dict(self):210 def _services_dict(self):
196 """Gets the services dictionary. Loads the services if empty.211 """Gets the services dictionary. Loads the services if empty.
197 212
198 Returns:213 Returns:
199 A dictionary of service name to service modules.214 A dictionary of service name to service modules.
200 """215 """
@@ -205,11 +220,11 @@
205 @classmethod220 @classmethod
206 def get_service_name(cls, context, project_id):221 def get_service_name(cls, context, project_id):
207 """Gets the network service given a project ID.222 """Gets the network service given a project ID.
208 223
209 Args:224 Args:
210 context: Nova context object needed for DB access.225 context: Nova context object needed for DB access.
211 project_id: Project ID to get the service of.226 project_id: Project ID to get the service of.
212 227
213 Returns:228 Returns:
214 Network service name for the project.229 Network service name for the project.
215 """230 """
@@ -220,20 +235,20 @@
220235
221 def get_service(self, context, project_id):236 def get_service(self, context, project_id):
222 """Gets the network service given a project ID.237 """Gets the network service given a project ID.
223 238
224 Args:239 Args:
225 context: Nova context object needed for DB access.240 context: Nova context object needed for DB access.
226 project_id: Project ID to get the service of.241 project_id: Project ID to get the service of.
227 242
228 Returns:243 Returns:
229 Network service module for the project.244 Network service module for the project.
230 """245 """
231 service = self.get_service_name(context, project_id)246 service = self.get_service_name(context, project_id)
232 return self._services_dict[service]247 return self._services_dict[service]
233 248
234 def get_all_services(self):249 def get_all_services(self):
235 """Gets all the network services available.250 """Gets all the network services available.
236 251
237 Returns:252 Returns:
238 Network service modules available.253 Network service modules available.
239 """254 """
@@ -241,21 +256,21 @@
241256
242def get_service_factory(context, project_id):257def get_service_factory(context, project_id):
243 """Gets the factory object for network service.258 """Gets the factory object for network service.
244 259
245 Args:260 Args:
246 context: Nova context needed for accessing DB.261 context: Nova context needed for accessing DB.
247 project_id: Project ID to get the appropriate network service.262 project_id: Project ID to get the appropriate network service.
248 263
249 Returns:264 Returns:
250 A tuple of the service module and its NetworkServiceFactory object.265 A tuple of the service module and its NetworkServiceFactory object.
251 """266 """
252 manager = NetworkServiceManager()267 manager = NetworkServiceManager()
253 service = manager.get_service(context, project_id)268 service = manager.get_service(context, project_id)
254 return (service, service.NetworkServiceFactory())269 return (service, service.NetworkServiceFactory())
255 270
256def get_all_service_factories():271def get_all_service_factories():
257 """Gets all the factory objects for network services.272 """Gets all the factory objects for network services.
258 273
259 Returns:274 Returns:
260 A list of tuples of service module and its factory.275 A list of tuples of service module and its factory.
261 """276 """
262277
=== modified file 'nova/virt/libvirt_conn.py'
--- nova/virt/libvirt_conn.py 2011-04-04 09:52:18 +0000
+++ nova/virt/libvirt_conn.py 2011-04-06 17:14:53 +0000
@@ -238,10 +238,7 @@
238238
239 vnics = db.virtual_nics_get_by_instance(ctxt, instance['id'])239 vnics = db.virtual_nics_get_by_instance(ctxt, instance['id'])
240 for vnic_id in vnics:240 for vnic_id in vnics:
241 try:241 self.firewall_driver.prepare_vnic_filter(vnic_id, instance)
242 self.firewall_driver.prepare_vnic_filter(vnic_id, instance)
243 except:
244 pass
245242
246 def _get_connection(self):243 def _get_connection(self):
247 if not self._wrapped_conn or not self._test_connection():244 if not self._wrapped_conn or not self._test_connection():

Subscribers

People subscribed via source and target branches

to status/vote changes: