Merge lp:~newell-jensen/maas/prefix-filter-virsh-bug-1393423 into lp:~maas-committers/maas/trunk

Proposed by Newell Jensen
Status: Merged
Approved by: Newell Jensen
Approved revision: no longer in the source branch.
Merged at revision: 3404
Proposed branch: lp:~newell-jensen/maas/prefix-filter-virsh-bug-1393423
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 332 lines (+113/-18)
9 files modified
src/maasserver/api/node_groups.py (+6/-1)
src/maasserver/api/tests/test_nodegroup.py (+70/-0)
src/maasserver/models/nodegroup.py (+4/-2)
src/maasserver/models/tests/test_nodegroup.py (+5/-2)
src/provisioningserver/drivers/hardware/tests/test_virsh.py (+11/-4)
src/provisioningserver/drivers/hardware/virsh.py (+9/-4)
src/provisioningserver/rpc/cluster.py (+1/-0)
src/provisioningserver/rpc/clusterservice.py (+2/-2)
src/provisioningserver/rpc/tests/test_clusterservice.py (+5/-3)
To merge this branch: bzr merge lp:~newell-jensen/maas/prefix-filter-virsh-bug-1393423
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+243569@code.launchpad.net

Commit message

This branch gives the user the capability to add an optional `prefix_filter` parameter to the api call for probe_and_enlist_hardware when the model type is `virsh`, which will only enlist the machines with the prefix in their names.

To post a comment you must log in.
Revision history for this message
Newell Jensen (newell-jensen) wrote :

Example usage:

Let us suppose there is a MAAS profile login named `admin`. Let us also suppose that we have three virsh machines with the names node0, node1, and nokk that are on MAAS's network and can PXE boot etc. Additionally let us assume the username for the host is ubuntu and the password for this system is ubuntu as well, with an IP address of 10.0.0.2.

1. Get UUID of the cluster controller for the `admin` profile.

$ uuid=$(maas admin node-groups list | grep uuid | cut -d'"' -f4)

2. probe and enlist ALL virsh nodes.

$ maas admin node-group probe-and-enlist-hardware $uuid model=virsh power_address=qemu+ssh://ubuntu@10.0.0.2/system power_pass=ubuntu

The above command will enlist ALL the virsh nodes, which are the three noted above.
This shows that the probe and enlist still works as it should. Now let's add a prefix_filter parameter to the api command so that we only enlist the nodes that start with `node` (this prefix_filter can obviously be anything that you desire to filter on). Make sure to delete all you current nodes in MAAS so that we can do the probe and enlist again. Once that is complete-

3. probe and enlist nodes with prefix of `node`.

$ maas admin node-group probe-and-enlist-hardware $uuid model=virsh power_address=qemu+ssh://ubuntu@10.0.0.2/system power_pass=ubuntu prefix_filter=node

This will now only enlist the two nodes that begin with the prefix of `node`, node0 and node1.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good.

Thanks for adding the sm15k test in there as well.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/node_groups.py'
--- src/maasserver/api/node_groups.py 2014-11-27 08:05:50 +0000
+++ src/maasserver/api/node_groups.py 2014-12-03 22:30:57 +0000
@@ -395,6 +395,8 @@
395 :param power_pass: The password to use, when qemu+ssh is given as a395 :param power_pass: The password to use, when qemu+ssh is given as a
396 connection string and ssh key authentication is not being used.396 connection string and ssh key authentication is not being used.
397 :type power_pass: unicode397 :type power_pass: unicode
398 :param prefix_filter: Only import nodes based on supplied prefix.
399 :type prefix_filter: unicode
398400
399 Returns 404 if the nodegroup (cluster) is not found.401 Returns 404 if the nodegroup (cluster) is not found.
400 Returns 403 if the user does not have access to the nodegroup.402 Returns 403 if the user does not have access to the nodegroup.
@@ -417,8 +419,11 @@
417 poweraddr = get_mandatory_param(request.data, 'power_address')419 poweraddr = get_mandatory_param(request.data, 'power_address')
418 password = get_optional_param(420 password = get_optional_param(
419 request.data, 'power_pass', default=None)421 request.data, 'power_pass', default=None)
422 prefix_filter = get_optional_param(
423 request.data, 'prefix_filter', default=None)
420424
421 nodegroup.add_virsh(poweraddr, password=password)425 nodegroup.add_virsh(
426 poweraddr, password=password, prefix_filter=prefix_filter)
422 else:427 else:
423 return HttpResponse(status=httplib.BAD_REQUEST)428 return HttpResponse(status=httplib.BAD_REQUEST)
424429
425430
=== modified file 'src/maasserver/api/tests/test_nodegroup.py'
--- src/maasserver/api/tests/test_nodegroup.py 2014-10-23 18:21:41 +0000
+++ src/maasserver/api/tests/test_nodegroup.py 2014-12-03 22:30:57 +0000
@@ -16,6 +16,7 @@
1616
17import httplib17import httplib
18import json18import json
19import random
19from textwrap import dedent20from textwrap import dedent
20from urlparse import urlparse21from urlparse import urlparse
2122
@@ -56,6 +57,8 @@
56 )57 )
57from mock import Mock58from mock import Mock
58from provisioningserver.rpc.cluster import (59from provisioningserver.rpc.cluster import (
60 AddSeaMicro15k,
61 AddVirsh,
59 EnlistNodesFromMSCM,62 EnlistNodesFromMSCM,
60 EnlistNodesFromUCSM,63 EnlistNodesFromUCSM,
61 ImportBootImages,64 ImportBootImages,
@@ -327,6 +330,73 @@
327 httplib.BAD_REQUEST, response.status_code,330 httplib.BAD_REQUEST, response.status_code,
328 explain_unexpected_response(httplib.BAD_REQUEST, response))331 explain_unexpected_response(httplib.BAD_REQUEST, response))
329332
333 def test_probe_and_enlist_hardware_adds_seamicro(self):
334 nodegroup = factory.make_NodeGroup()
335 model = 'seamicro15k'
336 mac = factory.make_mac_address()
337 username = factory.make_name('user')
338 password = factory.make_name('password')
339 power_control = random.choice(
340 ['ipmi', 'restapi', 'restapi2'])
341 self.become_admin()
342
343 getClientFor = self.patch(nodegroup_module, 'getClientFor')
344 client = getClientFor.return_value
345 nodegroup = factory.make_NodeGroup()
346
347 response = self.client.post(
348 reverse('nodegroup_handler', args=[nodegroup.uuid]),
349 {
350 'op': 'probe_and_enlist_hardware',
351 'model': model,
352 'mac': mac,
353 'username': username,
354 'password': password,
355 'power_control': power_control,
356 })
357
358 self.assertEqual(
359 httplib.OK, response.status_code,
360 explain_unexpected_response(httplib.OK, response))
361
362 self.expectThat(
363 client,
364 MockCalledOnceWith(
365 AddSeaMicro15k, mac=mac, username=username,
366 password=password, power_control=power_control))
367
368 def test_probe_and_enlist_hardware_adds_virsh(self):
369 nodegroup = factory.make_NodeGroup()
370 model = 'virsh'
371 poweraddr = factory.make_ipv4_address()
372 password = factory.make_name('password')
373 prefix_filter = factory.make_name('filter')
374 self.become_admin()
375
376 getClientFor = self.patch(nodegroup_module, 'getClientFor')
377 client = getClientFor.return_value
378 nodegroup = factory.make_NodeGroup()
379
380 response = self.client.post(
381 reverse('nodegroup_handler', args=[nodegroup.uuid]),
382 {
383 'op': 'probe_and_enlist_hardware',
384 'model': model,
385 'power_address': poweraddr,
386 'power_pass': password,
387 'prefix_filter': prefix_filter,
388 })
389
390 self.assertEqual(
391 httplib.OK, response.status_code,
392 explain_unexpected_response(httplib.OK, response))
393
394 self.expectThat(
395 client,
396 MockCalledOnceWith(
397 AddVirsh, poweraddr=poweraddr,
398 password=password, prefix_filter=prefix_filter))
399
330 def test_probe_and_enlist_ucsm_adds_ucsm(self):400 def test_probe_and_enlist_ucsm_adds_ucsm(self):
331 nodegroup = factory.make_NodeGroup()401 nodegroup = factory.make_NodeGroup()
332 url = 'http://url'402 url = 'http://url'
333403
=== modified file 'src/maasserver/models/nodegroup.py'
--- src/maasserver/models/nodegroup.py 2014-10-23 18:21:41 +0000
+++ src/maasserver/models/nodegroup.py 2014-12-03 22:30:57 +0000
@@ -337,11 +337,12 @@
337 AddSeaMicro15k, mac=mac, username=username,337 AddSeaMicro15k, mac=mac, username=username,
338 password=password, power_control=power_control)338 password=password, power_control=power_control)
339339
340 def add_virsh(self, poweraddr, password=None):340 def add_virsh(self, poweraddr, password=None, prefix_filter=None):
341 """ Add all of the virtual machines inside a virsh controller.341 """ Add all of the virtual machines inside a virsh controller.
342342
343 :param poweraddr: virsh connection string343 :param poweraddr: virsh connection string
344 :param password: ssh password344 :param password: ssh password
345 :param prefix_filter: import based on prefix
345346
346 :raises NoConnectionsAvailable: If no connections to the cluster347 :raises NoConnectionsAvailable: If no connections to the cluster
347 are available.348 are available.
@@ -355,7 +356,8 @@
355 raise356 raise
356 else:357 else:
357 return client(358 return client(
358 AddVirsh, poweraddr=poweraddr, password=password)359 AddVirsh, poweraddr=poweraddr,
360 password=password, prefix_filter=prefix_filter)
359361
360 def enlist_nodes_from_ucsm(self, url, username, password):362 def enlist_nodes_from_ucsm(self, url, username, password):
361 """ Add the servers from a Cicso UCS Manager.363 """ Add the servers from a Cicso UCS Manager.
362364
=== modified file 'src/maasserver/models/tests/test_nodegroup.py'
--- src/maasserver/models/tests/test_nodegroup.py 2014-10-23 18:21:41 +0000
+++ src/maasserver/models/tests/test_nodegroup.py 2014-12-03 22:30:57 +0000
@@ -506,7 +506,9 @@
506506
507 self.expectThat(507 self.expectThat(
508 protocol.AddVirsh,508 protocol.AddVirsh,
509 MockCalledOnceWith(ANY, poweraddr=poweraddr, password=password))509 MockCalledOnceWith(
510 ANY, poweraddr=poweraddr,
511 password=password, prefix_filter=None))
510512
511 def test_add_virsh_calls_client_with_resource_endpoint(self):513 def test_add_virsh_calls_client_with_resource_endpoint(self):
512 getClientFor = self.patch(nodegroup_module, 'getClientFor')514 getClientFor = self.patch(nodegroup_module, 'getClientFor')
@@ -520,7 +522,8 @@
520 self.expectThat(522 self.expectThat(
521 client,523 client,
522 MockCalledOnceWith(524 MockCalledOnceWith(
523 AddVirsh, poweraddr=poweraddr, password=password))525 AddVirsh, poweraddr=poweraddr,
526 password=password, prefix_filter=None))
524527
525 def test_add_virsh_raises_if_no_connection_to_cluster(self):528 def test_add_virsh_raises_if_no_connection_to_cluster(self):
526 getClientFor = self.patch(nodegroup_module, 'getClientFor')529 getClientFor = self.patch(nodegroup_module, 'getClientFor')
527530
=== modified file 'src/provisioningserver/drivers/hardware/tests/test_virsh.py'
--- src/provisioningserver/drivers/hardware/tests/test_virsh.py 2014-09-18 12:44:38 +0000
+++ src/provisioningserver/drivers/hardware/tests/test_virsh.py 2014-12-03 22:30:57 +0000
@@ -53,10 +53,10 @@
53class TestVirshSSH(MAASTestCase):53class TestVirshSSH(MAASTestCase):
54 """Tests for `VirshSSH`."""54 """Tests for `VirshSSH`."""
5555
56 def configure_virshssh_pexpect(self, inputs=None):56 def configure_virshssh_pexpect(self, inputs=None, dom_prefix=None):
57 """Configures the VirshSSH class to use 'cat' process57 """Configures the VirshSSH class to use 'cat' process
58 for testing instead of the actual virsh."""58 for testing instead of the actual virsh."""
59 conn = virsh.VirshSSH(timeout=0.1)59 conn = virsh.VirshSSH(timeout=0.1, dom_prefix=dom_prefix)
60 self.addCleanup(conn.close)60 self.addCleanup(conn.close)
61 self.patch(conn, '_execute')61 self.patch(conn, '_execute')
62 conn._spawn('cat')62 conn._spawn('cat')
@@ -65,9 +65,9 @@
65 conn.sendline(line)65 conn.sendline(line)
66 return conn66 return conn
6767
68 def configure_virshssh(self, output):68 def configure_virshssh(self, output, dom_prefix=None):
69 self.patch(virsh.VirshSSH, 'run').return_value = output69 self.patch(virsh.VirshSSH, 'run').return_value = output
70 return virsh.VirshSSH()70 return virsh.VirshSSH(dom_prefix=dom_prefix)
7171
72 def test_login_prompt(self):72 def test_login_prompt(self):
73 virsh_outputs = [73 virsh_outputs = [
@@ -157,6 +157,13 @@
157 expected = conn.list()157 expected = conn.list()
158 self.assertItemsEqual(names, expected)158 self.assertItemsEqual(names, expected)
159159
160 def test_list_dom_prefix(self):
161 prefix = 'dom_prefix'
162 names = [prefix + factory.make_name('machine') for _ in range(3)]
163 conn = self.configure_virshssh('\n'.join(names), dom_prefix=prefix)
164 expected = conn.list()
165 self.assertItemsEqual(names, expected)
166
160 def test_get_state(self):167 def test_get_state(self):
161 state = factory.make_name('state')168 state = factory.make_name('state')
162 conn = self.configure_virshssh(state)169 conn = self.configure_virshssh(state)
163170
=== modified file 'src/provisioningserver/drivers/hardware/virsh.py'
--- src/provisioningserver/drivers/hardware/virsh.py 2014-12-02 05:56:30 +0000
+++ src/provisioningserver/drivers/hardware/virsh.py 2014-12-03 22:30:57 +0000
@@ -79,10 +79,14 @@
79 I_PROMPT_SSHKEY = PROMPTS.index(PROMPT_SSHKEY)79 I_PROMPT_SSHKEY = PROMPTS.index(PROMPT_SSHKEY)
80 I_PROMPT_PASSWORD = PROMPTS.index(PROMPT_PASSWORD)80 I_PROMPT_PASSWORD = PROMPTS.index(PROMPT_PASSWORD)
8181
82 def __init__(self, timeout=30, maxread=2000):82 def __init__(self, timeout=30, maxread=2000, dom_prefix=None):
83 super(VirshSSH, self).__init__(83 super(VirshSSH, self).__init__(
84 None, timeout=timeout, maxread=maxread)84 None, timeout=timeout, maxread=maxread)
85 self.name = '<virssh>'85 self.name = '<virssh>'
86 if dom_prefix is None:
87 self.dom_prefix = ''
88 else:
89 self.dom_prefix = dom_prefix
8690
87 def _execute(self, poweraddr):91 def _execute(self, poweraddr):
88 """Spawns the pexpect command."""92 """Spawns the pexpect command."""
@@ -136,7 +140,8 @@
136 def list(self):140 def list(self):
137 """Lists all virtual machines by name."""141 """Lists all virtual machines by name."""
138 machines = self.run(['list', '--all', '--name'])142 machines = self.run(['list', '--all', '--name'])
139 return machines.strip().splitlines()143 machines = machines.strip().splitlines()
144 return [m for m in machines if m.startswith(self.dom_prefix)]
140145
141 def get_state(self, machine):146 def get_state(self, machine):
142 """Gets the virtual machine state."""147 """Gets the virtual machine state."""
@@ -183,13 +188,13 @@
183 return True188 return True
184189
185190
186def probe_virsh_and_enlist(poweraddr, password=None):191def probe_virsh_and_enlist(poweraddr, password=None, prefix_filter=None):
187 """Extracts all of the virtual machines from virsh and enlists them192 """Extracts all of the virtual machines from virsh and enlists them
188 into MAAS.193 into MAAS.
189194
190 :param poweraddr: virsh connection string195 :param poweraddr: virsh connection string
191 """196 """
192 conn = VirshSSH()197 conn = VirshSSH(dom_prefix=prefix_filter)
193 if not conn.login(poweraddr, password):198 if not conn.login(poweraddr, password):
194 raise VirshError('Failed to login to virsh console.')199 raise VirshError('Failed to login to virsh console.')
195200
196201
=== modified file 'src/provisioningserver/rpc/cluster.py'
--- src/provisioningserver/rpc/cluster.py 2014-10-23 18:21:41 +0000
+++ src/provisioningserver/rpc/cluster.py 2014-12-03 22:30:57 +0000
@@ -419,6 +419,7 @@
419 arguments = [419 arguments = [
420 (b"poweraddr", amp.Unicode()),420 (b"poweraddr", amp.Unicode()),
421 (b"password", amp.Unicode(optional=True)),421 (b"password", amp.Unicode(optional=True)),
422 (b"prefix_filter", amp.Unicode(optional=True)),
422 ]423 ]
423 response = []424 response = []
424 errors = []425 errors = []
425426
=== modified file 'src/provisioningserver/rpc/clusterservice.py'
--- src/provisioningserver/rpc/clusterservice.py 2014-11-10 15:11:58 +0000
+++ src/provisioningserver/rpc/clusterservice.py 2014-12-03 22:30:57 +0000
@@ -344,12 +344,12 @@
344 return d.addCallback(lambda _: {})344 return d.addCallback(lambda _: {})
345345
346 @cluster.AddVirsh.responder346 @cluster.AddVirsh.responder
347 def add_virsh(self, poweraddr, password):347 def add_virsh(self, poweraddr, password, prefix_filter):
348 """add_virsh()348 """add_virsh()
349349
350 Implementation of :py:class:`~provisioningserver.rpc.cluster.AddVirsh`.350 Implementation of :py:class:`~provisioningserver.rpc.cluster.AddVirsh`.
351 """351 """
352 probe_virsh_and_enlist(poweraddr, password)352 probe_virsh_and_enlist(poweraddr, password, prefix_filter)
353 return {}353 return {}
354354
355 @cluster.AddSeaMicro15k.responder355 @cluster.AddSeaMicro15k.responder
356356
=== modified file 'src/provisioningserver/rpc/tests/test_clusterservice.py'
--- src/provisioningserver/rpc/tests/test_clusterservice.py 2014-11-10 15:11:58 +0000
+++ src/provisioningserver/rpc/tests/test_clusterservice.py 2014-12-03 22:30:57 +0000
@@ -1889,13 +1889,15 @@
1889 clusterservice, 'probe_virsh_and_enlist')1889 clusterservice, 'probe_virsh_and_enlist')
1890 poweraddr = factory.make_name('poweraddr')1890 poweraddr = factory.make_name('poweraddr')
1891 password = factory.make_name('password')1891 password = factory.make_name('password')
1892 prefix_filter = factory.make_name('prefix_filter')
1892 call_responder(Cluster(), cluster.AddVirsh, {1893 call_responder(Cluster(), cluster.AddVirsh, {
1893 "poweraddr": poweraddr,1894 "poweraddr": poweraddr,
1894 "password": password,1895 "password": password,
1896 "prefix_filter": prefix_filter,
1895 })1897 })
1896 self.assertThat(1898 self.assertThat(
1897 probe_virsh_and_enlist, MockCalledOnceWith(1899 probe_virsh_and_enlist, MockCalledOnceWith(
1898 poweraddr, password))1900 poweraddr, password, prefix_filter))
18991901
1900 def test__password_is_optional(self):1902 def test__password_is_optional(self):
1901 probe_virsh_and_enlist = self.patch_autospec(1903 probe_virsh_and_enlist = self.patch_autospec(
@@ -1907,7 +1909,7 @@
1907 })1909 })
1908 self.assertThat(1910 self.assertThat(
1909 probe_virsh_and_enlist, MockCalledOnceWith(1911 probe_virsh_and_enlist, MockCalledOnceWith(
1910 poweraddr, None))1912 poweraddr, None, None))
19111913
1912 def test__can_be_called_without_password_key(self):1914 def test__can_be_called_without_password_key(self):
1913 probe_virsh_and_enlist = self.patch_autospec(1915 probe_virsh_and_enlist = self.patch_autospec(
@@ -1918,7 +1920,7 @@
1918 })1920 })
1919 self.assertThat(1921 self.assertThat(
1920 probe_virsh_and_enlist, MockCalledOnceWith(1922 probe_virsh_and_enlist, MockCalledOnceWith(
1921 poweraddr, None))1923 poweraddr, None, None))
19221924
19231925
1924class TestClusterProtocol_AddSeaMicro15k(MAASTestCase):1926class TestClusterProtocol_AddSeaMicro15k(MAASTestCase):