Merge lp:~citrix-openstack/nova/xenapi-vlan-network-manager into lp:~hudson-openstack/nova/trunk
- xenapi-vlan-network-manager
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Matt Dietz |
Approved revision: | 705 |
Merged at revision: | 906 |
Proposed branch: | lp:~citrix-openstack/nova/xenapi-vlan-network-manager |
Merge into: | lp:~hudson-openstack/nova/trunk |
Diff against target: |
1378 lines (+429/-226) 9 files modified
nova/network/linux_net.py (+0/-3) nova/network/manager.py (+2/-0) nova/network/xenapi_net.py (+85/-0) nova/tests/db/fakes.py (+44/-21) nova/tests/fake_utils.py (+10/-7) nova/tests/test_xenapi.py (+54/-45) nova/virt/xenapi/fake.py (+107/-65) nova/virt/xenapi/network_utils.py (+17/-2) nova/virt/xenapi/vmops.py (+110/-83) |
To merge this branch: | bzr merge lp:~citrix-openstack/nova/xenapi-vlan-network-manager |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
termie (community) | Needs Fixing | ||
Trey Morris (community) | Approve | ||
Thierry Carrez (community) | ffe | Approve | |
Matt Dietz (community) | Approve | ||
Review via email: mp+53660@code.launchpad.net |
Commit message
Added VLAN networking support for XenAPI
Description of the change
The branch proposed for merged implements the xenapi-
It extends VLAN network manager supports to compute nodes running xenapi.
To do so, we create a xenapi specific network driver, which implements only ensure_vlan_bridge.
This way, when the compute node invokes NetworkManager.
Note: the network node is not affected at all by this change, and it should therefore be executed using the linux_net driver as usual. The compute node should be executed with the xenapi_net driver instead.
Salvatore Orlando (salvatore-orlando) wrote : | # |
Hi Matt,
thanks for reviewing this branch
> I merged with trunk and got a conflict in network/manager.py
>
> <<<<<<< TREE
> flags.DEFINE_
> 'network device for vlans')
> flags.DEFINE_
> =======
> flags.DEFINE_
> >>>>>>> MERGE-SOURCE
>
I guess the conflict has arisen from a recent merge (the one for solving a bug in nova-manage). I will merge my branch with trunk, and push again...
> Also had a unit-test error in one of the test files, so the rest of the XenAPI
> tests aren't currently running. Forget a bzr add?
...and when pushing, this time I will not forget to add fake_utils.py!
>
> =======
> ERROR: Failure: ImportError (cannot import name fake_utils)
> -------
> Traceback (most recent call last):
> File "/usr/local/
> loadTestsFromName
> addr.filename, addr.module)
> File "/usr/local/
> importFromPath
> return self.importFrom
> File "/usr/local/
> importFromDir
> mod = load_module(
> File "/root/
> manager/
> from nova.tests import fake_utils
> ImportError: cannot import name fake_utils
>
> -------
>
> 36) Shouldn't this be the citrix copyright?
>
Yes, it should. Shame on me...
> 623-632) this seems really odd to me. What's an example of how we would
> actually fail into the exception block?
>
I agree this try/except block is quite weird.
The main reason is that find_network_
However, while this is true for flat networking (in which we tipically set the flag to xenbr0 or something like that), this does not work anymore with VLAN networks. In XenAPI names for bridges associated with VLAN networks are auto-generated. For instance, for VLAN 111 it will not be possible to call the bridge br111, but a name like xapiXX (where XX is an integer) will be generated.
To solve this problem, when we create a VLAN network in XenAPI, we set its 'name' attribute to the value of the 'bridge' column in the name db. We then lookup for the appropriate network by looking at the 'name' attribute instead of the 'bridge' one.
The try/except block first tries to identify the network by bridge, and then, if it fails, tries with the 'name' attribute. I agree however that this does not look good at all. I will therefore shift the burden of the bridge/name lookup into NetworkHelper, implementing a method that looks for both properties and then returns the first network for which either the 'bridge' or the 'nam...
Salvatore Orlando (salvatore-orlando) wrote : | # |
The branch has been updated and a new round of review is kindly asked.
Matt's comments have been addressed as follows:
- resolved conflicts with trunk (this does not exclude new conflicts in the future, though!)
- added missing fake_utils.py file
- changed copyright header in xenapi_net.py
- removed exception block in create_vif, changed find_network_
- Fixed missing whitespace problem with debug line
Also, the unit test for spawning VM on VLAN network has been slightly changed to reflect changes in the code
- 689. By Salvatore Orlando
-
removed excess debug line
- 690. By Salvatore Orlando
-
merge trunk
- 691. By Salvatore Orlando
-
merge trunk
- 692. By Salvatore Orlando
-
merge trunk
- 693. By Salvatore Orlando
-
merge trunk
Salvatore Orlando (salvatore-orlando) wrote : | # |
Requesting FF exception for this branch, as it has already 1 approve vote and there's no major dispute on the way it is implemented.
The branch touches two areas:
- VM spawn procedure for xenapi backend (thoroughly tested)
- Network driver for xenapi backend (new area - a test has been added in test_xenapi which offers good code coverage)
Benefit:
- this branch will enable support the Vlan Network manager on XenAPI backends. This support has also been implemented for VSphere backends in a branch that was merged yesterday.
Risk assessment:
- No risk for network manager, as its code has not been touched at all. The xenapi network driver will be used only by the compute node for setting up VLANs on xen hosts. This also means that there cannot be any impact on other capabilities of the Network Manager, such as VPN access or Floating IPs.
- Since this is a new feature, and the associated code is executed only when a compute node uses XenAPI and the VLan manager, there is no risk of breaking libvirt, hyperv, or mware installations, or XenAPI installations which use the flat manager
- 694. By Salvatore Orlando
-
merge trunk
Thierry Carrez (ttx) wrote : | # |
This is also a bit large, but relatively self-contained. It needs to get merged soon, FFe granted for merging by Tuesday evening.
Trey Morris (tr3buchet) wrote : | # |
Looks good. Unless I'm missing something multi-nic and this should get along just fine unless you want to have vlans on multiple interfaces, in which case setting the vlan_interface with a flag isnt'a good idea. The flat_network_bridge flag is going away with multi-nic, perhaps vlan_interface will need to go away too?
approve for now but we should probably discuss.
Trey Morris (tr3buchet) wrote : | # |
214 + def fake_network_
215 + l = []
216 + #even instance numbers are on vlan networks
217 + if instance_id % 2 == 0:
218 + l.append(
219 + else:
220 + l.append(
221 + return l
missed this... please don't use 'l' as a variable name (lower case L).
Trey Morris (tr3buchet) wrote : | # |
as this is down to the wire...
if I'm asleep or not around when 'l' is fixed, as that's all my approval is hinged upon, feel free to approve out from under me or whatever to get it in
Salvatore Orlando (salvatore-orlando) wrote : | # |
> as this is down to the wire...
> if I'm asleep or not around when 'l' is fixed, as that's all my approval is
> hinged upon, feel free to approve out from under me or whatever to get it in
Hi Trey, I'm going to update the 'l' thing right now.
As regards vlan_interface, its semantics are probably different from flat_network_
flat_network_bridge tells (or least it used to tell!) nova-compute which bridge should be used for VIFs. Of course, with multiple VIFs per VM, this does not make sense anymore.
vlan_interface instead represents the physical interface on the hypervisor we will use for creating the bridge for the vlan network.
In libvirt this means using vconfig to create a vlan on vlan_interface, create a new bridge, and then attach the vlan interface to this bridge; the virtual interface is then attached to newly created bridge.
In xenapi instead this means using xapi to create a network, retrieve the PIF associated with vlan_interface and then create a VLAN object using the network and the PIF. The VIF is then attached to the newly created network.
Summarizing I think that after multi-nic we will still need vlan_interface. What we will need to discuss is probably the way in which we create these bridges. Currently the nova-compute node does that, probably we want to let the network service do all networking-related arrangements.
- 695. By Salvatore Orlando
-
merge trunk
addressing Trey's comments
Salvatore Orlando (salvatore-orlando) wrote : | # |
Branch updated.
Lowercase 'L' not used anymore.
Merge with trunk (rev 892)
termie (termie) wrote : | # |
- please put a space between the # and the comment in all cases, preferably with Sentence case
- please end your docstrings with periods in all cases
- line 87: please put the beginning of the dictionary as the same line as the opening curly brace and indent the next lines to match
- line 93: please us parens to wrap the multiline expression rather than escaping the newline, as in:
expr = (foo == BLAH
and bar < 3)
- line 104: please place one argument per line, not two, or place them all on the same line on the next line and double indent
- line 114: please use != instead of <>
- please adjust all docstrings to match the formatting described in HACKING
- line 362: you have a commented out line that should probably be removed
- in xenapi tests you want 'from nova import log as logging' rather than as LOG, and you want it in the correct place in the import list, as described in HACKING
- line 454: please indent this line to match the other arguments
- line 619: the newline does not need to be escaped
- line 656: please put the beginning of the dict contents on teh first line and indent the rest of the contents to match it
- same for 676 and 684
- line 699: the correct format is # TODO(salvatore-
- line 740: parens instead of escaped newlines, please
- same for line 777
- 696. By Armando Migliaccio
-
addressed termies review (first round)
- 697. By Armando Migliaccio
-
addressed termie's review (second round)
- 698. By Armando Migliaccio
-
addressed termies review (third round)
Armando Migliaccio (armando-migliaccio) wrote : | # |
Folks,
thanks very much for the review. This last round was regarding coding style. I have addressed issues raised by Termie, whereas Salvatore addressed Trey's.
I guess we are ready to roll now.
Cheers,
Armando
- 699. By Armando Migliaccio
-
merge trunk
Trey Morris (tr3buchet) wrote : | # |
looks good to me
- 700. By Armando Migliaccio
-
merge trunk
termie (termie) wrote : | # |
thanks for the style cleanups :) there were still a few that didn't quite make it:
- single-line docstrings should be on a single line:
"""Single line docstring."""
not
"""
Single line docstring.
"""
- Multiline docstrings consist of a one-line summary on the first line, followed by a newline, followed by a more detailed description, such as:
"""Single line summary.
More detailed description.
"""
(We have the extra newline at the end to be nice to emacs users).
- you need some extra newlines at the top of nova/network/
- when importing import only modules, not objects directly, for example instead of
+from nova.virt.
you would want
from nova.virt.xenapi import network_utils
and then later on in your code you use network_
I agree this gets a little verbose do to the current code style in the xenapi tools, that code currently has a bit of a java fragrance, but this is the python way (we don't use static methods or factories nearly as much as java).
- when you define _CLASSES in fake.py you don't need to escape the newline (remove the backslash)
- in _create_local_sr a common way of dealing with long lines in that situation is to move everything to the next line and double indent, rather than escaping a new line, for example:
sr_ref = _create_object(
'SR',
'type': 'lvm',
'shared': False,
'VDIs': []})
Armando Migliaccio (armando-migliaccio) wrote : | # |
thanks for your thorough review. Please see my comments inline
> thanks for the style cleanups :) there were still a few that didn't quite make
> it:
>
> - single-line docstrings should be on a single line:
>
> """Single line docstring."""
>
> not
>
> """
> Single line docstring.
> """
>
> - Multiline docstrings consist of a one-line summary on the first line,
> followed by a newline, followed by a more detailed description, such as:
>
> """Single line summary.
>
> More detailed description.
>
> """
>
> (We have the extra newline at the end to be nice to emacs users).
>
>
I wish I didn't edit those in the first place as they weren't part of the initial diff...I was just acting politely ;)
> - you need some extra newlines at the top of nova/network/
> between the imports and LOG and between LOG and FLAGS
What do you mean? linux_net.py in the same folder has got the same number of lines. Can you clarify this please?
> - when importing import only modules, not objects directly, for example
> instead of
>
> +from nova.virt.
>
> you would want
>
> from nova.virt.xenapi import network_utils
>
> and then later on in your code you use network_
I am not sure this rule has been applied consistently throughout the code, but I'll oblige. On a more general note, it'd be nice to have a summarized set of Coding Style Guidelines on the Wiki (some sort of checklist other that http://
>
> I agree this gets a little verbose do to the current code style in the xenapi
> tools, that code currently has a bit of a java fragrance, but this is the
> python way (we don't use static methods or factories nearly as much as java).
>
> - when you define _CLASSES in fake.py you don't need to escape the newline
> (remove the backslash)
>
> - in _create_local_sr a common way of dealing with long lines in that
> situation is to move everything to the next line and double indent, rather
> than escaping a new line, for example:
>
> sr_ref = _create_object(
> 'SR',
> {'name_label': 'Local storage',
> 'type': 'lvm',
> 'content_type': 'user',
> 'shared': False,
> 'physical_size': str(1 << 30),
> 'physical_
> 'virtual_
> 'other_config': {
> 'i18n-original-
> 'i18n-key': 'local-storage'},
> 'VDIs': []})
but then the newline for the inner dictionary is okay?
- 701. By Armando Migliaccio
-
style fixes
- 702. By Armando Migliaccio
-
merge trunk
- 703. By Armando Migliaccio
-
fix docstrings
Armando Migliaccio (armando-migliaccio) wrote : | # |
okay, I hope I caught them all!
Cheers,
Armando
- 704. By Armando Migliaccio
-
merge trunk
- 705. By Armando Migliaccio
-
sorted pep8 errors that were introduced during previous fixes
Preview Diff
1 | === modified file 'nova/network/linux_net.py' |
2 | --- nova/network/linux_net.py 2011-03-24 18:51:38 +0000 |
3 | +++ nova/network/linux_net.py 2011-03-29 10:28:19 +0000 |
4 | @@ -44,13 +44,10 @@ |
5 | flags.DEFINE_string('dhcp_domain', |
6 | 'novalocal', |
7 | 'domain to use for building the hostnames') |
8 | - |
9 | flags.DEFINE_string('networks_path', '$state_path/networks', |
10 | 'Location to keep network config files') |
11 | flags.DEFINE_string('public_interface', 'eth0', |
12 | 'Interface for public IP addresses') |
13 | -flags.DEFINE_string('vlan_interface', 'eth0', |
14 | - 'network device for vlans') |
15 | flags.DEFINE_string('dhcpbridge', _bin_file('nova-dhcpbridge'), |
16 | 'location of nova-dhcpbridge') |
17 | flags.DEFINE_string('routing_source_ip', '$my_ip', |
18 | |
19 | === modified file 'nova/network/manager.py' |
20 | --- nova/network/manager.py 2011-03-24 19:04:24 +0000 |
21 | +++ nova/network/manager.py 2011-03-29 10:28:19 +0000 |
22 | @@ -73,6 +73,8 @@ |
23 | flags.DEFINE_string('flat_network_dhcp_start', '10.0.0.2', |
24 | 'Dhcp start for FlatDhcp') |
25 | flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks') |
26 | +flags.DEFINE_string('vlan_interface', 'eth0', |
27 | + 'network device for vlans') |
28 | flags.DEFINE_integer('num_networks', 1, 'Number of networks to support') |
29 | flags.DEFINE_string('vpn_ip', '$my_ip', |
30 | 'Public IP for the cloudpipe VPN servers') |
31 | |
32 | === added file 'nova/network/xenapi_net.py' |
33 | --- nova/network/xenapi_net.py 1970-01-01 00:00:00 +0000 |
34 | +++ nova/network/xenapi_net.py 2011-03-29 10:28:19 +0000 |
35 | @@ -0,0 +1,85 @@ |
36 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
37 | + |
38 | +# Copyright (c) 2011 Citrix Systems, Inc. |
39 | +# Copyright 2011 OpenStack LLC. |
40 | +# |
41 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
42 | +# not use this file except in compliance with the License. You may obtain |
43 | +# a copy of the License at |
44 | +# |
45 | +# http://www.apache.org/licenses/LICENSE-2.0 |
46 | +# |
47 | +# Unless required by applicable law or agreed to in writing, software |
48 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
49 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
50 | +# License for the specific language governing permissions and limitations |
51 | +# under the License. |
52 | + |
53 | +""" |
54 | +Implements vlans, bridges, and iptables rules using linux utilities. |
55 | +""" |
56 | + |
57 | +import os |
58 | + |
59 | +from nova import db |
60 | +from nova import exception |
61 | +from nova import flags |
62 | +from nova import log as logging |
63 | +from nova import utils |
64 | +from nova.virt.xenapi_conn import XenAPISession |
65 | +from nova.virt.xenapi import network_utils |
66 | + |
67 | +LOG = logging.getLogger("nova.xenapi_net") |
68 | + |
69 | +FLAGS = flags.FLAGS |
70 | + |
71 | + |
72 | +def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): |
73 | + """Create a vlan and bridge unless they already exist.""" |
74 | + # Open xenapi session |
75 | + LOG.debug("ENTERING ensure_vlan_bridge in xenapi net") |
76 | + url = FLAGS.xenapi_connection_url |
77 | + username = FLAGS.xenapi_connection_username |
78 | + password = FLAGS.xenapi_connection_password |
79 | + session = XenAPISession(url, username, password) |
80 | + # Check whether bridge already exists |
81 | + # Retrieve network whose name_label is "bridge" |
82 | + network_ref = network_utils.NetworkHelper.find_network_with_name_label( |
83 | + session, |
84 | + bridge) |
85 | + if network_ref == None: |
86 | + # If bridge does not exists |
87 | + # 1 - create network |
88 | + description = "network for nova bridge %s" % bridge |
89 | + network_rec = {'name_label': bridge, |
90 | + 'name_description': description, |
91 | + 'other_config': {}} |
92 | + network_ref = session.call_xenapi('network.create', network_rec) |
93 | + # 2 - find PIF for VLAN |
94 | + expr = 'field "device" = "%s" and \ |
95 | + field "VLAN" = "-1"' % FLAGS.vlan_interface |
96 | + pifs = session.call_xenapi('PIF.get_all_records_where', expr) |
97 | + pif_ref = None |
98 | + # Multiple PIF are ok: we are dealing with a pool |
99 | + if len(pifs) == 0: |
100 | + raise Exception( |
101 | + _('Found no PIF for device %s') % FLAGS.vlan_interface) |
102 | + # 3 - create vlan for network |
103 | + for pif_ref in pifs.keys(): |
104 | + session.call_xenapi('VLAN.create', |
105 | + pif_ref, |
106 | + str(vlan_num), |
107 | + network_ref) |
108 | + else: |
109 | + # Check VLAN tag is appropriate |
110 | + network_rec = session.call_xenapi('network.get_record', network_ref) |
111 | + # Retrieve PIFs from network |
112 | + for pif_ref in network_rec['PIFs']: |
113 | + # Retrieve VLAN from PIF |
114 | + pif_rec = session.call_xenapi('PIF.get_record', pif_ref) |
115 | + pif_vlan = int(pif_rec['VLAN']) |
116 | + # Raise an exception if VLAN != vlan_num |
117 | + if pif_vlan != vlan_num: |
118 | + raise Exception(_("PIF %(pif_rec['uuid'])s for network " |
119 | + "%(bridge)s has VLAN id %(pif_vlan)d. " |
120 | + "Expected %(vlan_num)d") % locals()) |
121 | |
122 | === modified file 'nova/tests/db/fakes.py' |
123 | --- nova/tests/db/fakes.py 2011-03-25 01:23:33 +0000 |
124 | +++ nova/tests/db/fakes.py 2011-03-29 10:28:19 +0000 |
125 | @@ -25,7 +25,7 @@ |
126 | |
127 | |
128 | def stub_out_db_instance_api(stubs, injected=True): |
129 | - """ Stubs out the db API for creating Instances """ |
130 | + """Stubs out the db API for creating Instances.""" |
131 | |
132 | INSTANCE_TYPES = { |
133 | 'm1.tiny': dict(memory_mb=512, |
134 | @@ -56,27 +56,39 @@ |
135 | flavorid=5, |
136 | rxtx_cap=5)} |
137 | |
138 | - network_fields = { |
139 | - 'id': 'test', |
140 | - 'bridge': 'xenbr0', |
141 | - 'label': 'test_network', |
142 | - 'netmask': '255.255.255.0', |
143 | - 'cidr_v6': 'fe80::a00:0/120', |
144 | - 'netmask_v6': '120', |
145 | - 'gateway': '10.0.0.1', |
146 | - 'gateway_v6': 'fe80::a00:1', |
147 | - 'broadcast': '10.0.0.255', |
148 | - 'dns': '10.0.0.2', |
149 | - 'ra_server': None, |
150 | - 'injected': injected} |
151 | - |
152 | - fixed_ip_fields = { |
153 | - 'address': '10.0.0.3', |
154 | - 'address_v6': 'fe80::a00:3', |
155 | - 'network_id': 'test'} |
156 | + flat_network_fields = {'id': 'fake_flat', |
157 | + 'bridge': 'xenbr0', |
158 | + 'label': 'fake_flat_network', |
159 | + 'netmask': '255.255.255.0', |
160 | + 'cidr_v6': 'fe80::a00:0/120', |
161 | + 'netmask_v6': '120', |
162 | + 'gateway': '10.0.0.1', |
163 | + 'gateway_v6': 'fe80::a00:1', |
164 | + 'broadcast': '10.0.0.255', |
165 | + 'dns': '10.0.0.2', |
166 | + 'ra_server': None, |
167 | + 'injected': injected} |
168 | + |
169 | + vlan_network_fields = {'id': 'fake_vlan', |
170 | + 'bridge': 'br111', |
171 | + 'label': 'fake_vlan_network', |
172 | + 'netmask': '255.255.255.0', |
173 | + 'cidr_v6': 'fe80::a00:0/120', |
174 | + 'netmask_v6': '120', |
175 | + 'gateway': '10.0.0.1', |
176 | + 'gateway_v6': 'fe80::a00:1', |
177 | + 'broadcast': '10.0.0.255', |
178 | + 'dns': '10.0.0.2', |
179 | + 'ra_server': None, |
180 | + 'vlan': 111, |
181 | + 'injected': False} |
182 | + |
183 | + fixed_ip_fields = {'address': '10.0.0.3', |
184 | + 'address_v6': 'fe80::a00:3', |
185 | + 'network_id': 'fake_flat'} |
186 | |
187 | class FakeModel(object): |
188 | - """ Stubs out for model """ |
189 | + """Stubs out for model.""" |
190 | def __init__(self, values): |
191 | self.values = values |
192 | |
193 | @@ -96,10 +108,19 @@ |
194 | return INSTANCE_TYPES[name] |
195 | |
196 | def fake_network_get_by_instance(context, instance_id): |
197 | + # Even instance numbers are on vlan networks |
198 | + if instance_id % 2 == 0: |
199 | + return FakeModel(vlan_network_fields) |
200 | + else: |
201 | + return FakeModel(flat_network_fields) |
202 | return FakeModel(network_fields) |
203 | |
204 | def fake_network_get_all_by_instance(context, instance_id): |
205 | - return [FakeModel(network_fields)] |
206 | + # Even instance numbers are on vlan networks |
207 | + if instance_id % 2 == 0: |
208 | + return [FakeModel(vlan_network_fields)] |
209 | + else: |
210 | + return [FakeModel(flat_network_fields)] |
211 | |
212 | def fake_instance_get_fixed_address(context, instance_id): |
213 | return FakeModel(fixed_ip_fields).address |
214 | @@ -111,6 +132,8 @@ |
215 | return [FakeModel(fixed_ip_fields)] |
216 | |
217 | stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) |
218 | + stubs.Set(db, 'network_get_all_by_instance', |
219 | + fake_network_get_all_by_instance) |
220 | stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) |
221 | stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) |
222 | stubs.Set(db, 'instance_get_fixed_address', |
223 | |
224 | === modified file 'nova/tests/fake_utils.py' |
225 | --- nova/tests/fake_utils.py 2011-03-24 02:01:46 +0000 |
226 | +++ nova/tests/fake_utils.py 2011-03-29 10:28:19 +0000 |
227 | @@ -14,8 +14,7 @@ |
228 | # License for the specific language governing permissions and limitations |
229 | # under the License. |
230 | |
231 | -"""This modules stubs out functions in nova.utils |
232 | -""" |
233 | +"""This modules stubs out functions in nova.utils.""" |
234 | |
235 | import re |
236 | import types |
237 | @@ -42,21 +41,25 @@ |
238 | |
239 | |
240 | def fake_execute_set_repliers(repliers): |
241 | - """Allows the client to configure replies to commands""" |
242 | + """Allows the client to configure replies to commands.""" |
243 | global _fake_execute_repliers |
244 | _fake_execute_repliers = repliers |
245 | |
246 | |
247 | def fake_execute_default_reply_handler(*ignore_args, **ignore_kwargs): |
248 | - """A reply handler for commands that haven't been added to the reply |
249 | - list. Returns empty strings for stdout and stderr |
250 | + """A reply handler for commands that haven't been added to the reply list. |
251 | + |
252 | + Returns empty strings for stdout and stderr. |
253 | + |
254 | """ |
255 | return '', '' |
256 | |
257 | |
258 | def fake_execute(*cmd_parts, **kwargs): |
259 | - """This function stubs out execute, optionally executing |
260 | - a preconfigued function to return expected data |
261 | + """This function stubs out execute. |
262 | + |
263 | + It optionally executes a preconfigued function to return expected data. |
264 | + |
265 | """ |
266 | global _fake_execute_repliers |
267 | |
268 | |
269 | === modified file 'nova/tests/test_xenapi.py' |
270 | --- nova/tests/test_xenapi.py 2011-03-25 00:16:53 +0000 |
271 | +++ nova/tests/test_xenapi.py 2011-03-29 10:28:19 +0000 |
272 | @@ -14,9 +14,7 @@ |
273 | # License for the specific language governing permissions and limitations |
274 | # under the License. |
275 | |
276 | -""" |
277 | -Test suite for XenAPI |
278 | -""" |
279 | +"""Test suite for XenAPI.""" |
280 | |
281 | import functools |
282 | import os |
283 | @@ -65,9 +63,7 @@ |
284 | |
285 | |
286 | class XenAPIVolumeTestCase(test.TestCase): |
287 | - """ |
288 | - Unit tests for Volume operations |
289 | - """ |
290 | + """Unit tests for Volume operations.""" |
291 | def setUp(self): |
292 | super(XenAPIVolumeTestCase, self).setUp() |
293 | self.stubs = stubout.StubOutForTesting() |
294 | @@ -101,7 +97,7 @@ |
295 | return db.volume_create(self.context, vol) |
296 | |
297 | def test_create_iscsi_storage(self): |
298 | - """ This shows how to test helper classes' methods """ |
299 | + """This shows how to test helper classes' methods.""" |
300 | stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests) |
301 | session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') |
302 | helper = volume_utils.VolumeHelper |
303 | @@ -116,7 +112,7 @@ |
304 | db.volume_destroy(context.get_admin_context(), vol['id']) |
305 | |
306 | def test_parse_volume_info_raise_exception(self): |
307 | - """ This shows how to test helper classes' methods """ |
308 | + """This shows how to test helper classes' methods.""" |
309 | stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests) |
310 | session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') |
311 | helper = volume_utils.VolumeHelper |
312 | @@ -130,7 +126,7 @@ |
313 | db.volume_destroy(context.get_admin_context(), vol['id']) |
314 | |
315 | def test_attach_volume(self): |
316 | - """ This shows how to test Ops classes' methods """ |
317 | + """This shows how to test Ops classes' methods.""" |
318 | stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests) |
319 | conn = xenapi_conn.get_connection(False) |
320 | volume = self._create_volume() |
321 | @@ -149,7 +145,7 @@ |
322 | check() |
323 | |
324 | def test_attach_volume_raise_exception(self): |
325 | - """ This shows how to test when exceptions are raised """ |
326 | + """This shows how to test when exceptions are raised.""" |
327 | stubs.stubout_session(self.stubs, |
328 | stubs.FakeSessionForVolumeFailedTests) |
329 | conn = xenapi_conn.get_connection(False) |
330 | @@ -172,9 +168,7 @@ |
331 | |
332 | |
333 | class XenAPIVMTestCase(test.TestCase): |
334 | - """ |
335 | - Unit tests for VM operations |
336 | - """ |
337 | + """Unit tests for VM operations.""" |
338 | def setUp(self): |
339 | super(XenAPIVMTestCase, self).setUp() |
340 | self.manager = manager.AuthManager() |
341 | @@ -188,6 +182,7 @@ |
342 | instance_name_template='%d') |
343 | xenapi_fake.reset() |
344 | xenapi_fake.create_local_srs() |
345 | + xenapi_fake.create_local_pifs() |
346 | db_fakes.stub_out_db_instance_api(self.stubs) |
347 | xenapi_fake.create_network('fake', FLAGS.flat_network_bridge) |
348 | stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) |
349 | @@ -247,12 +242,12 @@ |
350 | |
351 | check() |
352 | |
353 | - def create_vm_record(self, conn, os_type): |
354 | + def create_vm_record(self, conn, os_type, instance_id=1): |
355 | instances = conn.list_instances() |
356 | - self.assertEquals(instances, ['1']) |
357 | + self.assertEquals(instances, [str(instance_id)]) |
358 | |
359 | # Get Nova record for VM |
360 | - vm_info = conn.get_info(1) |
361 | + vm_info = conn.get_info(instance_id) |
362 | # Get XenAPI record for VM |
363 | vms = [rec for ref, rec |
364 | in xenapi_fake.get_all_records('VM').iteritems() |
365 | @@ -286,19 +281,19 @@ |
366 | key = 'vm-data/networking/aabbccddeeff' |
367 | xenstore_value = xenstore_data[key] |
368 | tcpip_data = ast.literal_eval(xenstore_value) |
369 | - self.assertEquals(tcpip_data, { |
370 | - 'label': 'test_network', |
371 | - 'broadcast': '10.0.0.255', |
372 | - 'ips': [{'ip': '10.0.0.3', |
373 | - 'netmask':'255.255.255.0', |
374 | - 'enabled':'1'}], |
375 | - 'ip6s': [{'ip': 'fe80::a8bb:ccff:fedd:eeff', |
376 | - 'netmask': '120', |
377 | - 'enabled': '1', |
378 | - 'gateway': 'fe80::a00:1'}], |
379 | - 'mac': 'aa:bb:cc:dd:ee:ff', |
380 | - 'dns': ['10.0.0.2'], |
381 | - 'gateway': '10.0.0.1'}) |
382 | + self.assertEquals(tcpip_data, |
383 | + {'label': 'fake_flat_network', |
384 | + 'broadcast': '10.0.0.255', |
385 | + 'ips': [{'ip': '10.0.0.3', |
386 | + 'netmask':'255.255.255.0', |
387 | + 'enabled':'1'}], |
388 | + 'ip6s': [{'ip': 'fe80::a8bb:ccff:fedd:eeff', |
389 | + 'netmask': '120', |
390 | + 'enabled': '1', |
391 | + 'gateway': 'fe80::a00:1'}], |
392 | + 'mac': 'aa:bb:cc:dd:ee:ff', |
393 | + 'dns': ['10.0.0.2'], |
394 | + 'gateway': '10.0.0.1'}) |
395 | |
396 | def check_vm_params_for_windows(self): |
397 | self.assertEquals(self.vm['platform']['nx'], 'true') |
398 | @@ -334,9 +329,9 @@ |
399 | |
400 | def _test_spawn(self, image_id, kernel_id, ramdisk_id, |
401 | instance_type="m1.large", os_type="linux", |
402 | - check_injection=False): |
403 | + instance_id=1, check_injection=False): |
404 | stubs.stubout_loopingcall_start(self.stubs) |
405 | - values = {'id': 1, |
406 | + values = {'id': instance_id, |
407 | 'project_id': self.project.id, |
408 | 'user_id': self.user.id, |
409 | 'image_id': image_id, |
410 | @@ -347,7 +342,7 @@ |
411 | 'os_type': os_type} |
412 | instance = db.instance_create(self.context, values) |
413 | self.conn.spawn(instance) |
414 | - self.create_vm_record(self.conn, os_type) |
415 | + self.create_vm_record(self.conn, os_type, instance_id) |
416 | self.check_vm_record(self.conn, check_injection) |
417 | |
418 | def test_spawn_not_enough_memory(self): |
419 | @@ -468,6 +463,28 @@ |
420 | # guest agent is detected |
421 | self.assertFalse(self._tee_executed) |
422 | |
423 | + def test_spawn_vlanmanager(self): |
424 | + self.flags(xenapi_image_service='glance', |
425 | + network_manager='nova.network.manager.VlanManager', |
426 | + network_driver='nova.network.xenapi_net', |
427 | + vlan_interface='fake0') |
428 | + # Reset network table |
429 | + xenapi_fake.reset_table('network') |
430 | + # Instance id = 2 will use vlan network (see db/fakes.py) |
431 | + fake_instance_id = 2 |
432 | + network_bk = self.network |
433 | + # Ensure we use xenapi_net driver |
434 | + self.network = utils.import_object(FLAGS.network_manager) |
435 | + self.network.setup_compute_network(None, fake_instance_id) |
436 | + self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE, |
437 | + glance_stubs.FakeGlance.IMAGE_KERNEL, |
438 | + glance_stubs.FakeGlance.IMAGE_RAMDISK, |
439 | + instance_id=fake_instance_id) |
440 | + # TODO(salvatore-orlando): a complete test here would require |
441 | + # a check for making sure the bridge for the VM's VIF is |
442 | + # consistent with bridge specified in nova db |
443 | + self.network = network_bk |
444 | + |
445 | def test_spawn_with_network_qos(self): |
446 | self._create_instance() |
447 | for vif_ref in xenapi_fake.get_all('VIF'): |
448 | @@ -497,7 +514,7 @@ |
449 | self.stubs.UnsetAll() |
450 | |
451 | def _create_instance(self): |
452 | - """Creates and spawns a test instance""" |
453 | + """Creates and spawns a test instance.""" |
454 | stubs.stubout_loopingcall_start(self.stubs) |
455 | values = { |
456 | 'id': 1, |
457 | @@ -515,9 +532,7 @@ |
458 | |
459 | |
460 | class XenAPIDiffieHellmanTestCase(test.TestCase): |
461 | - """ |
462 | - Unit tests for Diffie-Hellman code |
463 | - """ |
464 | + """Unit tests for Diffie-Hellman code.""" |
465 | def setUp(self): |
466 | super(XenAPIDiffieHellmanTestCase, self).setUp() |
467 | self.alice = SimpleDH() |
468 | @@ -541,9 +556,7 @@ |
469 | |
470 | |
471 | class XenAPIMigrateInstance(test.TestCase): |
472 | - """ |
473 | - Unit test for verifying migration-related actions |
474 | - """ |
475 | + """Unit test for verifying migration-related actions.""" |
476 | |
477 | def setUp(self): |
478 | super(XenAPIMigrateInstance, self).setUp() |
479 | @@ -598,9 +611,7 @@ |
480 | |
481 | |
482 | class XenAPIDetermineDiskImageTestCase(test.TestCase): |
483 | - """ |
484 | - Unit tests for code that detects the ImageType |
485 | - """ |
486 | + """Unit tests for code that detects the ImageType.""" |
487 | def setUp(self): |
488 | super(XenAPIDetermineDiskImageTestCase, self).setUp() |
489 | glance_stubs.stubout_glance_client(self.stubs, |
490 | @@ -619,9 +630,7 @@ |
491 | self.assertEqual(disk_type, dt) |
492 | |
493 | def test_instance_disk(self): |
494 | - """ |
495 | - If a kernel is specified then the image type is DISK (aka machine) |
496 | - """ |
497 | + """If a kernel is specified, the image type is DISK (aka machine).""" |
498 | FLAGS.xenapi_image_service = 'objectstore' |
499 | self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_MACHINE |
500 | self.fake_instance.kernel_id = glance_stubs.FakeGlance.IMAGE_KERNEL |
501 | |
502 | === modified file 'nova/virt/xenapi/fake.py' |
503 | --- nova/virt/xenapi/fake.py 2011-03-24 02:01:46 +0000 |
504 | +++ nova/virt/xenapi/fake.py 2011-03-29 10:28:19 +0000 |
505 | @@ -60,8 +60,8 @@ |
506 | from nova import log as logging |
507 | |
508 | |
509 | -_CLASSES = ['host', 'network', 'session', 'SR', 'VBD',\ |
510 | - 'PBD', 'VDI', 'VIF', 'VM', 'task'] |
511 | +_CLASSES = ['host', 'network', 'session', 'SR', 'VBD', |
512 | + 'PBD', 'VDI', 'VIF', 'PIF', 'VM', 'VLAN', 'task'] |
513 | |
514 | _db_content = {} |
515 | |
516 | @@ -78,30 +78,36 @@ |
517 | for c in _CLASSES: |
518 | _db_content[c] = {} |
519 | create_host('fake') |
520 | - create_vm('fake', 'Running', is_a_template=False, is_control_domain=True) |
521 | + create_vm('fake', |
522 | + 'Running', |
523 | + is_a_template=False, |
524 | + is_control_domain=True) |
525 | + |
526 | + |
527 | +def reset_table(table): |
528 | + if not table in _CLASSES: |
529 | + return |
530 | + _db_content[table] = {} |
531 | |
532 | |
533 | def create_host(name_label): |
534 | - return _create_object('host', { |
535 | - 'name_label': name_label, |
536 | - }) |
537 | + return _create_object('host', |
538 | + {'name_label': name_label}) |
539 | |
540 | |
541 | def create_network(name_label, bridge): |
542 | - return _create_object('network', { |
543 | - 'name_label': name_label, |
544 | - 'bridge': bridge, |
545 | - }) |
546 | + return _create_object('network', |
547 | + {'name_label': name_label, |
548 | + 'bridge': bridge}) |
549 | |
550 | |
551 | def create_vm(name_label, status, |
552 | is_a_template=False, is_control_domain=False): |
553 | - return _create_object('VM', { |
554 | - 'name_label': name_label, |
555 | - 'power-state': status, |
556 | - 'is_a_template': is_a_template, |
557 | - 'is_control_domain': is_control_domain, |
558 | - }) |
559 | + return _create_object('VM', |
560 | + {'name_label': name_label, |
561 | + 'power-state': status, |
562 | + 'is_a_template': is_a_template, |
563 | + 'is_control_domain': is_control_domain}) |
564 | |
565 | |
566 | def destroy_vm(vm_ref): |
567 | @@ -123,27 +129,24 @@ |
568 | |
569 | |
570 | def create_vdi(name_label, read_only, sr_ref, sharable): |
571 | - return _create_object('VDI', { |
572 | - 'name_label': name_label, |
573 | - 'read_only': read_only, |
574 | - 'SR': sr_ref, |
575 | - 'type': '', |
576 | - 'name_description': '', |
577 | - 'sharable': sharable, |
578 | - 'other_config': {}, |
579 | - 'location': '', |
580 | - 'xenstore_data': '', |
581 | - 'sm_config': {}, |
582 | - 'VBDs': {}, |
583 | - }) |
584 | + return _create_object('VDI', |
585 | + {'name_label': name_label, |
586 | + 'read_only': read_only, |
587 | + 'SR': sr_ref, |
588 | + 'type': '', |
589 | + 'name_description': '', |
590 | + 'sharable': sharable, |
591 | + 'other_config': {}, |
592 | + 'location': '', |
593 | + 'xenstore_data': '', |
594 | + 'sm_config': {}, |
595 | + 'VBDs': {}}) |
596 | |
597 | |
598 | def create_vbd(vm_ref, vdi_ref): |
599 | - vbd_rec = { |
600 | - 'VM': vm_ref, |
601 | - 'VDI': vdi_ref, |
602 | - 'currently_attached': False, |
603 | - } |
604 | + vbd_rec = {'VM': vm_ref, |
605 | + 'VDI': vdi_ref, |
606 | + 'currently_attached': False} |
607 | vbd_ref = _create_object('VBD', vbd_rec) |
608 | after_VBD_create(vbd_ref, vbd_rec) |
609 | return vbd_ref |
610 | @@ -169,19 +172,24 @@ |
611 | |
612 | |
613 | def create_pbd(config, host_ref, sr_ref, attached): |
614 | - return _create_object('PBD', { |
615 | - 'device-config': config, |
616 | - 'host': host_ref, |
617 | - 'SR': sr_ref, |
618 | - 'currently-attached': attached, |
619 | - }) |
620 | + return _create_object('PBD', |
621 | + {'device-config': config, |
622 | + 'host': host_ref, |
623 | + 'SR': sr_ref, |
624 | + 'currently-attached': attached}) |
625 | |
626 | |
627 | def create_task(name_label): |
628 | - return _create_object('task', { |
629 | - 'name_label': name_label, |
630 | - 'status': 'pending', |
631 | - }) |
632 | + return _create_object('task', |
633 | + {'name_label': name_label, |
634 | + 'status': 'pending'}) |
635 | + |
636 | + |
637 | +def create_local_pifs(): |
638 | + """Adds a PIF for each to the local database with VLAN=-1. |
639 | + Do this one per host.""" |
640 | + for host_ref in _db_content['host'].keys(): |
641 | + _create_local_pif(host_ref) |
642 | |
643 | |
644 | def create_local_srs(): |
645 | @@ -192,25 +200,34 @@ |
646 | |
647 | |
648 | def _create_local_sr(host_ref): |
649 | - sr_ref = _create_object('SR', { |
650 | - 'name_label': 'Local storage', |
651 | - 'type': 'lvm', |
652 | - 'content_type': 'user', |
653 | - 'shared': False, |
654 | - 'physical_size': str(1 << 30), |
655 | - 'physical_utilisation': str(0), |
656 | - 'virtual_allocation': str(0), |
657 | - 'other_config': { |
658 | - 'i18n-original-value-name_label': 'Local storage', |
659 | - 'i18n-key': 'local-storage', |
660 | - }, |
661 | - 'VDIs': [] |
662 | - }) |
663 | + sr_ref = _create_object( |
664 | + 'SR', |
665 | + {'name_label': 'Local storage', |
666 | + 'type': 'lvm', |
667 | + 'content_type': 'user', |
668 | + 'shared': False, |
669 | + 'physical_size': str(1 << 30), |
670 | + 'physical_utilisation': str(0), |
671 | + 'virtual_allocation': str(0), |
672 | + 'other_config': { |
673 | + 'i18n-original-value-name_label': 'Local storage', |
674 | + 'i18n-key': 'local-storage'}, |
675 | + 'VDIs': []}) |
676 | pbd_ref = create_pbd('', host_ref, sr_ref, True) |
677 | _db_content['SR'][sr_ref]['PBDs'] = [pbd_ref] |
678 | return sr_ref |
679 | |
680 | |
681 | +def _create_local_pif(host_ref): |
682 | + pif_ref = _create_object('PIF', |
683 | + {'name-label': 'Fake PIF', |
684 | + 'MAC': '00:11:22:33:44:55', |
685 | + 'physical': True, |
686 | + 'VLAN': -1, |
687 | + 'device': 'fake0', |
688 | + 'host_uuid': host_ref}) |
689 | + |
690 | + |
691 | def _create_object(table, obj): |
692 | ref = str(uuid.uuid4()) |
693 | obj['uuid'] = str(uuid.uuid4()) |
694 | @@ -234,6 +251,21 @@ |
695 | return sr_ref |
696 | |
697 | |
698 | +def _create_vlan(pif_ref, vlan_num, network_ref): |
699 | + pif_rec = get_record('PIF', pif_ref) |
700 | + vlan_pif_ref = _create_object('PIF', |
701 | + {'name-label': 'Fake VLAN PIF', |
702 | + 'MAC': '00:11:22:33:44:55', |
703 | + 'physical': True, |
704 | + 'VLAN': vlan_num, |
705 | + 'device': pif_rec['device'], |
706 | + 'host_uuid': pif_rec['host_uuid']}) |
707 | + return _create_object('VLAN', |
708 | + {'tagged-pif': pif_ref, |
709 | + 'untagged-pif': vlan_pif_ref, |
710 | + 'tag': vlan_num}) |
711 | + |
712 | + |
713 | def get_all(table): |
714 | return _db_content[table].keys() |
715 | |
716 | @@ -292,6 +324,10 @@ |
717 | rec['currently_attached'] = False |
718 | rec['device'] = '' |
719 | |
720 | + def PIF_get_all_records_where(self, _1, _2): |
721 | + # TODO (salvatore-orlando): filter table on _2 |
722 | + return _db_content['PIF'] |
723 | + |
724 | def VM_get_xenstore_data(self, _1, vm_ref): |
725 | return _db_content['VM'][vm_ref].get('xenstore_data', '') |
726 | |
727 | @@ -302,7 +338,7 @@ |
728 | db_ref['xenstore_data'][key] = None |
729 | |
730 | def network_get_all_records_where(self, _1, _2): |
731 | - # TODO (salvatore-orlando):filter table on _2 |
732 | + # TODO (salvatore-orlando): filter table on _2 |
733 | return _db_content['network'] |
734 | |
735 | def VM_add_to_xenstore_data(self, _1, vm_ref, key, value): |
736 | @@ -318,6 +354,9 @@ |
737 | def host_call_plugin(*args): |
738 | return 'herp' |
739 | |
740 | + def network_get_all_records_where(self, _1, filter): |
741 | + return self.xenapi.network.get_all_records() |
742 | + |
743 | def xenapi_request(self, methodname, params): |
744 | if methodname.startswith('login'): |
745 | self._login(methodname, params) |
746 | @@ -337,10 +376,9 @@ |
747 | |
748 | def _login(self, method, params): |
749 | self._session = str(uuid.uuid4()) |
750 | - _db_content['session'][self._session] = { |
751 | - 'uuid': str(uuid.uuid4()), |
752 | - 'this_host': _db_content['host'].keys()[0], |
753 | - } |
754 | + _db_content['session'][self._session] = \ |
755 | + {'uuid': str(uuid.uuid4()), |
756 | + 'this_host': _db_content['host'].keys()[0]} |
757 | |
758 | def _logout(self): |
759 | s = self._session |
760 | @@ -456,12 +494,16 @@ |
761 | def _create(self, name, params): |
762 | self._check_session(params) |
763 | is_sr_create = name == 'SR.create' |
764 | + is_vlan_create = name == 'VLAN.create' |
765 | # Storage Repositories have a different API |
766 | - expected = is_sr_create and 10 or 2 |
767 | + expected = is_sr_create and 10 or is_vlan_create and 4 or 2 |
768 | self._check_arg_count(params, expected) |
769 | (cls, _) = name.split('.') |
770 | ref = is_sr_create and \ |
771 | - _create_sr(cls, params) or _create_object(cls, params[1]) |
772 | + _create_sr(cls, params) or \ |
773 | + is_vlan_create and \ |
774 | + _create_vlan(params[1], params[2], params[3]) or \ |
775 | + _create_object(cls, params[1]) |
776 | |
777 | # Call hook to provide any fixups needed (ex. creating backrefs) |
778 | after_hook = 'after_%s_create' % cls |
779 | |
780 | === modified file 'nova/virt/xenapi/network_utils.py' |
781 | --- nova/virt/xenapi/network_utils.py 2010-12-23 03:35:41 +0000 |
782 | +++ nova/virt/xenapi/network_utils.py 2011-03-29 10:28:19 +0000 |
783 | @@ -28,11 +28,26 @@ |
784 | """ |
785 | The class that wraps the helper methods together. |
786 | """ |
787 | + @classmethod |
788 | + def find_network_with_name_label(cls, session, name_label): |
789 | + networks = session.call_xenapi('network.get_by_name_label', name_label) |
790 | + if len(networks) == 1: |
791 | + return networks[0] |
792 | + elif len(networks) > 1: |
793 | + raise Exception(_('Found non-unique network' |
794 | + ' for name_label %s') % name_label) |
795 | + else: |
796 | + return None |
797 | |
798 | @classmethod |
799 | def find_network_with_bridge(cls, session, bridge): |
800 | - """Return the network on which the bridge is attached, if found.""" |
801 | - expr = 'field "bridge" = "%s"' % bridge |
802 | + """ |
803 | + Return the network on which the bridge is attached, if found. |
804 | + The bridge is defined in the nova db and can be found either in the |
805 | + 'bridge' or 'name_label' fields of the XenAPI network record. |
806 | + """ |
807 | + expr = 'field "name__label" = "%s" or ' \ |
808 | + 'field "bridge" = "%s"' % (bridge, bridge) |
809 | networks = session.call_xenapi('network.get_all_records_where', expr) |
810 | if len(networks) == 1: |
811 | return networks.keys()[0] |
812 | |
813 | === modified file 'nova/virt/xenapi/vmops.py' |
814 | --- nova/virt/xenapi/vmops.py 2011-03-25 00:16:53 +0000 |
815 | +++ nova/virt/xenapi/vmops.py 2011-03-29 10:28:19 +0000 |
816 | @@ -58,7 +58,7 @@ |
817 | VMHelper.XenAPI = self.XenAPI |
818 | |
819 | def list_instances(self): |
820 | - """List VM instances""" |
821 | + """List VM instances.""" |
822 | # TODO(justinsb): Should we just always use the details method? |
823 | # Seems to be the same number of API calls.. |
824 | vm_refs = [] |
825 | @@ -69,7 +69,7 @@ |
826 | return vm_refs |
827 | |
828 | def list_instances_detail(self): |
829 | - """List VM instances, returning InstanceInfo objects""" |
830 | + """List VM instances, returning InstanceInfo objects.""" |
831 | instance_infos = [] |
832 | for vm_ref in self._session.get_xenapi().VM.get_all(): |
833 | vm_rec = self._session.get_xenapi().VM.get_record(vm_ref) |
834 | @@ -119,11 +119,11 @@ |
835 | self._spawn(instance, vm_ref) |
836 | |
837 | def spawn_rescue(self, instance): |
838 | - """Spawn a rescue instance""" |
839 | + """Spawn a rescue instance.""" |
840 | self.spawn(instance) |
841 | |
842 | def _create_vm(self, instance, vdi_uuid, network_info=None): |
843 | - """Create VM instance""" |
844 | + """Create VM instance.""" |
845 | instance_name = instance.name |
846 | vm_ref = VMHelper.lookup(self._session, instance_name) |
847 | if vm_ref is not None: |
848 | @@ -180,7 +180,7 @@ |
849 | return vm_ref |
850 | |
851 | def _spawn(self, instance, vm_ref): |
852 | - """Spawn a new instance""" |
853 | + """Spawn a new instance.""" |
854 | LOG.debug(_('Starting VM %s...'), vm_ref) |
855 | self._start(instance, vm_ref) |
856 | instance_name = instance.name |
857 | @@ -236,7 +236,8 @@ |
858 | return timer.start(interval=0.5, now=True) |
859 | |
860 | def _get_vm_opaque_ref(self, instance_or_vm): |
861 | - """Refactored out the common code of many methods that receive either |
862 | + """ |
863 | + Refactored out the common code of many methods that receive either |
864 | a vm name or a vm instance, and want a vm instance in return. |
865 | """ |
866 | # if instance_or_vm is a string it must be opaque ref or instance name |
867 | @@ -264,21 +265,21 @@ |
868 | return vm_ref |
869 | |
870 | def _acquire_bootlock(self, vm): |
871 | - """Prevent an instance from booting""" |
872 | + """Prevent an instance from booting.""" |
873 | self._session.call_xenapi( |
874 | "VM.set_blocked_operations", |
875 | vm, |
876 | {"start": ""}) |
877 | |
878 | def _release_bootlock(self, vm): |
879 | - """Allow an instance to boot""" |
880 | + """Allow an instance to boot.""" |
881 | self._session.call_xenapi( |
882 | "VM.remove_from_blocked_operations", |
883 | vm, |
884 | "start") |
885 | |
886 | def snapshot(self, instance, image_id): |
887 | - """Create snapshot from a running VM instance |
888 | + """Create snapshot from a running VM instance. |
889 | |
890 | :param instance: instance to be snapshotted |
891 | :param image_id: id of image to upload to |
892 | @@ -298,6 +299,7 @@ |
893 | 3. Push-to-glance: Once coalesced, we call a plugin on the XenServer |
894 | that will bundle the VHDs together and then push the bundle into |
895 | Glance. |
896 | + |
897 | """ |
898 | template_vm_ref = None |
899 | try: |
900 | @@ -330,11 +332,12 @@ |
901 | return |
902 | |
903 | def migrate_disk_and_power_off(self, instance, dest): |
904 | - """Copies a VHD from one host machine to another |
905 | - |
906 | - :param instance: the instance that owns the VHD in question |
907 | - :param dest: the destination host machine |
908 | - :param disk_type: values are 'primary' or 'cow' |
909 | + """Copies a VHD from one host machine to another. |
910 | + |
911 | + :param instance: the instance that owns the VHD in question. |
912 | + :param dest: the destination host machine. |
913 | + :param disk_type: values are 'primary' or 'cow'. |
914 | + |
915 | """ |
916 | vm_ref = VMHelper.lookup(self._session, instance.name) |
917 | |
918 | @@ -383,7 +386,7 @@ |
919 | return {'base_copy': base_copy_uuid, 'cow': cow_uuid} |
920 | |
921 | def link_disks(self, instance, base_copy_uuid, cow_uuid): |
922 | - """Links the base copy VHD to the COW via the XAPI plugin""" |
923 | + """Links the base copy VHD to the COW via the XAPI plugin.""" |
924 | vm_ref = VMHelper.lookup(self._session, instance.name) |
925 | new_base_copy_uuid = str(uuid.uuid4()) |
926 | new_cow_uuid = str(uuid.uuid4()) |
927 | @@ -404,7 +407,7 @@ |
928 | return new_cow_uuid |
929 | |
930 | def resize_instance(self, instance, vdi_uuid): |
931 | - """Resize a running instance by changing it's RAM and disk size """ |
932 | + """Resize a running instance by changing it's RAM and disk size.""" |
933 | #TODO(mdietz): this will need to be adjusted for swap later |
934 | #The new disk size must be in bytes |
935 | |
936 | @@ -418,18 +421,20 @@ |
937 | LOG.debug(_("Resize instance %s complete") % (instance.name)) |
938 | |
939 | def reboot(self, instance): |
940 | - """Reboot VM instance""" |
941 | + """Reboot VM instance.""" |
942 | vm_ref = self._get_vm_opaque_ref(instance) |
943 | task = self._session.call_xenapi('Async.VM.clean_reboot', vm_ref) |
944 | self._session.wait_for_task(task, instance.id) |
945 | |
946 | def set_admin_password(self, instance, new_pass): |
947 | - """Set the root/admin password on the VM instance. This is done via |
948 | - an agent running on the VM. Communication between nova and the agent |
949 | - is done via writing xenstore records. Since communication is done over |
950 | - the XenAPI RPC calls, we need to encrypt the password. We're using a |
951 | - simple Diffie-Hellman class instead of the more advanced one in |
952 | - M2Crypto for compatibility with the agent code. |
953 | + """Set the root/admin password on the VM instance. |
954 | + |
955 | + This is done via an agent running on the VM. Communication between nova |
956 | + and the agent is done via writing xenstore records. Since communication |
957 | + is done over the XenAPI RPC calls, we need to encrypt the password. |
958 | + We're using a simple Diffie-Hellman class instead of the more advanced |
959 | + one in M2Crypto for compatibility with the agent code. |
960 | + |
961 | """ |
962 | # Need to uniquely identify this request. |
963 | transaction_id = str(uuid.uuid4()) |
964 | @@ -462,11 +467,14 @@ |
965 | return resp_dict['message'] |
966 | |
967 | def inject_file(self, instance, path, contents): |
968 | - """Write a file to the VM instance. The path to which it is to be |
969 | - written and the contents of the file need to be supplied; both will |
970 | - be base64-encoded to prevent errors with non-ASCII characters being |
971 | - transmitted. If the agent does not support file injection, or the user |
972 | - has disabled it, a NotImplementedError will be raised. |
973 | + """Write a file to the VM instance. |
974 | + |
975 | + The path to which it is to be written and the contents of the file |
976 | + need to be supplied; both will be base64-encoded to prevent errors |
977 | + with non-ASCII characters being transmitted. If the agent does not |
978 | + support file injection, or the user has disabled it, a |
979 | + NotImplementedError will be raised. |
980 | + |
981 | """ |
982 | # Files/paths must be base64-encoded for transmission to agent |
983 | b64_path = base64.b64encode(path) |
984 | @@ -487,7 +495,7 @@ |
985 | return resp_dict['message'] |
986 | |
987 | def _shutdown(self, instance, vm_ref, hard=True): |
988 | - """Shutdown an instance""" |
989 | + """Shutdown an instance.""" |
990 | state = self.get_info(instance['name'])['state'] |
991 | if state == power_state.SHUTDOWN: |
992 | instance_name = instance.name |
993 | @@ -511,11 +519,11 @@ |
994 | LOG.exception(exc) |
995 | |
996 | def _shutdown_rescue(self, rescue_vm_ref): |
997 | - """Shutdown a rescue instance""" |
998 | + """Shutdown a rescue instance.""" |
999 | self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm_ref) |
1000 | |
1001 | def _destroy_vdis(self, instance, vm_ref): |
1002 | - """Destroys all VDIs associated with a VM""" |
1003 | + """Destroys all VDIs associated with a VM.""" |
1004 | instance_id = instance.id |
1005 | LOG.debug(_("Destroying VDIs for Instance %(instance_id)s") |
1006 | % locals()) |
1007 | @@ -532,7 +540,7 @@ |
1008 | LOG.exception(exc) |
1009 | |
1010 | def _destroy_rescue_vdis(self, rescue_vm_ref): |
1011 | - """Destroys all VDIs associated with a rescued VM""" |
1012 | + """Destroys all VDIs associated with a rescued VM.""" |
1013 | vdi_refs = VMHelper.lookup_vm_vdis(self._session, rescue_vm_ref) |
1014 | for vdi_ref in vdi_refs: |
1015 | try: |
1016 | @@ -541,7 +549,7 @@ |
1017 | continue |
1018 | |
1019 | def _destroy_rescue_vbds(self, rescue_vm_ref): |
1020 | - """Destroys all VBDs tied to a rescue VM""" |
1021 | + """Destroys all VBDs tied to a rescue VM.""" |
1022 | vbd_refs = self._session.get_xenapi().VM.get_VBDs(rescue_vm_ref) |
1023 | for vbd_ref in vbd_refs: |
1024 | vbd_rec = self._session.get_xenapi().VBD.get_record(vbd_ref) |
1025 | @@ -550,8 +558,7 @@ |
1026 | VMHelper.destroy_vbd(self._session, vbd_ref) |
1027 | |
1028 | def _destroy_kernel_ramdisk(self, instance, vm_ref): |
1029 | - """ |
1030 | - Three situations can occur: |
1031 | + """Three situations can occur: |
1032 | |
1033 | 1. We have neither a ramdisk nor a kernel, in which case we are a |
1034 | RAW image and can omit this step |
1035 | @@ -561,6 +568,7 @@ |
1036 | |
1037 | 3. We have both, in which case we safely remove both the kernel |
1038 | and the ramdisk. |
1039 | + |
1040 | """ |
1041 | instance_id = instance.id |
1042 | if not instance.kernel_id and not instance.ramdisk_id: |
1043 | @@ -589,7 +597,7 @@ |
1044 | LOG.debug(_("kernel/ramdisk files removed")) |
1045 | |
1046 | def _destroy_vm(self, instance, vm_ref): |
1047 | - """Destroys a VM record""" |
1048 | + """Destroys a VM record.""" |
1049 | instance_id = instance.id |
1050 | try: |
1051 | task = self._session.call_xenapi('Async.VM.destroy', vm_ref) |
1052 | @@ -600,7 +608,7 @@ |
1053 | LOG.debug(_("Instance %(instance_id)s VM destroyed") % locals()) |
1054 | |
1055 | def _destroy_rescue_instance(self, rescue_vm_ref): |
1056 | - """Destroy a rescue instance""" |
1057 | + """Destroy a rescue instance.""" |
1058 | self._destroy_rescue_vbds(rescue_vm_ref) |
1059 | self._shutdown_rescue(rescue_vm_ref) |
1060 | self._destroy_rescue_vdis(rescue_vm_ref) |
1061 | @@ -608,11 +616,11 @@ |
1062 | self._session.call_xenapi("Async.VM.destroy", rescue_vm_ref) |
1063 | |
1064 | def destroy(self, instance): |
1065 | - """ |
1066 | - Destroy VM instance |
1067 | + """Destroy VM instance. |
1068 | |
1069 | This is the method exposed by xenapi_conn.destroy(). The rest of the |
1070 | destroy_* methods are internal. |
1071 | + |
1072 | """ |
1073 | instance_id = instance.id |
1074 | LOG.info(_("Destroying VM for Instance %(instance_id)s") % locals()) |
1075 | @@ -621,13 +629,13 @@ |
1076 | |
1077 | def _destroy(self, instance, vm_ref, shutdown=True, |
1078 | destroy_kernel_ramdisk=True): |
1079 | - """ |
1080 | - Destroys VM instance by performing: |
1081 | - |
1082 | - 1. A shutdown if requested |
1083 | - 2. Destroying associated VDIs |
1084 | - 3. Destroying kernel and ramdisk files (if necessary) |
1085 | - 4. Destroying that actual VM record |
1086 | + """Destroys VM instance by performing: |
1087 | + |
1088 | + 1. A shutdown if requested. |
1089 | + 2. Destroying associated VDIs. |
1090 | + 3. Destroying kernel and ramdisk files (if necessary). |
1091 | + 4. Destroying that actual VM record. |
1092 | + |
1093 | """ |
1094 | if vm_ref is None: |
1095 | LOG.warning(_("VM is not present, skipping destroy...")) |
1096 | @@ -650,35 +658,36 @@ |
1097 | callback(ret) |
1098 | |
1099 | def pause(self, instance, callback): |
1100 | - """Pause VM instance""" |
1101 | + """Pause VM instance.""" |
1102 | vm_ref = self._get_vm_opaque_ref(instance) |
1103 | task = self._session.call_xenapi('Async.VM.pause', vm_ref) |
1104 | self._wait_with_callback(instance.id, task, callback) |
1105 | |
1106 | def unpause(self, instance, callback): |
1107 | - """Unpause VM instance""" |
1108 | + """Unpause VM instance.""" |
1109 | vm_ref = self._get_vm_opaque_ref(instance) |
1110 | task = self._session.call_xenapi('Async.VM.unpause', vm_ref) |
1111 | self._wait_with_callback(instance.id, task, callback) |
1112 | |
1113 | def suspend(self, instance, callback): |
1114 | - """suspend the specified instance""" |
1115 | + """Suspend the specified instance.""" |
1116 | vm_ref = self._get_vm_opaque_ref(instance) |
1117 | task = self._session.call_xenapi('Async.VM.suspend', vm_ref) |
1118 | self._wait_with_callback(instance.id, task, callback) |
1119 | |
1120 | def resume(self, instance, callback): |
1121 | - """resume the specified instance""" |
1122 | + """Resume the specified instance.""" |
1123 | vm_ref = self._get_vm_opaque_ref(instance) |
1124 | task = self._session.call_xenapi('Async.VM.resume', vm_ref, False, |
1125 | True) |
1126 | self._wait_with_callback(instance.id, task, callback) |
1127 | |
1128 | def rescue(self, instance, callback): |
1129 | - """Rescue the specified instance |
1130 | - - shutdown the instance VM |
1131 | - - set 'bootlock' to prevent the instance from starting in rescue |
1132 | - - spawn a rescue VM (the vm name-label will be instance-N-rescue) |
1133 | + """Rescue the specified instance. |
1134 | + |
1135 | + - shutdown the instance VM. |
1136 | + - set 'bootlock' to prevent the instance from starting in rescue. |
1137 | + - spawn a rescue VM (the vm name-label will be instance-N-rescue). |
1138 | |
1139 | """ |
1140 | rescue_vm_ref = VMHelper.lookup(self._session, |
1141 | @@ -702,10 +711,11 @@ |
1142 | self._session.call_xenapi("Async.VBD.plug", rescue_vbd_ref) |
1143 | |
1144 | def unrescue(self, instance, callback): |
1145 | - """Unrescue the specified instance |
1146 | - - unplug the instance VM's disk from the rescue VM |
1147 | - - teardown the rescue VM |
1148 | - - release the bootlock to allow the instance VM to start |
1149 | + """Unrescue the specified instance. |
1150 | + |
1151 | + - unplug the instance VM's disk from the rescue VM. |
1152 | + - teardown the rescue VM. |
1153 | + - release the bootlock to allow the instance VM to start. |
1154 | |
1155 | """ |
1156 | rescue_vm_ref = VMHelper.lookup(self._session, |
1157 | @@ -723,9 +733,11 @@ |
1158 | self._start(instance, original_vm_ref) |
1159 | |
1160 | def poll_rescued_instances(self, timeout): |
1161 | - """Look for expirable rescued instances |
1162 | + """Look for expirable rescued instances. |
1163 | + |
1164 | - forcibly exit rescue mode for any instances that have been |
1165 | in rescue mode for >= the provided timeout |
1166 | + |
1167 | """ |
1168 | last_ran = self.poll_rescue_last_ran |
1169 | if not last_ran: |
1170 | @@ -761,30 +773,30 @@ |
1171 | False) |
1172 | |
1173 | def get_info(self, instance): |
1174 | - """Return data about VM instance""" |
1175 | + """Return data about VM instance.""" |
1176 | vm_ref = self._get_vm_opaque_ref(instance) |
1177 | vm_rec = self._session.get_xenapi().VM.get_record(vm_ref) |
1178 | return VMHelper.compile_info(vm_rec) |
1179 | |
1180 | def get_diagnostics(self, instance): |
1181 | - """Return data about VM diagnostics""" |
1182 | + """Return data about VM diagnostics.""" |
1183 | vm_ref = self._get_vm_opaque_ref(instance) |
1184 | vm_rec = self._session.get_xenapi().VM.get_record(vm_ref) |
1185 | return VMHelper.compile_diagnostics(self._session, vm_rec) |
1186 | |
1187 | def get_console_output(self, instance): |
1188 | - """Return snapshot of console""" |
1189 | + """Return snapshot of console.""" |
1190 | # TODO: implement this to fix pylint! |
1191 | return 'FAKE CONSOLE OUTPUT of instance' |
1192 | |
1193 | def get_ajax_console(self, instance): |
1194 | - """Return link to instance's ajax console""" |
1195 | + """Return link to instance's ajax console.""" |
1196 | # TODO: implement this! |
1197 | return 'http://fakeajaxconsole/fake_url' |
1198 | |
1199 | # TODO(tr3buchet) - remove this function after nova multi-nic |
1200 | def _get_network_info(self, instance): |
1201 | - """creates network info list for instance""" |
1202 | + """Creates network info list for instance.""" |
1203 | admin_context = context.get_admin_context() |
1204 | IPs = db.fixed_ip_get_all_by_instance(admin_context, |
1205 | instance['id']) |
1206 | @@ -826,7 +838,7 @@ |
1207 | def inject_network_info(self, instance, vm_ref, network_info): |
1208 | """ |
1209 | Generate the network info and make calls to place it into the |
1210 | - xenstore and the xenstore param list |
1211 | + xenstore and the xenstore param list. |
1212 | """ |
1213 | logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref) |
1214 | |
1215 | @@ -847,7 +859,7 @@ |
1216 | pass |
1217 | |
1218 | def create_vifs(self, vm_ref, network_info): |
1219 | - """Creates vifs for an instance""" |
1220 | + """Creates vifs for an instance.""" |
1221 | logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref) |
1222 | |
1223 | # this function raises if vm_ref is not a vm_opaque_ref |
1224 | @@ -858,8 +870,8 @@ |
1225 | bridge = network['bridge'] |
1226 | rxtx_cap = info.pop('rxtx_cap') |
1227 | network_ref = \ |
1228 | - NetworkHelper.find_network_with_bridge(self._session, bridge) |
1229 | - |
1230 | + NetworkHelper.find_network_with_bridge(self._session, |
1231 | + bridge) |
1232 | VMHelper.create_vif(self._session, vm_ref, network_ref, |
1233 | mac_address, device, rxtx_cap) |
1234 | |
1235 | @@ -872,7 +884,8 @@ |
1236 | args, vm_ref) |
1237 | |
1238 | def list_from_xenstore(self, vm, path): |
1239 | - """Runs the xenstore-ls command to get a listing of all records |
1240 | + """ |
1241 | + Runs the xenstore-ls command to get a listing of all records |
1242 | from 'path' downward. Returns a dict with the sub-paths as keys, |
1243 | and the value stored in those paths as values. If nothing is |
1244 | found at that path, returns None. |
1245 | @@ -881,7 +894,8 @@ |
1246 | return json.loads(ret) |
1247 | |
1248 | def read_from_xenstore(self, vm, path): |
1249 | - """Returns the value stored in the xenstore record for the given VM |
1250 | + """ |
1251 | + Returns the value stored in the xenstore record for the given VM |
1252 | at the specified location. A XenAPIPlugin.PluginError will be raised |
1253 | if any error is encountered in the read process. |
1254 | """ |
1255 | @@ -897,7 +911,8 @@ |
1256 | return ret |
1257 | |
1258 | def write_to_xenstore(self, vm, path, value): |
1259 | - """Writes the passed value to the xenstore record for the given VM |
1260 | + """ |
1261 | + Writes the passed value to the xenstore record for the given VM |
1262 | at the specified location. A XenAPIPlugin.PluginError will be raised |
1263 | if any error is encountered in the write process. |
1264 | """ |
1265 | @@ -905,7 +920,8 @@ |
1266 | {'value': json.dumps(value)}) |
1267 | |
1268 | def clear_xenstore(self, vm, path): |
1269 | - """Deletes the VM's xenstore record for the specified path. |
1270 | + """ |
1271 | + Deletes the VM's xenstore record for the specified path. |
1272 | If there is no such record, the request is ignored. |
1273 | """ |
1274 | self._make_xenstore_call('delete_record', vm, path) |
1275 | @@ -922,7 +938,8 @@ |
1276 | |
1277 | def _make_plugin_call(self, plugin, method, vm, path, addl_args=None, |
1278 | vm_ref=None): |
1279 | - """Abstracts out the process of calling a method of a xenapi plugin. |
1280 | + """ |
1281 | + Abstracts out the process of calling a method of a xenapi plugin. |
1282 | Any errors raised by the plugin will in turn raise a RuntimeError here. |
1283 | """ |
1284 | instance_id = vm.id |
1285 | @@ -952,7 +969,8 @@ |
1286 | return ret |
1287 | |
1288 | def add_to_xenstore(self, vm, path, key, value): |
1289 | - """Adds the passed key/value pair to the xenstore record for |
1290 | + """ |
1291 | + Adds the passed key/value pair to the xenstore record for |
1292 | the given VM at the specified location. A XenAPIPlugin.PluginError |
1293 | will be raised if any error is encountered in the write process. |
1294 | """ |
1295 | @@ -965,7 +983,8 @@ |
1296 | self.write_to_xenstore(vm, path, current) |
1297 | |
1298 | def remove_from_xenstore(self, vm, path, key_or_keys): |
1299 | - """Takes either a single key or a list of keys and removes |
1300 | + """ |
1301 | + Takes either a single key or a list of keys and removes |
1302 | them from the xenstoreirecord data for the given VM. |
1303 | If the key doesn't exist, the request is ignored. |
1304 | """ |
1305 | @@ -992,7 +1011,8 @@ |
1306 | ###### names to distinguish them. (dabo) |
1307 | ######################################################################## |
1308 | def read_partial_from_param_xenstore(self, instance_or_vm, key_prefix): |
1309 | - """Returns a dict of all the keys in the xenstore parameter record |
1310 | + """ |
1311 | + Returns a dict of all the keys in the xenstore parameter record |
1312 | for the given instance that begin with the key_prefix. |
1313 | """ |
1314 | data = self.read_from_param_xenstore(instance_or_vm) |
1315 | @@ -1003,7 +1023,8 @@ |
1316 | return data |
1317 | |
1318 | def read_from_param_xenstore(self, instance_or_vm, keys=None): |
1319 | - """Returns the xenstore parameter record data for the specified VM |
1320 | + """ |
1321 | + Returns the xenstore parameter record data for the specified VM |
1322 | instance as a dict. Accepts an optional key or list of keys; if a |
1323 | value for 'keys' is passed, the returned dict is filtered to only |
1324 | return the values for those keys. |
1325 | @@ -1025,9 +1046,11 @@ |
1326 | return ret |
1327 | |
1328 | def add_to_param_xenstore(self, instance_or_vm, key, val): |
1329 | - """Takes a key/value pair and adds it to the xenstore parameter |
1330 | + """ |
1331 | + Takes a key/value pair and adds it to the xenstore parameter |
1332 | record for the given vm instance. If the key exists in xenstore, |
1333 | - it is overwritten""" |
1334 | + it is overwritten |
1335 | + """ |
1336 | vm_ref = self._get_vm_opaque_ref(instance_or_vm) |
1337 | self.remove_from_param_xenstore(instance_or_vm, key) |
1338 | jsonval = json.dumps(val) |
1339 | @@ -1035,7 +1058,8 @@ |
1340 | (vm_ref, key, jsonval)) |
1341 | |
1342 | def write_to_param_xenstore(self, instance_or_vm, mapping): |
1343 | - """Takes a dict and writes each key/value pair to the xenstore |
1344 | + """ |
1345 | + Takes a dict and writes each key/value pair to the xenstore |
1346 | parameter record for the given vm instance. Any existing data for |
1347 | those keys is overwritten. |
1348 | """ |
1349 | @@ -1043,7 +1067,8 @@ |
1350 | self.add_to_param_xenstore(instance_or_vm, k, v) |
1351 | |
1352 | def remove_from_param_xenstore(self, instance_or_vm, key_or_keys): |
1353 | - """Takes either a single key or a list of keys and removes |
1354 | + """ |
1355 | + Takes either a single key or a list of keys and removes |
1356 | them from the xenstore parameter record data for the given VM. |
1357 | If the key doesn't exist, the request is ignored. |
1358 | """ |
1359 | @@ -1069,7 +1094,8 @@ |
1360 | |
1361 | |
1362 | class SimpleDH(object): |
1363 | - """This class wraps all the functionality needed to implement |
1364 | + """ |
1365 | + This class wraps all the functionality needed to implement |
1366 | basic Diffie-Hellman-Merkle key exchange in Python. It features |
1367 | intelligent defaults for the prime and base numbers needed for the |
1368 | calculation, while allowing you to supply your own. It requires that |
1369 | @@ -1078,7 +1104,8 @@ |
1370 | is not available, a RuntimeError will be raised. |
1371 | """ |
1372 | def __init__(self, prime=None, base=None, secret=None): |
1373 | - """You can specify the values for prime and base if you wish; |
1374 | + """ |
1375 | + You can specify the values for prime and base if you wish; |
1376 | otherwise, reasonable default values will be used. |
1377 | """ |
1378 | if prime is None: |
I merged with trunk and got a conflict in network/manager.py
<<<<<<< TREE string( 'vlan_interface ', 'eth0',
' network device for vlans') integer( 'num_networks' , 1000, 'Number of networks to support') integer( 'num_networks' , 1, 'Number of networks to support')
flags.DEFINE_
flags.DEFINE_
=======
flags.DEFINE_
>>>>>>> MERGE-SOURCE
Also had a unit-test error in one of the test files, so the rest of the XenAPI tests aren't currently running. Forget a bzr add?
======= ======= ======= ======= ======= ======= ======= ======= ======= ======= ------- ------- ------- ------- ------- ------- ------- ------- ------- lib/python2. 6/dist- packages/ nose/loader. py", line 390, in loadTestsFromName lib/python2. 6/dist- packages/ nose/importer. py", line 39, in importFromPath Dir(dir_ path, fqname) lib/python2. 6/dist- packages/ nose/importer. py", line 86, in importFromDir part_fqname, fh, filename, desc) code/nova/ xenapi- vlan-network- manager/ nova/tests/ test_xenapi. py", line 41, in <module>
ERROR: Failure: ImportError (cannot import name fake_utils)
-------
Traceback (most recent call last):
File "/usr/local/
addr.filename, addr.module)
File "/usr/local/
return self.importFrom
File "/usr/local/
mod = load_module(
File "/root/
from nova.tests import fake_utils
ImportError: cannot import name fake_utils
------- ------- ------- ------- ------- ------- ------- ------- ------- -------
36) Shouldn't this be the citrix copyright?
623-632) this seems really odd to me. What's an example of how we would actually fail into the exception block?
Tiny nits:
116 + "Expected %(vlan_num)d") % locals())
You might want a space before Expected or it will run up against the period from the previous sentence