Merge lp:~blamar/nova/libvirt-cleanup-branch into lp:~hudson-openstack/nova/trunk
- libvirt-cleanup-branch
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~blamar/nova/libvirt-cleanup-branch |
Merge into: | lp:~hudson-openstack/nova/trunk |
Diff against target: |
433 lines (+123/-139) 6 files modified
nova/tests/test_libvirt.py (+8/-52) nova/utils.py (+15/-0) nova/virt/libvirt/__init__.py (+85/-0) nova/virt/libvirt/connection.py (+13/-82) nova/virt/libvirt/firewall.py (+1/-5) tools/pip-requires (+1/-0) |
To merge this branch: | bzr merge lp:~blamar/nova/libvirt-cleanup-branch |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Monty Taylor (community) | Needs Fixing | ||
Devin Carlen (community) | Approve | ||
Ed Leafe (community) | Approve | ||
William Wolf (community) | Approve | ||
Alex Meade (community) | Approve | ||
Review via email: mp+66064@code.launchpad.net |
Commit message
Moved libvirt flag defines to libvirt/
Description of the change
Two small cleanups in libvirt:
1) Moved flag defines to __init__.py
2) Replaced lazy-loading of imports with utils.optional_
One unrelated change is the addition of unittest2 to pip-requires. I'm
using the unittest.skipIf method in test_libvirt.py and unittest2 has
backported all Python 2.7 unittest improvements.
Brian Waldon (bcwaldon) wrote : | # |
Good refactoring, Brian. one minor thing:
164: Can you rephrase this log line to not repeat the word 'import'? And should it be info or debug?
Ed Leafe (ed-leafe) wrote : | # |
358 + """Retrieve a libvirt connection object.
359 +
360 + :param read_only: If True, the libvirt connection will be read only.
361 + :returns: `nova.virt.
362 +
363 + """
Can this be reduced to a single line docstring? The param name makes its use obvious, and the return value type is what the docstring already stated.
Also, is there a reason not to change all uses of unittest to unittest2? Or is that planned as part of another merge?
- 1219. By Brian Lamar
-
review feedback from bcwaldon and ed leafe
Brian Lamar (blamar) wrote : | # |
Shortened comment to single-line (you're very right, it was unnecessarily redundant!) and re-worded the log line for bcwaldon.
Ed, there is not a blueprint I'm aware of, so I made one. Great suggestion. I'm not going to actively pursue it now, but it will be something to point to when I do get the time or someone else feels like taking it on.
William Wolf (throughnothing) wrote : | # |
This looks good
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~blamar/nova/libvirt-cleanup-branch into lp:nova failed. Below is the output from the failed tests.
FloatingIpTest
test_
test_
test_
test_
test_
test_
test_
FlavorsExtraSpe
test_create OK 0.04
test_
test_delete OK 0.04
test_index OK 0.05
test_
test_show OK 0.05
test_
test_
test_
test_
test_
AccountsTest
test_
test_
test_
test_
AdminAPITest
test_
test_
APITest
test_
test_
test_
Test
test_
test_
test_
test_
test_bad_token OK 0.05
test_
test_
test_no_user OK 0.06
test_
test_
TestFunctional
test_
test_
TestLimiter
test_
LimiterTest
test_
- 1220. By Brian Lamar
-
Merged trunk.
- 1221. By Brian Lamar
-
Merged trunk
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~blamar/nova/libvirt-cleanup-branch into lp:nova failed. Below is the output from the failed tests.
FloatingIpTest
test_
test_
test_
test_
test_
test_
test_
FixedIpTest
test_
test_
test_
test_
FlavorsExtraSpe
test_create OK 0.04
test_
test_delete OK 0.05
test_index OK 0.05
test_
test_show OK 0.05
test_
test_
test_
test_
test_
AccountsTest
test_
test_
test_
test_
AdminAPITest
test_
test_
APITest
test_
test_
test_
Test
test_
test_
test_
test_
test_bad_token OK 0.06
test_
test_
test_no_user OK 0.05
test_
test_
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~blamar/nova/libvirt-cleanup-branch into lp:nova failed. Below is the output from the failed tests.
FloatingIpTest
test_
test_
test_
test_
test_
test_
test_
FixedIpTest
test_
test_
test_
test_
FlavorsExtraSpe
test_create OK 0.05
test_
test_delete OK 0.05
test_index OK 0.05
test_
test_show OK 0.05
test_
test_
test_
test_
test_
AccountsTest
test_
test_
test_
test_
AdminAPITest
test_
test_
APITest
test_
test_
test_
Test
test_
test_
test_
test_
test_bad_token OK 0.06
test_
test_
test_no_user OK 0.06
test_
test_
Monty Taylor (mordred) wrote : | # |
skipIf seems like an excellent way to describe conditional skipping... except that now it's throwing a SkipTest error.
The other tests which skip things use skip_test ... could you look in to fixing things so that skipIf does not throw a SkipTest error (or that the test harness understands that we skipped that on purpose)
OR
If unittest2 was added to get skipIf and skipIf is causing these problems - perhaps just use skip_test and remove the unittest2 need? (I mean, unless there is some grand scheme for migrating to unittest2 underway)
Unmerged revisions
- 1221. By Brian Lamar
-
Merged trunk
- 1220. By Brian Lamar
-
Merged trunk.
- 1219. By Brian Lamar
-
review feedback from bcwaldon and ed leafe
- 1218. By Brian Lamar
-
Two small cleanups in libvirt:
1) Moved flag defines to __init__.py
2) Replaced lazy-loading of imports with utils.optional_import (new) One unrelated change is the addition of unittest2 to pip-requires. I'm
using the unittest.skipIf method in test_libvirt.py and unittest2 has
backported all Python 2.7 unittest improvements.
Preview Diff
1 | === modified file 'nova/tests/test_libvirt.py' |
2 | --- nova/tests/test_libvirt.py 2011-06-28 16:20:02 +0000 |
3 | +++ nova/tests/test_libvirt.py 2011-07-08 00:31:40 +0000 |
4 | @@ -22,6 +22,7 @@ |
5 | import shutil |
6 | import sys |
7 | |
8 | +import unittest2 as unittest |
9 | from xml.etree.ElementTree import fromstring as xml_to_tree |
10 | from xml.dom.minidom import parseString as xml_to_dom |
11 | |
12 | @@ -37,7 +38,12 @@ |
13 | from nova.virt.libvirt import connection |
14 | from nova.virt.libvirt import firewall |
15 | |
16 | -libvirt = None |
17 | + |
18 | +libvirt = utils.optional_import("libvirt") |
19 | +libxml2 = utils.optional_import("libxml2") |
20 | +Template = utils.optional_import("Cheetah.Template") |
21 | + |
22 | + |
23 | FLAGS = flags.FLAGS |
24 | flags.DECLARE('instances_path', 'nova.compute.manager') |
25 | |
26 | @@ -147,7 +153,6 @@ |
27 | |
28 | def setUp(self): |
29 | super(LibvirtConnTestCase, self).setUp() |
30 | - connection._late_load_cheetah() |
31 | self.flags(fake_call=True) |
32 | self.manager = manager.AuthManager() |
33 | |
34 | @@ -188,20 +193,6 @@ |
35 | 'image_ref': '123456', |
36 | 'instance_type_id': '5'} # m1.small |
37 | |
38 | - def lazy_load_library_exists(self): |
39 | - """check if libvirt is available.""" |
40 | - # try to connect libvirt. if fail, skip test. |
41 | - try: |
42 | - import libvirt |
43 | - import libxml2 |
44 | - except ImportError: |
45 | - return False |
46 | - global libvirt |
47 | - libvirt = __import__('libvirt') |
48 | - connection.libvirt = __import__('libvirt') |
49 | - connection.libxml2 = __import__('libxml2') |
50 | - return True |
51 | - |
52 | def create_fake_libvirt_mock(self, **kwargs): |
53 | """Defining mocks for LibvirtConnection(libvirt is not used).""" |
54 | |
55 | @@ -337,9 +328,6 @@ |
56 | self._check_xml_and_container(instance_data) |
57 | |
58 | def test_snapshot(self): |
59 | - if not self.lazy_load_library_exists(): |
60 | - return |
61 | - |
62 | FLAGS.image_service = 'nova.image.fake.FakeImageService' |
63 | |
64 | # Start test |
65 | @@ -372,9 +360,6 @@ |
66 | self.assertEquals(snapshot['name'], snapshot_name) |
67 | |
68 | def test_snapshot_no_image_architecture(self): |
69 | - if not self.lazy_load_library_exists(): |
70 | - return |
71 | - |
72 | FLAGS.image_service = 'nova.image.fake.FakeImageService' |
73 | |
74 | # Start test |
75 | @@ -649,10 +634,6 @@ |
76 | |
77 | def test_ensure_filtering_rules_for_instance_timeout(self): |
78 | """ensure_filtering_fules_for_instance() finishes with timeout.""" |
79 | - # Skip if non-libvirt environment |
80 | - if not self.lazy_load_library_exists(): |
81 | - return |
82 | - |
83 | # Preparing mocks |
84 | def fake_none(self): |
85 | return |
86 | @@ -690,12 +671,9 @@ |
87 | |
88 | db.instance_destroy(self.context, instance_ref['id']) |
89 | |
90 | + @unittest.skipIf(libvirt is None, "libvirt required for this test") |
91 | def test_live_migration_raises_exception(self): |
92 | """Confirms recover method is called when exceptions are raised.""" |
93 | - # Skip if non-libvirt environment |
94 | - if not self.lazy_load_library_exists(): |
95 | - return |
96 | - |
97 | # Preparing data |
98 | self.compute = utils.import_object(FLAGS.compute_manager) |
99 | instance_dict = {'host': 'fake', 'state': power_state.RUNNING, |
100 | @@ -741,10 +719,6 @@ |
101 | |
102 | @test.skip_test("test needs rewrite: instance no longer has mac_address") |
103 | def test_spawn_with_network_info(self): |
104 | - # Skip if non-libvirt environment |
105 | - if not self.lazy_load_library_exists(): |
106 | - return |
107 | - |
108 | # Preparing mocks |
109 | def fake_none(self, instance): |
110 | return |
111 | @@ -829,20 +803,6 @@ |
112 | self.fw = firewall.IptablesFirewallDriver( |
113 | get_connection=lambda: self.fake_libvirt_connection) |
114 | |
115 | - def lazy_load_library_exists(self): |
116 | - """check if libvirt is available.""" |
117 | - # try to connect libvirt. if fail, skip test. |
118 | - try: |
119 | - import libvirt |
120 | - import libxml2 |
121 | - except ImportError: |
122 | - return False |
123 | - global libvirt |
124 | - libvirt = __import__('libvirt') |
125 | - connection.libvirt = __import__('libvirt') |
126 | - connection.libxml2 = __import__('libxml2') |
127 | - return True |
128 | - |
129 | def tearDown(self): |
130 | self.manager.delete_project(self.project) |
131 | self.manager.delete_user(self.user) |
132 | @@ -1056,10 +1016,6 @@ |
133 | |
134 | @test.skip_test("skip libvirt test project_get_network no longer exists") |
135 | def test_unfilter_instance_undefines_nwfilter(self): |
136 | - # Skip if non-libvirt environment |
137 | - if not self.lazy_load_library_exists(): |
138 | - return |
139 | - |
140 | admin_ctxt = context.get_admin_context() |
141 | |
142 | fakefilter = NWFilterFakes() |
143 | |
144 | === modified file 'nova/utils.py' |
145 | --- nova/utils.py 2011-06-30 19:20:59 +0000 |
146 | +++ nova/utils.py 2011-07-08 00:31:40 +0000 |
147 | @@ -75,6 +75,21 @@ |
148 | return cls() |
149 | |
150 | |
151 | +def optional_import(import_str): |
152 | + """Attempt to import the given string, but fall back to returning None. |
153 | + |
154 | + :param import_str: The name of the module to import. |
155 | + :returns: The requested module, or None if unable to import. |
156 | + |
157 | + """ |
158 | + try: |
159 | + __import__(import_str) |
160 | + return sys.modules[import_str] |
161 | + except ImportError: |
162 | + LOG.info(_("Unable to import optional module: %s") % import_str) |
163 | + return None |
164 | + |
165 | + |
166 | def vpn_ping(address, port, timeout=0.05, session_id=None): |
167 | """Sends a vpn negotiation packet and returns the server session. |
168 | |
169 | |
170 | === modified file 'nova/virt/libvirt/__init__.py' |
171 | --- nova/virt/libvirt/__init__.py 2011-04-22 16:47:09 +0000 |
172 | +++ nova/virt/libvirt/__init__.py 2011-07-08 00:31:40 +0000 |
173 | @@ -0,0 +1,85 @@ |
174 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
175 | + |
176 | +# Copyright 2011 OpenStack, LLC |
177 | +# |
178 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
179 | +# not use this file except in compliance with the License. You may obtain |
180 | +# a copy of the License at |
181 | +# |
182 | +# http://www.apache.org/licenses/LICENSE-2.0 |
183 | +# |
184 | +# Unless required by applicable law or agreed to in writing, software |
185 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
186 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
187 | +# License for the specific language governing permissions and limitations |
188 | +# under the License. |
189 | + |
190 | +from nova import flags |
191 | +from nova import utils |
192 | + |
193 | + |
194 | +FLAGS = flags.FLAGS |
195 | + |
196 | + |
197 | +flags.DECLARE('live_migration_retry_count', 'nova.compute.manager') |
198 | + |
199 | +flags.DEFINE_string('rescue_image_id', |
200 | + 'ami-rescue', 'Rescue ami image') |
201 | + |
202 | +flags.DEFINE_string('rescue_kernel_id', |
203 | + 'aki-rescue', 'Rescue aki image') |
204 | + |
205 | +flags.DEFINE_string('rescue_ramdisk_id', |
206 | + 'ari-rescue', |
207 | + 'Rescue ari image') |
208 | + |
209 | +flags.DEFINE_string('libvirt_xml_template', |
210 | + utils.abspath('virt/libvirt.xml.template'), |
211 | + 'Libvirt XML Template') |
212 | + |
213 | +flags.DEFINE_string('libvirt_type', |
214 | + 'kvm', |
215 | + 'Libvirt driver (kvm, lxc, qemu, uml, or xen)') |
216 | + |
217 | +flags.DEFINE_string('libvirt_uri', |
218 | + '', |
219 | + 'Override the default libvirt URI set by libvirt_type') |
220 | + |
221 | +flags.DEFINE_string('ajaxterm_portrange', |
222 | + '10000-12000', |
223 | + 'Range of ports that ajaxterm should randomly try to bind') |
224 | + |
225 | +flags.DEFINE_string('firewall_driver', |
226 | + 'nova.virt.libvirt.firewall.IptablesFirewallDriver', |
227 | + 'Firewall driver (defaults to iptables)') |
228 | + |
229 | +flags.DEFINE_string('cpuinfo_xml_template', |
230 | + utils.abspath('virt/cpuinfo.xml.template'), |
231 | + 'CpuInfo XML Template (Used only live migration now)') |
232 | + |
233 | +flags.DEFINE_string('live_migration_uri', |
234 | + "qemu+tcp://%s/system", |
235 | + 'Define protocol used by live_migration feature') |
236 | + |
237 | +flags.DEFINE_string('live_migration_flag', |
238 | + "VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER", |
239 | + 'Define live migration behavior.') |
240 | + |
241 | +flags.DEFINE_string('qemu_img', 'qemu-img', |
242 | + 'binary to use for qemu-img commands') |
243 | + |
244 | +flags.DEFINE_integer('live_migration_bandwidth', 0, |
245 | + 'Define live migration behavior') |
246 | + |
247 | +flags.DEFINE_bool('start_guests_on_host_boot', |
248 | + False, |
249 | + 'Whether to restart guests when the host reboots') |
250 | + |
251 | +flags.DEFINE_bool('allow_project_net_traffic', |
252 | + True, |
253 | + 'Whether to allow in project network traffic') |
254 | + |
255 | +flags.DEFINE_bool('use_cow_images', |
256 | + True, |
257 | + 'Whether to use cow images') |
258 | + |
259 | |
260 | === modified file 'nova/virt/libvirt/connection.py' |
261 | --- nova/virt/libvirt/connection.py 2011-07-07 18:30:47 +0000 |
262 | +++ nova/virt/libvirt/connection.py 2011-07-08 00:31:40 +0000 |
263 | @@ -17,23 +17,10 @@ |
264 | # License for the specific language governing permissions and limitations |
265 | # under the License. |
266 | |
267 | -""" |
268 | -A connection to a hypervisor through libvirt. |
269 | +"""Provides logic for connecting to a hypervisor through libvirt. |
270 | |
271 | Supports KVM, LXC, QEMU, UML, and XEN. |
272 | |
273 | -**Related Flags** |
274 | - |
275 | -:libvirt_type: Libvirt domain type. Can be kvm, qemu, uml, xen |
276 | - (default: kvm). |
277 | -:libvirt_uri: Override for the default libvirt URI (depends on libvirt_type). |
278 | -:libvirt_xml_template: Libvirt XML Template. |
279 | -:rescue_image_id: Rescue ami image (default: ami-rescue). |
280 | -:rescue_kernel_id: Rescue aki image (default: aki-rescue). |
281 | -:rescue_ramdisk_id: Rescue ari image (default: ari-rescue). |
282 | -:injected_network_template: Template file for injected network |
283 | -:allow_project_net_traffic: Whether to allow in project network traffic |
284 | - |
285 | """ |
286 | |
287 | import hashlib |
288 | @@ -71,83 +58,27 @@ |
289 | from nova.virt.libvirt import netutils |
290 | |
291 | |
292 | -libvirt = None |
293 | -libxml2 = None |
294 | -Template = None |
295 | +libvirt = utils.optional_import("libvirt") |
296 | +libxml2 = utils.optional_import("libxml2") |
297 | +Template = utils.optional_import("Cheetah.Template") |
298 | |
299 | |
300 | LOG = logging.getLogger('nova.virt.libvirt_conn') |
301 | - |
302 | - |
303 | FLAGS = flags.FLAGS |
304 | -flags.DECLARE('live_migration_retry_count', 'nova.compute.manager') |
305 | -# TODO(vish): These flags should probably go into a shared location |
306 | -flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image') |
307 | -flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image') |
308 | -flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image') |
309 | -flags.DEFINE_string('libvirt_xml_template', |
310 | - utils.abspath('virt/libvirt.xml.template'), |
311 | - 'Libvirt XML Template') |
312 | -flags.DEFINE_string('libvirt_type', |
313 | - 'kvm', |
314 | - 'Libvirt domain type (valid options are: ' |
315 | - 'kvm, lxc, qemu, uml, xen)') |
316 | -flags.DEFINE_string('libvirt_uri', |
317 | - '', |
318 | - 'Override the default libvirt URI (which is dependent' |
319 | - ' on libvirt_type)') |
320 | -flags.DEFINE_bool('allow_project_net_traffic', |
321 | - True, |
322 | - 'Whether to allow in project network traffic') |
323 | -flags.DEFINE_bool('use_cow_images', |
324 | - True, |
325 | - 'Whether to use cow images') |
326 | -flags.DEFINE_string('ajaxterm_portrange', |
327 | - '10000-12000', |
328 | - 'Range of ports that ajaxterm should randomly try to bind') |
329 | -flags.DEFINE_string('firewall_driver', |
330 | - 'nova.virt.libvirt.firewall.IptablesFirewallDriver', |
331 | - 'Firewall driver (defaults to iptables)') |
332 | -flags.DEFINE_string('cpuinfo_xml_template', |
333 | - utils.abspath('virt/cpuinfo.xml.template'), |
334 | - 'CpuInfo XML Template (Used only live migration now)') |
335 | -flags.DEFINE_string('live_migration_uri', |
336 | - "qemu+tcp://%s/system", |
337 | - 'Define protocol used by live_migration feature') |
338 | -flags.DEFINE_string('live_migration_flag', |
339 | - "VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER", |
340 | - 'Define live migration behavior.') |
341 | -flags.DEFINE_integer('live_migration_bandwidth', 0, |
342 | - 'Define live migration behavior') |
343 | -flags.DEFINE_string('qemu_img', 'qemu-img', |
344 | - 'binary to use for qemu-img commands') |
345 | -flags.DEFINE_bool('start_guests_on_host_boot', False, |
346 | - 'Whether to restart guests when the host reboots') |
347 | |
348 | |
349 | def get_connection(read_only): |
350 | - # These are loaded late so that there's no need to install these |
351 | - # libraries when not using libvirt. |
352 | - # Cheetah is separate because the unit tests want to load Cheetah, |
353 | - # but not libvirt. |
354 | - global libvirt |
355 | - global libxml2 |
356 | + """Retrieve a libvirt connection object.""" |
357 | if libvirt is None: |
358 | - libvirt = __import__('libvirt') |
359 | + raise ImportError(_("Unable to import libvirt")) |
360 | if libxml2 is None: |
361 | - libxml2 = __import__('libxml2') |
362 | - _late_load_cheetah() |
363 | + raise ImportError(_("Unable to import libxml2")) |
364 | + if Template is None: |
365 | + raise ImportError(_("Unable to import Cheetah.Template")) |
366 | + |
367 | return LibvirtConnection(read_only) |
368 | |
369 | |
370 | -def _late_load_cheetah(): |
371 | - global Template |
372 | - if Template is None: |
373 | - t = __import__('Cheetah.Template', globals(), locals(), |
374 | - ['Template'], -1) |
375 | - Template = t.Template |
376 | - |
377 | - |
378 | def _strip_dev(mount_path): |
379 | return re.sub(r'^/dev/', '', mount_path) |
380 | |
381 | @@ -896,7 +827,7 @@ |
382 | nets.append(net_info) |
383 | |
384 | if have_injected_networks: |
385 | - net = str(Template(ifc_template, |
386 | + net = str(Template.Template(ifc_template, |
387 | searchList=[{'interfaces': nets, |
388 | 'use_ipv6': FLAGS.use_ipv6}])) |
389 | |
390 | @@ -1035,7 +966,7 @@ |
391 | LOG.debug(_('instance %s: starting toXML method'), instance['name']) |
392 | xml_info = self._prepare_xml_info(instance, rescue, network_info, |
393 | block_device_mapping) |
394 | - xml = str(Template(self.libvirt_xml, searchList=[xml_info])) |
395 | + xml = str(Template.Template(self.libvirt_xml, searchList=[xml_info])) |
396 | LOG.debug(_('instance %s: finished toXML method'), instance['name']) |
397 | return xml |
398 | |
399 | @@ -1439,7 +1370,7 @@ |
400 | |
401 | LOG.info(_('Instance launched has CPU info:\n%s') % cpu_info) |
402 | dic = utils.loads(cpu_info) |
403 | - xml = str(Template(self.cpuinfo_xml, searchList=dic)) |
404 | + xml = str(Template.Template(self.cpuinfo_xml, searchList=dic)) |
405 | LOG.info(_('to xml...\n:%s ' % xml)) |
406 | |
407 | u = "http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult" |
408 | |
409 | === modified file 'nova/virt/libvirt/firewall.py' |
410 | --- nova/virt/libvirt/firewall.py 2011-06-27 21:48:03 +0000 |
411 | +++ nova/virt/libvirt/firewall.py 2011-07-08 00:31:40 +0000 |
412 | @@ -32,11 +32,7 @@ |
413 | FLAGS = flags.FLAGS |
414 | |
415 | |
416 | -try: |
417 | - import libvirt |
418 | -except ImportError: |
419 | - LOG.warn(_("Libvirt module could not be loaded. NWFilterFirewall will " |
420 | - "not work correctly.")) |
421 | +libvirt = utils.optional_import("libvirt") |
422 | |
423 | |
424 | class FirewallDriver(object): |
425 | |
426 | === modified file 'tools/pip-requires' |
427 | --- tools/pip-requires 2011-06-30 18:11:03 +0000 |
428 | +++ tools/pip-requires 2011-07-08 00:31:40 +0000 |
429 | @@ -33,3 +33,4 @@ |
430 | nosexcover |
431 | GitPython |
432 | paramiko |
433 | +unittest2 |
I like it