Merge lp:~julian-edwards/maas/1.7-race-for-staticip-bug-1387262 into lp:~maas-committers/maas/trunk

Proposed by Julian Edwards
Status: Superseded
Proposed branch: lp:~julian-edwards/maas/1.7-race-for-staticip-bug-1387262
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 2560 lines (+1719/-477) (has conflicts)
17 files modified
Makefile (+2/-2)
docs/changelog.rst (+937/-466)
src/maasserver/api/tests/test_node.py (+6/-1)
src/maasserver/forms_settings.py (+10/-0)
src/maasserver/models/config.py (+1/-0)
src/maasserver/models/node.py (+162/-0)
src/maasserver/models/tests/test_bootsource.py (+4/-0)
src/maasserver/models/tests/test_node.py (+417/-0)
src/maasserver/tests/test_bootresources.py (+19/-0)
src/maasserver/views/nodes.py (+5/-0)
src/maasserver/views/tests/test_nodes.py (+54/-0)
src/metadataserver/api.py (+17/-3)
src/metadataserver/models/commissioningscript.py (+22/-5)
src/metadataserver/models/tests/test_noderesults.py (+27/-0)
src/metadataserver/tests/test_api.py (+15/-0)
src/provisioningserver/rpc/boot_images.py (+14/-0)
src/provisioningserver/rpc/tests/test_boot_images.py (+7/-0)
Text conflict in docs/changelog.rst
Text conflict in src/maasserver/api/tests/test_node.py
Text conflict in src/maasserver/models/node.py
Text conflict in src/maasserver/models/tests/test_node.py
Text conflict in src/maasserver/tests/test_bootresources.py
Text conflict in src/maasserver/views/nodes.py
Text conflict in src/maasserver/views/tests/test_nodes.py
Text conflict in src/metadataserver/api.py
Text conflict in src/provisioningserver/rpc/boot_images.py
To merge this branch: bzr merge lp:~julian-edwards/maas/1.7-race-for-staticip-bug-1387262
Reviewer Review Type Date Requested Status
Julian Edwards (community) Approve
Review via email: mp+240799@code.launchpad.net

Commit message

Backport trunk r3337: Use a lock in the critical section of the code that finds and records a new static IP address in the database. Some users have reported errors in the PG log file complaining about inserting duplicates, so there must be a race.

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) wrote :

Approved for a backport by Andres earlier in the bug.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2014-10-01 07:22:16 +0000
3+++ Makefile 2014-11-06 01:24:10 +0000
4@@ -351,8 +351,8 @@
5 # has a bug and always considers apt-source tarballs before the specified
6 # branch. So instead, export to a local tarball which is always found.
7 # Make sure debhelper and dh-apport packages are installed before using this.
8-PACKAGING := $(CURDIR)/../packaging.trunk
9-PACKAGING_BRANCH := lp:~maas-maintainers/maas/packaging
10+PACKAGING := $(CURDIR)/../packaging.utopic
11+PACKAGING_BRANCH := lp:~maas-maintainers/maas/packaging.utopic
12
13 package_branch:
14 @echo Downloading/refreshing packaging branch...
15
16=== modified file 'docs/changelog.rst'
17--- docs/changelog.rst 2014-11-05 03:36:42 +0000
18+++ docs/changelog.rst 2014-11-06 01:24:10 +0000
19@@ -2,472 +2,943 @@
20 Changelog
21 =========
22
23-1.7.0
24-=====
25-
26-Important announcements
27------------------------
28-
29-**Re-import your boot images**
30- You must re-import your boot images, see below for details.
31-
32-**Update Curtin preseed files**
33- Two changes were made to Curtin preseed files that need your attention
34- if you made any customisations:
35-
36- * The OS name must now appear in the filename. The new schema is shown
37- here, each file pattern is tried in turn until a match is found::
38-
39- {prefix}_{osystem}_{node_arch}_{node_subarch}_{release}_{node_name}
40- {prefix}_{osystem}_{node_arch}_{node_subarch}_{release}
41- {prefix}_{osystem}_{node_arch}_{node_subarch}
42- {prefix}_{osystem}_{node_arch}
43- {prefix}_{osystem}
44- {prefix}
45-
46- * If you are modifying ``/etc/network/interfaces`` in the preseed, it must be
47- moved so it is processed last in ``late_commands`` since MAAS now writes
48- to this file itself as part of IPv6 setup. For example::
49-
50- late_commands:
51- bonding_02: ["curtin", "in-target", "--", "wget", "-O", "/etc/network/interfaces", "http://[...snip...]"]
52-
53- must now look like this::
54-
55- late_commands:
56- zz_write_ifaces: ["curtin", "in-target", "--", "wget", "-O", "/etc/network/interfaces", "http://[...snip...]"]
57-
58- The leading ``zz`` ensures the command sorts to the end of the
59- ``late_commands`` list.
60-
61-
62-Major new features
63-------------------
64-
65-**Improved image downloading and reporting.**
66- MAAS boot images are now downloaded centrally by the region controller
67- and disseminated to all registered cluster controllers. This change includes
68- a new web UI under the `Images` tab that allows the admin to select
69- which images to import and shows the progress of the ongoing download.
70- This completely replaces any file-based configuration that used to take
71- place on cluster controllers. The cluster page now shows whether it has
72- synchronised all the images from the region controller.
73-
74- This process is also completely controllable using the API.
75-
76-.. Note::
77- Unfortunately due to a format change in the way images are stored, it
78- was not possible to migrate previously downloaded images to the new region
79- storage. The cluster(s) will still be able to use the existing images,
80- however the region controller will be unaware of them until an import
81- is initiated. When the import is finished, the cluster(s) will remove
82- older image resources.
83-
84- This means that the first thing to do after upgrading to 1.7 is go to the
85- `Images` tab and re-import the images.
86-
87-**Increased robustness.**
88- A large amount of effort has been given to ensuring that MAAS remains
89- robust in the face of adversity. An updated node state model has been
90- implemented that takes into account more of the situations in which a
91- node can be found including any failures at each stage.
92-
93- When a node is getting deployed, it is now monitored to check that each
94- stage is reached in a timely fashion; if it does not then it is marked
95- as failed.
96-
97- The core power driver was updated to check the state of the power on each
98- node and is reported in the web UI and API. The core driver now also
99- handles retries when changing the power state of hardware, removing the
100- requirement that each power template handle it individually.
101-
102-**RPC security.**
103- As a step towards mutually verified TLS connections between MAAS's
104- components, 1.7 introduces a simple shared-secret mechanism to
105- authenticate the region with the clusters and vice-versa. For those
106- clusters that run on the same machine as the region controller (which
107- will account for most people), everything will continue to work
108- without intervention. However, if you're running a cluster on a
109- separate machine, you must install the secret:
110-
111- 1. After upgrading the region controller, view /var/lib/maas/secret
112- (it's text) and copy it.
113-
114- 2. On each cluster, run:
115-
116- sudo -u maas maas-provision install-shared-secret
117-
118- You'll be prompted for the secret; paste it in and press enter. It
119- is a password prompt, so the secret will not be echoed back to you.
120-
121- That's it; the upgraded cluster controller will find the secret
122- without needing to be told.
123-
124-**RPC connections.**
125- Each cluster maintains a persistent connection to each region
126- controller process that's running. The ports on which the region is
127- listening are all high-numbered, and they are allocated randomly by
128- the OS. In a future release of MAAS we will narrow this down. For now,
129- each cluster controller needs unfiltered access to each machine in the
130- region on all high-numbered TCP ports.
131-
132-**Node event log.**
133- For every major event on nodes, it is now logged in a node-specific log.
134- This includes events such as power changes, deployments and any failures.
135-
136-**IPv6.**
137- It is now possible to deploy Ubuntu nodes that have IPv6 enabled.
138- See :doc:`ipv6` for more details.
139-
140-**Removal of Celery and RabbitMQ.**
141- While Celery was found to be very reliable it ultimately did not suit
142- the project's requirements as it is a largely fire-and-forget mechanism.
143- Additionally it was another moving part that caused some headaches for
144- users and admins alike, so the decision was taken to remove it and implement
145- a custom communications mechanism between the region controller and cluster
146- controllers. The new mechanism is bidirectional and allowed the complex
147- interactions to take place that are required as part of the robustness
148- improvements.
149-
150- Since a constant connection is maintained, as a side effect the web UI now
151- shows whether each cluster is connected or not.
152-
153-**Support for other OSes.**
154- Non-Ubuntu OSes are fully supported now. This includes:
155- - Windows
156- - Centos
157- - SuSE
158-
159-**Custom Images.**
160- MAAS now supports the deployment of Custom Images. Custom images can be
161- uploaded via the API. The usage of custom images allows the deployment of
162- other Ubuntu Flavors, such as Ubuntu Desktop.
163-
164-**maas-proxy.**
165- MAAS now uses maas-proxy as the default proxy solution instead of
166- squid-deb-proxy. On a fresh install, MAAS will use maas-proxy by default.
167- On upgrades from previous releases, MAAS will install maas-proxy instead of
168- squid-deb-proxy.
169-
170-Minor notable changes
171----------------------
172-
173-**Better handling of networks.**
174- All networks referred to by cluster interfaces are now automatically
175- registered on the Network page. Any node network interfaces are
176- automatically linked to the relevant Network.
177-
178-.. Note::
179- Commissioning currently requires an IP address to be available for each
180- network interface on a network that MAAS manages; this allows MAAS to
181- auto-populate its networks database. In general you should use a
182- well-sized network (/16 recommended if you will be using containers and
183- VMs) and dynamic pool. If this feature risks causing IP exhaustion for
184- your deployment and you do not need the auto-populate functionality, you
185- can disable it by running the following command on your region controller::
186-
187- sudo maas <profile> maas set-config name=enable_dhcp_discovery_on_unconfigured_interfaces value=False
188-
189-**Improved logging.**
190- A total overhaul of where logging is produced was undertaken, and now
191- all the main events in MAAS are selectively reported to syslog with the
192- "maas" prefix from both the region and cluster controllers alike. If MAAS
193- is installed using the standard Ubuntu packaging, its syslog entries are
194- redirected to /var/log/maas/maas.log.
195-
196- On the clusters, pserv.log is now less chatty and contains only errors.
197- On the region controller appservers, maas-django.log contains only appserver
198- errors.
199-
200-**Static IP selection.**
201- The API was extended so that specific IPs can be pre-allocated for network
202- interfaces on nodes and for user-allocated IPs.
203-
204-**Pronounceable random hostnames.**
205- The old auto-generated 5-letter names were replaced with a pseudo-random
206- name that is produced from a dictionary giving names of the form
207- 'adjective-noun'.
208-
209-
210-Known Problems & Workarounds
211-----------------------------
212-
213-**Upgrade issues**
214- There may be upgrade issues for users currently on MAAS 1.5 and 1.6; while we
215- have attempted to reproduce and address all the issues reported, some bugs
216- remain inconclusive. We recommend a full, tested backup of the MAAS servers
217- before attempting the upgrade to 1.7. If you do encounter issues, please file
218- these and flag them to the attention of the MAAS team and we will address them
219- in point-releases. See bugs `1381058`_, `1382266`_, `1379890`_, `1379532`_,
220- and `1379144`_.
221-
222-.. _1381058:
223- https://launchpad.net/bugs/1381058
224-.. _1382266:
225- https://launchpad.net/bugs/1382266
226-.. _1379890:
227- https://launchpad.net/bugs/1379890
228-.. _1379532:
229- https://launchpad.net/bugs/1379532
230-.. _1379144:
231- https://launchpad.net/bugs/1379144
232-
233-**Split Region/Cluster set-ups**
234- If you site your cluster on a separate host to the region, it needs a
235- security key to be manually installed by running
236- ``maas-provision install-shared-secret`` on the cluster host.
237-
238-**Private boot streams**
239- If you had private boot image stream information configured in MAAS 1.5 or
240- 1.6, upgrading to 1.7 will not take that into account and it will need to be
241- manually entered on the settings page in the MAAS UI (bug `1379890`_)
242-
243-.. _1379890:
244- https://launchpad.net/bugs/1379890
245-
246-**Concurrency issues**
247- Concurrency issues expose us to races when simultaneous operations are
248- triggered. This is the source of many hard to reproduce issues which will
249- require us to change the default database isolation level. We intend to address
250- this in the first point release of 1.7.
251-
252-**Destroying a Juju environment**
253- When attempting to "juju destroy" an environment, nodes must be in the DEPLOYED
254- state; otherwise, the destroy will fail. You should wait for all in-progress
255- actions on the MAAS cluster to conclude before issuing the command. (bug
256- `1381619`_)
257-
258-.. _1381619:
259- https://launchpad.net/bugs/_1381619
260-
261-**AMT power control**
262- A few AMT-related issues remain, with workarounds:
263-
264- * Commissioning NUC reboots instead of shutting down (bug `1368685`_). There
265- is `a workaround in the power template`_
266-
267- * MAAS (amttool) cannot control AMT version > 8. See `workaround described in
268- bug 1331214`_
269-
270- * AMT NUC stuck at boot prompt instead of powering down (no ACPI support in
271- syslinux poweroff) (bug `1376716`_). See the `ACPI-only workaround`_
272-
273-.. _1368685:
274- https://bugs.launchpad.net/maas/+bug/1368685
275-.. _a workaround in the power template:
276- https://bugs.launchpad.net/maas/+bug/1368685/comments/8
277-.. _workaround described in bug 1331214:
278- https://bugs.launchpad.net/maas/+bug/1331214/comments/18
279-.. _1376716:
280- https://bugs.launchpad.net/maas/+bug/1376716
281-.. _ACPI-only workaround:
282- https://bugs.launchpad.net/maas/+bug/1376716/comments/12
283-
284-
285-**Disk wiping**
286- If you enable disk wiping, juju destroy-environment may fail for you. The
287- current workaround is to wait and re-issue the command. This will be fixed in
288- future versions of MAAS & Juju. (bug `1386327`_)
289-
290-.. _1386327:
291- https://bugs.launchpad.net/maas/+bug/1386327
292-
293-**BIND with DNSSEC**
294- If you are using BIND with a forwarder that uses DNSSEC and have not
295- configured certificates, you will need to explicitly disable that feature in
296- your BIND configuration (1384334)
297-
298-.. _1384334:
299- https://bugs.launchpad.net/maas/+bug/1384334
300-
301-**Boot source selections on the API**
302- Use of API to change image selections can leave DB in a bad state
303- (bug `1376812`_). It can be fixed by issuing direct database updates.
304-
305-.. _1376812:
306- https://bugs.launchpad.net/maas/+bug/1376812
307-
308-**Disabling DNS**
309- Disabling DNS may not work (bug `1383768`_)
310-
311-.. _1383768:
312- https://bugs.launchpad.net/maas/+bug/1383768
313-
314-**Stale DNS zone files**
315- Stale DNS zone files may be left behind if the MAAS domainname is changed
316- (bug `1383329`_)
317-
318-.. _1383329:
319- https://bugs.launchpad.net/maas/+bug/1383329
320-
321-
322-
323-Major bugs fixed in this release
324---------------------------------
325-
326-See https://launchpad.net/maas/+milestone/1.7.0 for full details.
327-
328-#1081660 If maas-enlist fails to reach a DNS server, the node will be named ";; connection timed out; no servers could be reached"
329-
330-#1087183 MaaS cloud-init configuration specifies 'manage_etc_hosts: localhost'
331-
332-#1328351 ConstipationError: When the cluster runs the "import boot images" task it blocks other tasks
333-
334-#1342117 CLI command to set up node-group-interface fails with /usr/lib/python2.7/dist-packages/maascli/__main__.py: error: u'name'
335-
336-#1349254 Duplicate FQDN can be configured on MAAS via CLI or API
337-
338-#1352575 BMC password showing in the apache2 logs
339-
340-#1355534 UnknownPowerType traceback in appserver log
341-
342-#1363850 Auto-enlistment not reporting power parameters
343-
344-#1363900 Dev server errors while trying to write to '/var/lib/maas'
345-
346-#1363999 Not assigning static IP addresses
347-
348-#1364481 http 500 error doesn't contain a stack trace
349-
350-#1364993 500 error when trying to acquire a commissioned node (AddrFormatError: failed to detect a valid IP address from None)
351-
352-#1365130 django-admin prints spurious messages to stdout, breaking scripts
353-
354-#1365850 DHCP scan using cluster interface name as network interface?
355-
356-#1366172 NUC does not boot after power off/power on
357-
358-#1366212 Large dhcp leases file leads to tftp timeouts
359-
360-#1366652 Leaking temporary directories
361-
362-#1368269 internal server error when deleting a node
363-
364-#1368590 Power actions are not serialized.
365-
366-#1370534 Recurrent update of the power state of nodes crashes if the connection to the BMC fails.
367-
368-#1370958 excessive pserv logging
369-
370-#1372767 Twisted web client does not support IPv6 address
371-
372-#1372944 Twisted web client fails looking up IPv6 address hostname
373-
374-#1373031 Cannot register cluster
375-
376-#1373103 compose_curtin_network_preseed breaks installation of all other operating systems
377-
378-#1373368 Conflicting power actions being dropped on the floor can result in leaving a node in an inconsistent state
379-
380-#1373699 Cluster Listing Page lacks feedback about the images each cluster has
381-
382-#1374102 No retries for AMT power?
383-
384-#1375980 Nodes failed to transition out of "New" state on bulk commission
385-
386-#1376023 After performing bulk action on maas nodes, Internal Server Error
387-
388-#1376888 Nodes can't be deleted if DHCP management is off.
389-
390-#1377099 Bulk operation leaves nodes in inconsistent state
391-
392-#1379209 When a node has multiple interfaces on a network MAAS manages, MAAS assigns static IP addresses to all of them
393-
394-#1379744 Cluster registration is fragile and insecure
395-
396-#1380932 MAAS does not cope with changes of the dhcp daemons
397-
398-#1381605 Not all the DNS records are being added when deploying multiple nodes
399-
400-#1012954 If a power script fails, there is no UI feedback
401-
402-#1186196 "Starting a node" has different meanings in the UI and in the API.
403-
404-#1237215 maas and curtin do not indicate failure reasonably
405-
406-#1273222 MAAS doesn't check return values of power actions
407-
408-#1288502 archive and proxy settings not honoured for commissioning
409-
410-#1316919 Checks don't exist to confirm a node will actually boot
411-
412-#1321885 IPMI detection and automatic setting fail in ubuntu 14.04 maas
413-
414-#1325610 node marked "Ready" before poweroff complete
415-
416-#1340188 unallocated node started manually, causes AssertionError for purpose poweroff
417-
418-#1341118 No feedback when IPMI credentials fail
419-
420-#1341121 No feedback to user when cluster is not running
421-
422-#1341581 power state is not represented in api and ui
423-
424-#1341800 MAAS doesn't support soft power off through the API
425-
426-#1344177 hostnames can't be changed while a node is acquired
427-
428-#1347518 Confusing error message when API key is wrong
429-
430-#1349496 Unable to request a specific static IP on the API
431-
432-#1349736 MAAS logging is too verbose and not very useful
433-
434-#1349917 guess_server_address() can return IPAddress or hostname
435-
436-#1350103 No support for armhf/keystone architecture
437-
438-#1350856 Can't constrain acquisition of nodes by not having a tag
439-
440-#1356880 MAAS shouldn't allow changing the hostname of a deployed node
441-
442-#1357714 Virsh power driver does not seem to work at all
443-
444-#1358859 Commissioning output xml is hard to understand, would be nice to have yaml as an output option.
445-
446-#1359169 MAAS should handle invalid consumers gracefully
447-
448-#1359822 Gateway is missing in network definition
449-
450-#1363913 Impossible to remove last MAC from network in UI
451-
452-#1364228 Help text for node hostname is wrong
453-
454-#1364591 MAAS Archive Mirror does not respect non-default port
455-
456-#1365616 Non-admin access to cluster controller config
457-
458-#1365619 DNS should be an optional field in the network definition
459-
460-#1365776 commissioning results view for a node also shows installation results
461-
462-#1366812 Old boot resources are not being removed on clusters
463-
464-#1367455 MAC address for node's IPMI is reversed looked up to yield IP address using case sensitive comparison
465-
466-#1373580 [SRU] Glen m700 cartridge list as ARM64/generic after enlist
467-
468-#1373723 Releasing a node without power parameters ends up in not being able to release a node
469-
470-#1233158 no way to get power parameters in api
471-
472-#1319854 `maas login` tells you you're logged in successfully when you're not
473-
474-#1368480 Need API to gather image metadata across all of MAAS
475-
476-#1281406 Disk/memory space on Node edit page have no units
477-
478-#1299231 MAAS DHCP/DNS can't manage more than a /16 network
479-
480-#1357381 maas-region-admin createadmin shows error if not params given
481-
482-#1376393 powerkvm boot loader installs even when not needed
483-
484-#1287224 MAAS random generated hostnames are not pronounceable
485-
486-#1348364 non-maas managed subnets cannot query maas DNS
487-
488-
489+<<<<<<< TREE
490+1.7.0
491+=====
492+
493+Important announcements
494+-----------------------
495+
496+**Re-import your boot images**
497+ You must re-import your boot images, see below for details.
498+
499+**Update Curtin preseed files**
500+ Two changes were made to Curtin preseed files that need your attention
501+ if you made any customisations:
502+
503+ * The OS name must now appear in the filename. The new schema is shown
504+ here, each file pattern is tried in turn until a match is found::
505+
506+ {prefix}_{osystem}_{node_arch}_{node_subarch}_{release}_{node_name}
507+ {prefix}_{osystem}_{node_arch}_{node_subarch}_{release}
508+ {prefix}_{osystem}_{node_arch}_{node_subarch}
509+ {prefix}_{osystem}_{node_arch}
510+ {prefix}_{osystem}
511+ {prefix}
512+
513+ * If you are modifying ``/etc/network/interfaces`` in the preseed, it must be
514+ moved so it is processed last in ``late_commands`` since MAAS now writes
515+ to this file itself as part of IPv6 setup. For example::
516+
517+ late_commands:
518+ bonding_02: ["curtin", "in-target", "--", "wget", "-O", "/etc/network/interfaces", "http://[...snip...]"]
519+
520+ must now look like this::
521+
522+ late_commands:
523+ zz_write_ifaces: ["curtin", "in-target", "--", "wget", "-O", "/etc/network/interfaces", "http://[...snip...]"]
524+
525+ The leading ``zz`` ensures the command sorts to the end of the
526+ ``late_commands`` list.
527+
528+
529+Major new features
530+------------------
531+
532+**Improved image downloading and reporting.**
533+ MAAS boot images are now downloaded centrally by the region controller
534+ and disseminated to all registered cluster controllers. This change includes
535+ a new web UI under the `Images` tab that allows the admin to select
536+ which images to import and shows the progress of the ongoing download.
537+ This completely replaces any file-based configuration that used to take
538+ place on cluster controllers. The cluster page now shows whether it has
539+ synchronised all the images from the region controller.
540+
541+ This process is also completely controllable using the API.
542+
543+.. Note::
544+ Unfortunately due to a format change in the way images are stored, it
545+ was not possible to migrate previously downloaded images to the new region
546+ storage. The cluster(s) will still be able to use the existing images,
547+ however the region controller will be unaware of them until an import
548+ is initiated. When the import is finished, the cluster(s) will remove
549+ older image resources.
550+
551+ This means that the first thing to do after upgrading to 1.7 is go to the
552+ `Images` tab and re-import the images.
553+
554+**Increased robustness.**
555+ A large amount of effort has been given to ensuring that MAAS remains
556+ robust in the face of adversity. An updated node state model has been
557+ implemented that takes into account more of the situations in which a
558+ node can be found including any failures at each stage.
559+
560+ When a node is getting deployed, it is now monitored to check that each
561+ stage is reached in a timely fashion; if it does not then it is marked
562+ as failed.
563+
564+ The core power driver was updated to check the state of the power on each
565+ node and is reported in the web UI and API. The core driver now also
566+ handles retries when changing the power state of hardware, removing the
567+ requirement that each power template handle it individually.
568+
569+**RPC security.**
570+ As a step towards mutually verified TLS connections between MAAS's
571+ components, 1.7 introduces a simple shared-secret mechanism to
572+ authenticate the region with the clusters and vice-versa. For those
573+ clusters that run on the same machine as the region controller (which
574+ will account for most people), everything will continue to work
575+ without intervention. However, if you're running a cluster on a
576+ separate machine, you must install the secret:
577+
578+ 1. After upgrading the region controller, view /var/lib/maas/secret
579+ (it's text) and copy it.
580+
581+ 2. On each cluster, run:
582+
583+ sudo -u maas maas-provision install-shared-secret
584+
585+ You'll be prompted for the secret; paste it in and press enter. It
586+ is a password prompt, so the secret will not be echoed back to you.
587+
588+ That's it; the upgraded cluster controller will find the secret
589+ without needing to be told.
590+
591+**RPC connections.**
592+ Each cluster maintains a persistent connection to each region
593+ controller process that's running. The ports on which the region is
594+ listening are all high-numbered, and they are allocated randomly by
595+ the OS. In a future release of MAAS we will narrow this down. For now,
596+ each cluster controller needs unfiltered access to each machine in the
597+ region on all high-numbered TCP ports.
598+
599+**Node event log.**
600+ For every major event on nodes, it is now logged in a node-specific log.
601+ This includes events such as power changes, deployments and any failures.
602+
603+**IPv6.**
604+ It is now possible to deploy Ubuntu nodes that have IPv6 enabled.
605+ See :doc:`ipv6` for more details.
606+
607+**Removal of Celery and RabbitMQ.**
608+ While Celery was found to be very reliable it ultimately did not suit
609+ the project's requirements as it is a largely fire-and-forget mechanism.
610+ Additionally it was another moving part that caused some headaches for
611+ users and admins alike, so the decision was taken to remove it and implement
612+ a custom communications mechanism between the region controller and cluster
613+ controllers. The new mechanism is bidirectional and allowed the complex
614+ interactions to take place that are required as part of the robustness
615+ improvements.
616+
617+ Since a constant connection is maintained, as a side effect the web UI now
618+ shows whether each cluster is connected or not.
619+
620+**Support for other OSes.**
621+ Non-Ubuntu OSes are fully supported now. This includes:
622+ - Windows
623+ - Centos
624+ - SuSE
625+
626+**Custom Images.**
627+ MAAS now supports the deployment of Custom Images. Custom images can be
628+ uploaded via the API. The usage of custom images allows the deployment of
629+ other Ubuntu Flavors, such as Ubuntu Desktop.
630+
631+**maas-proxy.**
632+ MAAS now uses maas-proxy as the default proxy solution instead of
633+ squid-deb-proxy. On a fresh install, MAAS will use maas-proxy by default.
634+ On upgrades from previous releases, MAAS will install maas-proxy instead of
635+ squid-deb-proxy.
636+
637+Minor notable changes
638+---------------------
639+
640+**Better handling of networks.**
641+ All networks referred to by cluster interfaces are now automatically
642+ registered on the Network page. Any node network interfaces are
643+ automatically linked to the relevant Network.
644+
645+.. Note::
646+ Commissioning currently requires an IP address to be available for each
647+ network interface on a network that MAAS manages; this allows MAAS to
648+ auto-populate its networks database. In general you should use a
649+ well-sized network (/16 recommended if you will be using containers and
650+ VMs) and dynamic pool. If this feature risks causing IP exhaustion for
651+ your deployment and you do not need the auto-populate functionality, you
652+ can disable it by running the following command on your region controller::
653+
654+ sudo maas <profile> maas set-config name=enable_dhcp_discovery_on_unconfigured_interfaces value=False
655+
656+**Improved logging.**
657+ A total overhaul of where logging is produced was undertaken, and now
658+ all the main events in MAAS are selectively reported to syslog with the
659+ "maas" prefix from both the region and cluster controllers alike. If MAAS
660+ is installed using the standard Ubuntu packaging, its syslog entries are
661+ redirected to /var/log/maas/maas.log.
662+
663+ On the clusters, pserv.log is now less chatty and contains only errors.
664+ On the region controller appservers, maas-django.log contains only appserver
665+ errors.
666+
667+**Static IP selection.**
668+ The API was extended so that specific IPs can be pre-allocated for network
669+ interfaces on nodes and for user-allocated IPs.
670+
671+**Pronounceable random hostnames.**
672+ The old auto-generated 5-letter names were replaced with a pseudo-random
673+ name that is produced from a dictionary giving names of the form
674+ 'adjective-noun'.
675+
676+
677+Known Problems & Workarounds
678+----------------------------
679+
680+**Upgrade issues**
681+ There may be upgrade issues for users currently on MAAS 1.5 and 1.6; while we
682+ have attempted to reproduce and address all the issues reported, some bugs
683+ remain inconclusive. We recommend a full, tested backup of the MAAS servers
684+ before attempting the upgrade to 1.7. If you do encounter issues, please file
685+ these and flag them to the attention of the MAAS team and we will address them
686+ in point-releases. See bugs `1381058`_, `1382266`_, `1379890`_, `1379532`_,
687+ and `1379144`_.
688+
689+.. _1381058:
690+ https://launchpad.net/bugs/1381058
691+.. _1382266:
692+ https://launchpad.net/bugs/1382266
693+.. _1379890:
694+ https://launchpad.net/bugs/1379890
695+.. _1379532:
696+ https://launchpad.net/bugs/1379532
697+.. _1379144:
698+ https://launchpad.net/bugs/1379144
699+
700+**Split Region/Cluster set-ups**
701+ If you site your cluster on a separate host to the region, it needs a
702+ security key to be manually installed by running
703+ ``maas-provision install-shared-secret`` on the cluster host.
704+
705+**Private boot streams**
706+ If you had private boot image stream information configured in MAAS 1.5 or
707+ 1.6, upgrading to 1.7 will not take that into account and it will need to be
708+ manually entered on the settings page in the MAAS UI (bug `1379890`_)
709+
710+.. _1379890:
711+ https://launchpad.net/bugs/1379890
712+
713+**Concurrency issues**
714+ Concurrency issues expose us to races when simultaneous operations are
715+ triggered. This is the source of many hard to reproduce issues which will
716+ require us to change the default database isolation level. We intend to address
717+ this in the first point release of 1.7.
718+
719+**Destroying a Juju environment**
720+ When attempting to "juju destroy" an environment, nodes must be in the DEPLOYED
721+ state; otherwise, the destroy will fail. You should wait for all in-progress
722+ actions on the MAAS cluster to conclude before issuing the command. (bug
723+ `1381619`_)
724+
725+.. _1381619:
726+ https://launchpad.net/bugs/_1381619
727+
728+**AMT power control**
729+ A few AMT-related issues remain, with workarounds:
730+
731+ * Commissioning NUC reboots instead of shutting down (bug `1368685`_). There
732+ is `a workaround in the power template`_
733+
734+ * MAAS (amttool) cannot control AMT version > 8. See `workaround described in
735+ bug 1331214`_
736+
737+ * AMT NUC stuck at boot prompt instead of powering down (no ACPI support in
738+ syslinux poweroff) (bug `1376716`_). See the `ACPI-only workaround`_
739+
740+.. _1368685:
741+ https://bugs.launchpad.net/maas/+bug/1368685
742+.. _a workaround in the power template:
743+ https://bugs.launchpad.net/maas/+bug/1368685/comments/8
744+.. _workaround described in bug 1331214:
745+ https://bugs.launchpad.net/maas/+bug/1331214/comments/18
746+.. _1376716:
747+ https://bugs.launchpad.net/maas/+bug/1376716
748+.. _ACPI-only workaround:
749+ https://bugs.launchpad.net/maas/+bug/1376716/comments/12
750+
751+
752+**Disk wiping**
753+ If you enable disk wiping, juju destroy-environment may fail for you. The
754+ current workaround is to wait and re-issue the command. This will be fixed in
755+ future versions of MAAS & Juju. (bug `1386327`_)
756+
757+.. _1386327:
758+ https://bugs.launchpad.net/maas/+bug/1386327
759+
760+**BIND with DNSSEC**
761+ If you are using BIND with a forwarder that uses DNSSEC and have not
762+ configured certificates, you will need to explicitly disable that feature in
763+ your BIND configuration (1384334)
764+
765+.. _1384334:
766+ https://bugs.launchpad.net/maas/+bug/1384334
767+
768+**Boot source selections on the API**
769+ Use of API to change image selections can leave DB in a bad state
770+ (bug `1376812`_). It can be fixed by issuing direct database updates.
771+
772+.. _1376812:
773+ https://bugs.launchpad.net/maas/+bug/1376812
774+
775+**Disabling DNS**
776+ Disabling DNS may not work (bug `1383768`_)
777+
778+.. _1383768:
779+ https://bugs.launchpad.net/maas/+bug/1383768
780+
781+**Stale DNS zone files**
782+ Stale DNS zone files may be left behind if the MAAS domainname is changed
783+ (bug `1383329`_)
784+
785+.. _1383329:
786+ https://bugs.launchpad.net/maas/+bug/1383329
787+
788+
789+
790+Major bugs fixed in this release
791+--------------------------------
792+
793+See https://launchpad.net/maas/+milestone/1.7.0 for full details.
794+
795+#1081660 If maas-enlist fails to reach a DNS server, the node will be named ";; connection timed out; no servers could be reached"
796+
797+#1087183 MaaS cloud-init configuration specifies 'manage_etc_hosts: localhost'
798+
799+#1328351 ConstipationError: When the cluster runs the "import boot images" task it blocks other tasks
800+
801+#1342117 CLI command to set up node-group-interface fails with /usr/lib/python2.7/dist-packages/maascli/__main__.py: error: u'name'
802+
803+#1349254 Duplicate FQDN can be configured on MAAS via CLI or API
804+
805+#1352575 BMC password showing in the apache2 logs
806+
807+#1355534 UnknownPowerType traceback in appserver log
808+
809+#1363850 Auto-enlistment not reporting power parameters
810+
811+#1363900 Dev server errors while trying to write to '/var/lib/maas'
812+
813+#1363999 Not assigning static IP addresses
814+
815+#1364481 http 500 error doesn't contain a stack trace
816+
817+#1364993 500 error when trying to acquire a commissioned node (AddrFormatError: failed to detect a valid IP address from None)
818+
819+#1365130 django-admin prints spurious messages to stdout, breaking scripts
820+
821+#1365850 DHCP scan using cluster interface name as network interface?
822+
823+#1366172 NUC does not boot after power off/power on
824+
825+#1366212 Large dhcp leases file leads to tftp timeouts
826+
827+#1366652 Leaking temporary directories
828+
829+#1368269 internal server error when deleting a node
830+
831+#1368590 Power actions are not serialized.
832+
833+#1370534 Recurrent update of the power state of nodes crashes if the connection to the BMC fails.
834+
835+#1370958 excessive pserv logging
836+
837+#1372767 Twisted web client does not support IPv6 address
838+
839+#1372944 Twisted web client fails looking up IPv6 address hostname
840+
841+#1373031 Cannot register cluster
842+
843+#1373103 compose_curtin_network_preseed breaks installation of all other operating systems
844+
845+#1373368 Conflicting power actions being dropped on the floor can result in leaving a node in an inconsistent state
846+
847+#1373699 Cluster Listing Page lacks feedback about the images each cluster has
848+
849+#1374102 No retries for AMT power?
850+
851+#1375980 Nodes failed to transition out of "New" state on bulk commission
852+
853+#1376023 After performing bulk action on maas nodes, Internal Server Error
854+
855+#1376888 Nodes can't be deleted if DHCP management is off.
856+
857+#1377099 Bulk operation leaves nodes in inconsistent state
858+
859+#1379209 When a node has multiple interfaces on a network MAAS manages, MAAS assigns static IP addresses to all of them
860+
861+#1379744 Cluster registration is fragile and insecure
862+
863+#1380932 MAAS does not cope with changes of the dhcp daemons
864+
865+#1381605 Not all the DNS records are being added when deploying multiple nodes
866+
867+#1012954 If a power script fails, there is no UI feedback
868+
869+#1186196 "Starting a node" has different meanings in the UI and in the API.
870+
871+#1237215 maas and curtin do not indicate failure reasonably
872+
873+#1273222 MAAS doesn't check return values of power actions
874+
875+#1288502 archive and proxy settings not honoured for commissioning
876+
877+#1316919 Checks don't exist to confirm a node will actually boot
878+
879+#1321885 IPMI detection and automatic setting fail in ubuntu 14.04 maas
880+
881+#1325610 node marked "Ready" before poweroff complete
882+
883+#1340188 unallocated node started manually, causes AssertionError for purpose poweroff
884+
885+#1341118 No feedback when IPMI credentials fail
886+
887+#1341121 No feedback to user when cluster is not running
888+
889+#1341581 power state is not represented in api and ui
890+
891+#1341800 MAAS doesn't support soft power off through the API
892+
893+#1344177 hostnames can't be changed while a node is acquired
894+
895+#1347518 Confusing error message when API key is wrong
896+
897+#1349496 Unable to request a specific static IP on the API
898+
899+#1349736 MAAS logging is too verbose and not very useful
900+
901+#1349917 guess_server_address() can return IPAddress or hostname
902+
903+#1350103 No support for armhf/keystone architecture
904+
905+#1350856 Can't constrain acquisition of nodes by not having a tag
906+
907+#1356880 MAAS shouldn't allow changing the hostname of a deployed node
908+
909+#1357714 Virsh power driver does not seem to work at all
910+
911+#1358859 Commissioning output xml is hard to understand, would be nice to have yaml as an output option.
912+
913+#1359169 MAAS should handle invalid consumers gracefully
914+
915+#1359822 Gateway is missing in network definition
916+
917+#1363913 Impossible to remove last MAC from network in UI
918+
919+#1364228 Help text for node hostname is wrong
920+
921+#1364591 MAAS Archive Mirror does not respect non-default port
922+
923+#1365616 Non-admin access to cluster controller config
924+
925+#1365619 DNS should be an optional field in the network definition
926+
927+#1365776 commissioning results view for a node also shows installation results
928+
929+#1366812 Old boot resources are not being removed on clusters
930+
931+#1367455 MAC address for node's IPMI is reversed looked up to yield IP address using case sensitive comparison
932+
933+#1373580 [SRU] Glen m700 cartridge list as ARM64/generic after enlist
934+
935+#1373723 Releasing a node without power parameters ends up in not being able to release a node
936+
937+#1233158 no way to get power parameters in api
938+
939+#1319854 `maas login` tells you you're logged in successfully when you're not
940+
941+#1368480 Need API to gather image metadata across all of MAAS
942+
943+#1281406 Disk/memory space on Node edit page have no units
944+
945+#1299231 MAAS DHCP/DNS can't manage more than a /16 network
946+
947+#1357381 maas-region-admin createadmin shows error if not params given
948+
949+#1376393 powerkvm boot loader installs even when not needed
950+
951+#1287224 MAAS random generated hostnames are not pronounceable
952+
953+#1348364 non-maas managed subnets cannot query maas DNS
954+
955+
956+=======
957+1.7.0
958+=====
959+
960+Important announcements
961+-----------------------
962+
963+**Re-import your boot images**
964+ You must re-import your boot images, see below for details.
965+
966+**Update Curtin preseed files**
967+ Two changes were made to Curtin preseed files that need your attention
968+ if you made any customisations:
969+
970+ * The OS name must now appear in the filename. The new schema is shown
971+ here, each file pattern is tried in turn until a match is found::
972+
973+ {prefix}_{osystem}_{node_arch}_{node_subarch}_{release}_{node_name}
974+ {prefix}_{osystem}_{node_arch}_{node_subarch}_{release}
975+ {prefix}_{osystem}_{node_arch}_{node_subarch}
976+ {prefix}_{osystem}_{node_arch}
977+ {prefix}_{osystem}
978+ {prefix}
979+
980+ * If you are modifying ``/etc/network/interfaces`` in the preseed, it must be
981+ moved so it is processed last in ``late_commands`` since MAAS now writes
982+ to this file itself as part of IPv6 setup. For example::
983+
984+ late_commands:
985+ bonding_02: ["curtin", "in-target", "--", "wget", "-O", "/etc/network/interfaces", "http://[...snip...]"]
986+
987+ must now look like this::
988+
989+ late_commands:
990+ zz_write_ifaces: ["curtin", "in-target", "--", "wget", "-O", "/etc/network/interfaces", "http://[...snip...]"]
991+
992+ The leading ``zz`` ensures the command sorts to the end of the
993+ ``late_commands`` list.
994+
995+
996+Major new features
997+------------------
998+
999+**Improved image downloading and reporting.**
1000+ MAAS boot images are now downloaded centrally by the region controller
1001+ and disseminated to all registered cluster controllers. This change includes
1002+ a new web UI under the `Images` tab that allows the admin to select
1003+ which images to import and shows the progress of the ongoing download.
1004+ This completely replaces any file-based configuration that used to take
1005+ place on cluster controllers. The cluster page now shows whether it has
1006+ synchronised all the images from the region controller.
1007+
1008+ This process is also completely controllable using the API.
1009+
1010+.. Note::
1011+ Unfortunately due to a format change in the way images are stored, it
1012+ was not possible to migrate previously downloaded images to the new region
1013+ storage. The cluster(s) will still be able to use the existing images,
1014+ however the region controller will be unaware of them until an import
1015+ is initiated. When the import is finished, the cluster(s) will remove
1016+ older image resources.
1017+
1018+ This means that the first thing to do after upgrading to 1.7 is go to the
1019+ `Images` tab and re-import the images.
1020+
1021+**Increased robustness.**
1022+ A large amount of effort has been given to ensuring that MAAS remains
1023+ robust in the face of adversity. An updated node state model has been
1024+ implemented that takes into account more of the situations in which a
1025+ node can be found including any failures at each stage.
1026+
1027+ When a node is getting deployed, it is now monitored to check that each
1028+ stage is reached in a timely fashion; if it does not then it is marked
1029+ as failed.
1030+
1031+ The core power driver was updated to check the state of the power on each
1032+ node and is reported in the web UI and API. The core driver now also
1033+ handles retries when changing the power state of hardware, removing the
1034+ requirement that each power template handle it individually.
1035+
1036+**RPC security.**
1037+ As a step towards mutually verified TLS connections between MAAS's
1038+ components, 1.7 introduces a simple shared-secret mechanism to
1039+ authenticate the region with the clusters and vice-versa. For those
1040+ clusters that run on the same machine as the region controller (which
1041+ will account for most people), everything will continue to work
1042+ without intervention. However, if you're running a cluster on a
1043+ separate machine, you must install the secret:
1044+
1045+ 1. After upgrading the region controller, view /var/lib/maas/secret
1046+ (it's text) and copy it.
1047+
1048+ 2. On each cluster, run:
1049+
1050+ sudo -u maas maas-provision install-shared-secret
1051+
1052+ You'll be prompted for the secret; paste it in and press enter. It
1053+ is a password prompt, so the secret will not be echoed back to you.
1054+
1055+ That's it; the upgraded cluster controller will find the secret
1056+ without needing to be told.
1057+
1058+**RPC connections.**
1059+ Each cluster maintains a persistent connection to each region
1060+ controller process that's running. The ports on which the region is
1061+ listening are all high-numbered, and they are allocated randomly by
1062+ the OS. In a future release of MAAS we will narrow this down. For now,
1063+ each cluster controller needs unfiltered access to each machine in the
1064+ region on all high-numbered TCP ports.
1065+
1066+**Node event log.**
1067+ For every major event on nodes, it is now logged in a node-specific log.
1068+ This includes events such as power changes, deployments and any failures.
1069+
1070+**IPv6.**
1071+ It is now possible to deploy Ubuntu nodes that have IPv6 enabled.
1072+ See :doc:`ipv6` for more details.
1073+
1074+**Removal of Celery and RabbitMQ.**
1075+ While Celery was found to be very reliable it ultimately did not suit
1076+ the project's requirements as it is a largely fire-and-forget mechanism.
1077+ Additionally it was another moving part that caused some headaches for
1078+ users and admins alike, so the decision was taken to remove it and implement
1079+ a custom communications mechanism between the region controller and cluster
1080+ controllers. The new mechanism is bidirectional and allowed the complex
1081+ interactions to take place that are required as part of the robustness
1082+ improvements.
1083+
1084+ Since a constant connection is maintained, as a side effect the web UI now
1085+ shows whether each cluster is connected or not.
1086+
1087+**Support for other OSes.**
1088+ Non-Ubuntu OSes are fully supported now. This includes:
1089+ - Windows
1090+ - Centos
1091+ - SuSE
1092+
1093+**Custom Images.**
1094+ MAAS now supports the deployment of Custom Images. Custom images can be
1095+ uploaded via the API. The usage of custom images allows the deployment of
1096+ other Ubuntu Flavors, such as Ubuntu Desktop.
1097+
1098+**maas-proxy.**
1099+ MAAS now uses maas-proxy as the default proxy solution instead of
1100+ squid-deb-proxy. On a fresh install, MAAS will use maas-proxy by default.
1101+ On upgrades from previous releases, MAAS will install maas-proxy instead of
1102+ squid-deb-proxy.
1103+
1104+Minor notable changes
1105+---------------------
1106+
1107+**Better handling of networks.**
1108+ All networks referred to by cluster interfaces are now automatically
1109+ registered on the Network page. Any node network interfaces are
1110+ automatically linked to the relevant Network.
1111+
1112+.. Note::
1113+ Commissioning currently requires an IP address to be available for each
1114+ network interface on a network that MAAS manages; this allows MAAS to
1115+ auto-populate its networks database. In general you should use a
1116+ well-sized network (/16 recommended if you will be using containers and
1117+ VMs) and dynamic pool. If this feature risks causing IP exhaustion for
1118+ your deployment and you do not need the auto-populate functionality, you
1119+ can disable it by running the following command on your region controller::
1120+
1121+ sudo maas <profile> maas set-config name=enable_dhcp_discovery_on_unconfigured_interfaces value=False
1122+
1123+**Improved logging.**
1124+ A total overhaul of where logging is produced was undertaken, and now
1125+ all the main events in MAAS are selectively reported to syslog with the
1126+ "maas" prefix from both the region and cluster controllers alike. If MAAS
1127+ is installed using the standard Ubuntu packaging, its syslog entries are
1128+ redirected to /var/log/maas/maas.log.
1129+
1130+ On the clusters, pserv.log is now less chatty and contains only errors.
1131+ On the region controller appservers, maas-django.log contains only appserver
1132+ errors.
1133+
1134+**Static IP selection.**
1135+ The API was extended so that specific IPs can be pre-allocated for network
1136+ interfaces on nodes and for user-allocated IPs.
1137+
1138+**Pronounceable random hostnames.**
1139+ The old auto-generated 5-letter names were replaced with a pseudo-random
1140+ name that is produced from a dictionary giving names of the form
1141+ 'adjective-noun'.
1142+
1143+
1144+Known Problems & Workarounds
1145+----------------------------
1146+
1147+**Upgrade issues**
1148+ There may be upgrade issues for users currently on MAAS 1.5 and 1.6; while we
1149+ have attempted to reproduce and address all the issues reported, some bugs
1150+ remain inconclusive. We recommend a full, tested backup of the MAAS servers
1151+ before attempting the upgrade to 1.7. If you do encounter issues, please file
1152+ these and flag them to the attention of the MAAS team and we will address them
1153+ in point-releases. See bugs `1381058`_, `1382266`_, `1379890`_, `1379532`_,
1154+ and `1379144`_.
1155+
1156+.. _1381058:
1157+ https://launchpad.net/bugs/1381058
1158+.. _1382266:
1159+ https://launchpad.net/bugs/1382266
1160+.. _1379890:
1161+ https://launchpad.net/bugs/1379890
1162+.. _1379532:
1163+ https://launchpad.net/bugs/1379532
1164+.. _1379144:
1165+ https://launchpad.net/bugs/1379144
1166+
1167+**Split Region/Cluster set-ups**
1168+ If you site your cluster on a separate host to the region, it needs a
1169+ security key to be manually installed by running
1170+ ``maas-provision install-shared-secret`` on the cluster host.
1171+
1172+**Private boot streams**
1173+ If you had private boot image stream information configured in MAAS 1.5 or
1174+ 1.6, upgrading to 1.7 will not take that into account and it will need to be
1175+ manually entered on the settings page in the MAAS UI (bug `1379890`_)
1176+
1177+.. _1379890:
1178+ https://launchpad.net/bugs/1379890
1179+
1180+**Concurrency issues**
1181+ Concurrency issues expose us to races when simultaneous operations are
1182+ triggered. This is the source of many hard to reproduce issues which will
1183+ require us to change the default database isolation level. We intend to address
1184+ this in the first point release of 1.7.
1185+
1186+**Destroying a Juju environment**
1187+ When attempting to "juju destroy" an environment, nodes must be in the DEPLOYED
1188+ state; otherwise, the destroy will fail. You should wait for all in-progress
1189+ actions on the MAAS cluster to conclude before issuing the command. (bug
1190+ `1381619`_)
1191+
1192+.. _1381619:
1193+ https://launchpad.net/bugs/_1381619
1194+
1195+**AMT power control**
1196+ A few AMT-related issues remain, with workarounds:
1197+
1198+ * Commissioning NUC reboots instead of shutting down (bug `1368685`_). There
1199+ is `a workaround in the power template`_
1200+
1201+ * MAAS (amttool) cannot control AMT version > 8. See `workaround described in
1202+ bug 1331214`_
1203+
1204+ * AMT NUC stuck at boot prompt instead of powering down (no ACPI support in
1205+ syslinux poweroff) (bug `1376716`_). See the `ACPI-only workaround`_
1206+
1207+.. _1368685:
1208+ https://bugs.launchpad.net/maas/+bug/1368685
1209+.. _a workaround in the power template:
1210+ https://bugs.launchpad.net/maas/+bug/1368685/comments/8
1211+.. _workaround described in bug 1331214:
1212+ https://bugs.launchpad.net/maas/+bug/1331214/comments/18
1213+.. _1376716:
1214+ https://bugs.launchpad.net/maas/+bug/1376716
1215+.. _ACPI-only workaround:
1216+ https://bugs.launchpad.net/maas/+bug/1376716/comments/12
1217+
1218+
1219+**Disk wiping**
1220+ If you enable disk wiping, juju destroy-environment may fail for you. The
1221+ current workaround is to wait and re-issue the command. This will be fixed in
1222+ future versions of MAAS & Juju. (bug `1386327`_)
1223+
1224+.. _1386327:
1225+ https://bugs.launchpad.net/maas/+bug/1386327
1226+
1227+**BIND with DNSSEC**
1228+ If you are using BIND with a forwarder that uses DNSSEC and have not
1229+ configured certificates, you will need to explicitly disable that feature in
1230+ your BIND configuration (1384334)
1231+
1232+.. _1384334:
1233+ https://bugs.launchpad.net/maas/+bug/1384334
1234+
1235+**Boot source selections on the API**
1236+ Use of API to change image selections can leave DB in a bad state
1237+ (bug `1376812`_). It can be fixed by issuing direct database updates.
1238+
1239+.. _1376812:
1240+ https://bugs.launchpad.net/maas/+bug/1376812
1241+
1242+**Disabling DNS**
1243+ Disabling DNS may not work (bug `1383768`_)
1244+
1245+.. _1383768:
1246+ https://bugs.launchpad.net/maas/+bug/1383768
1247+
1248+**Stale DNS zone files**
1249+ Stale DNS zone files may be left behind if the MAAS domainname is changed
1250+ (bug `1383329`_)
1251+
1252+.. _1383329:
1253+ https://bugs.launchpad.net/maas/+bug/1383329
1254+
1255+
1256+
1257+Major bugs fixed in this release
1258+--------------------------------
1259+
1260+See https://launchpad.net/maas/+milestone/1.7.0 for full details.
1261+
1262+#1081660 If maas-enlist fails to reach a DNS server, the node will be named ";; connection timed out; no servers could be reached"
1263+
1264+#1087183 MaaS cloud-init configuration specifies 'manage_etc_hosts: localhost'
1265+
1266+#1328351 ConstipationError: When the cluster runs the "import boot images" task it blocks other tasks
1267+
1268+#1342117 CLI command to set up node-group-interface fails with /usr/lib/python2.7/dist-packages/maascli/__main__.py: error: u'name'
1269+
1270+#1349254 Duplicate FQDN can be configured on MAAS via CLI or API
1271+
1272+#1352575 BMC password showing in the apache2 logs
1273+
1274+#1355534 UnknownPowerType traceback in appserver log
1275+
1276+#1363850 Auto-enlistment not reporting power parameters
1277+
1278+#1363900 Dev server errors while trying to write to '/var/lib/maas'
1279+
1280+#1363999 Not assigning static IP addresses
1281+
1282+#1364481 http 500 error doesn't contain a stack trace
1283+
1284+#1364993 500 error when trying to acquire a commissioned node (AddrFormatError: failed to detect a valid IP address from None)
1285+
1286+#1365130 django-admin prints spurious messages to stdout, breaking scripts
1287+
1288+#1365850 DHCP scan using cluster interface name as network interface?
1289+
1290+#1366172 NUC does not boot after power off/power on
1291+
1292+#1366212 Large dhcp leases file leads to tftp timeouts
1293+
1294+#1366652 Leaking temporary directories
1295+
1296+#1368269 internal server error when deleting a node
1297+
1298+#1368590 Power actions are not serialized.
1299+
1300+#1370534 Recurrent update of the power state of nodes crashes if the connection to the BMC fails.
1301+
1302+#1370958 excessive pserv logging
1303+
1304+#1372767 Twisted web client does not support IPv6 address
1305+
1306+#1372944 Twisted web client fails looking up IPv6 address hostname
1307+
1308+#1373031 Cannot register cluster
1309+
1310+#1373103 compose_curtin_network_preseed breaks installation of all other operating systems
1311+
1312+#1373368 Conflicting power actions being dropped on the floor can result in leaving a node in an inconsistent state
1313+
1314+#1373699 Cluster Listing Page lacks feedback about the images each cluster has
1315+
1316+#1374102 No retries for AMT power?
1317+
1318+#1375980 Nodes failed to transition out of "New" state on bulk commission
1319+
1320+#1376023 After performing bulk action on maas nodes, Internal Server Error
1321+
1322+#1376888 Nodes can't be deleted if DHCP management is off.
1323+
1324+#1377099 Bulk operation leaves nodes in inconsistent state
1325+
1326+#1379209 When a node has multiple interfaces on a network MAAS manages, MAAS assigns static IP addresses to all of them
1327+
1328+#1379744 Cluster registration is fragile and insecure
1329+
1330+#1380932 MAAS does not cope with changes of the dhcp daemons
1331+
1332+#1381605 Not all the DNS records are being added when deploying multiple nodes
1333+
1334+#1012954 If a power script fails, there is no UI feedback
1335+
1336+#1186196 "Starting a node" has different meanings in the UI and in the API.
1337+
1338+#1237215 maas and curtin do not indicate failure reasonably
1339+
1340+#1273222 MAAS doesn't check return values of power actions
1341+
1342+#1288502 archive and proxy settings not honoured for commissioning
1343+
1344+#1316919 Checks don't exist to confirm a node will actually boot
1345+
1346+#1321885 IPMI detection and automatic setting fail in ubuntu 14.04 maas
1347+
1348+#1325610 node marked "Ready" before poweroff complete
1349+
1350+#1325638 Add hardware enablement for Universal Management Gateway
1351+
1352+#1340188 unallocated node started manually, causes AssertionError for purpose poweroff
1353+
1354+#1341118 No feedback when IPMI credentials fail
1355+
1356+#1341121 No feedback to user when cluster is not running
1357+
1358+#1341581 power state is not represented in api and ui
1359+
1360+#1341800 MAAS doesn't support soft power off through the API
1361+
1362+#1344177 hostnames can't be changed while a node is acquired
1363+
1364+#1347518 Confusing error message when API key is wrong
1365+
1366+#1349496 Unable to request a specific static IP on the API
1367+
1368+#1349736 MAAS logging is too verbose and not very useful
1369+
1370+#1349917 guess_server_address() can return IPAddress or hostname
1371+
1372+#1350103 No support for armhf/keystone architecture
1373+
1374+#1350856 Can't constrain acquisition of nodes by not having a tag
1375+
1376+#1356880 MAAS shouldn't allow changing the hostname of a deployed node
1377+
1378+#1357714 Virsh power driver does not seem to work at all
1379+
1380+#1358859 Commissioning output xml is hard to understand, would be nice to have yaml as an output option.
1381+
1382+#1359169 MAAS should handle invalid consumers gracefully
1383+
1384+#1359822 Gateway is missing in network definition
1385+
1386+#1363913 Impossible to remove last MAC from network in UI
1387+
1388+#1364228 Help text for node hostname is wrong
1389+
1390+#1364591 MAAS Archive Mirror does not respect non-default port
1391+
1392+#1365616 Non-admin access to cluster controller config
1393+
1394+#1365619 DNS should be an optional field in the network definition
1395+
1396+#1365776 commissioning results view for a node also shows installation results
1397+
1398+#1366812 Old boot resources are not being removed on clusters
1399+
1400+#1367455 MAC address for node's IPMI is reversed looked up to yield IP address using case sensitive comparison
1401+
1402+#1373580 [SRU] Glen m700 cartridge list as ARM64/generic after enlist
1403+
1404+#1373723 Releasing a node without power parameters ends up in not being able to release a node
1405+
1406+#1233158 no way to get power parameters in api
1407+
1408+#1319854 `maas login` tells you you're logged in successfully when you're not
1409+
1410+#1368480 Need API to gather image metadata across all of MAAS
1411+
1412+#1281406 Disk/memory space on Node edit page have no units
1413+
1414+#1299231 MAAS DHCP/DNS can't manage more than a /16 network
1415+
1416+#1357381 maas-region-admin createadmin shows error if not params given
1417+
1418+#1376393 powerkvm boot loader installs even when not needed
1419+
1420+#1287224 MAAS random generated hostnames are not pronounceable
1421+
1422+#1348364 non-maas managed subnets cannot query maas DNS
1423+
1424+
1425+>>>>>>> MERGE-SOURCE
1426 1.6.1
1427 =====
1428
1429
1430=== modified file 'src/maasserver/api/nodes.py'
1431=== modified file 'src/maasserver/api/tests/test_enlistment.py'
1432=== modified file 'src/maasserver/api/tests/test_node.py'
1433--- src/maasserver/api/tests/test_node.py 2014-11-05 16:29:35 +0000
1434+++ src/maasserver/api/tests/test_node.py 2014-11-06 01:24:10 +0000
1435@@ -26,7 +26,12 @@
1436 from maasserver.enum import (
1437 IPADDRESS_TYPE,
1438 NODE_STATUS,
1439- NODE_STATUS_CHOICES,
1440+<<<<<<< TREE
1441+ NODE_STATUS_CHOICES,
1442+=======
1443+ NODE_STATUS_CHOICES,
1444+ NODE_STATUS_CHOICES_DICT,
1445+>>>>>>> MERGE-SOURCE
1446 )
1447 from maasserver.fields import (
1448 MAC,
1449
1450=== modified file 'src/maasserver/api/tests/test_nodes.py'
1451=== modified file 'src/maasserver/bootresources.py'
1452=== modified file 'src/maasserver/forms.py'
1453=== modified file 'src/maasserver/forms_settings.py'
1454--- src/maasserver/forms_settings.py 2014-09-25 21:06:39 +0000
1455+++ src/maasserver/forms_settings.py 2014-11-06 01:24:10 +0000
1456@@ -266,6 +266,16 @@
1457 "Erase nodes' disks prior to releasing.")
1458 }
1459 },
1460+ 'enable_dhcp_discovery_on_unconfigured_interfaces': {
1461+ 'default': False,
1462+ 'form': forms.BooleanField,
1463+ 'form_kwargs': {
1464+ 'required': False,
1465+ 'label': (
1466+ "Perform DHCP discovery on unconfigured network "
1467+ "interfaces of commissioning nodes."),
1468+ }
1469+ },
1470 }
1471
1472
1473
1474=== modified file 'src/maasserver/middleware.py'
1475=== modified file 'src/maasserver/models/config.py'
1476--- src/maasserver/models/config.py 2014-10-08 20:41:31 +0000
1477+++ src/maasserver/models/config.py 2014-11-06 01:24:10 +0000
1478@@ -60,6 +60,7 @@
1479 # Third Party
1480 'enable_third_party_drivers': True,
1481 'enable_disk_erasing_on_release': False,
1482+ 'enable_dhcp_discovery_on_unconfigured_interfaces': True,
1483 ## /settings
1484 }
1485
1486
1487=== modified file 'src/maasserver/models/node.py'
1488--- src/maasserver/models/node.py 2014-11-05 23:39:16 +0000
1489+++ src/maasserver/models/node.py 2014-11-06 01:24:10 +0000
1490@@ -319,6 +319,146 @@
1491 available_nodes = self.get_nodes(for_user, NODE_PERMISSION.VIEW)
1492 return available_nodes.filter(status=NODE_STATUS.READY)
1493
1494+<<<<<<< TREE
1495+=======
1496+ def stop_nodes(self, ids, by_user, stop_mode='hard'):
1497+ """Request on given user's behalf that the given nodes be shut down.
1498+
1499+ Shutdown is only requested for nodes that the user has ownership
1500+ privileges for; any other nodes in the request are ignored.
1501+
1502+ :param ids: The `system_id` values for nodes to be shut down.
1503+ :type ids: Sequence
1504+ :param by_user: Requesting user.
1505+ :type by_user: User_
1506+ :param stop_mode: Power off mode - usually 'soft' or 'hard'.
1507+ :type stop_mode: unicode
1508+ :return: Those Nodes for which shutdown was actually requested.
1509+ :rtype: list
1510+ """
1511+ # Obtain node model objects for each node specified.
1512+ nodes = self.get_nodes(by_user, NODE_PERMISSION.EDIT, ids=ids)
1513+
1514+ # Helper function to whittle the list of nodes down to those that we
1515+ # can actually stop, and keep hold of their power control info.
1516+ def gen_power_info(nodes):
1517+ for node in nodes:
1518+ power_info = node.get_effective_power_info()
1519+ if power_info.can_be_stopped:
1520+ # Smuggle in a hint about how to power-off the node.
1521+ power_info.power_parameters['power_off_mode'] = stop_mode
1522+ yield node, power_info
1523+
1524+ # Create info that we can pass into the reactor (no model objects).
1525+ nodes_stop_info = list(
1526+ (node.system_id, node.hostname, node.nodegroup.uuid, power_info)
1527+ for node, power_info in gen_power_info(nodes))
1528+ powered_systems = [
1529+ system_id for system_id, _, _, _ in nodes_stop_info]
1530+
1531+ # Request that these nodes be powered off and wait for the
1532+ # commands to return or fail.
1533+ deferreds = power_off_nodes(nodes_stop_info).viewvalues()
1534+ wait_for_power_commands(deferreds)
1535+
1536+ # Return a list of those nodes that we've sent power commands for.
1537+ return list(
1538+ node for node in nodes if node.system_id in powered_systems)
1539+
1540+ def start_nodes(self, ids, by_user, user_data=None):
1541+ """Request on given user's behalf that the given nodes be started up.
1542+
1543+ Power-on is only requested for nodes that the user has ownership
1544+ privileges for; any other nodes in the request are ignored.
1545+
1546+ Nodes are also ignored if they don't have a valid power type
1547+ configured.
1548+
1549+ :param ids: The `system_id` values for nodes to be started.
1550+ :type ids: Sequence
1551+ :param by_user: Requesting user.
1552+ :type by_user: User_
1553+ :param user_data: Optional blob of user-data to be made available to
1554+ the nodes through the metadata service. If not given, any
1555+ previous user data is used.
1556+ :type user_data: unicode
1557+ :return: Those Nodes for which power-on was actually requested.
1558+ :rtype: list
1559+
1560+ :raises MultipleFailures: When there are failures originating from a
1561+ remote process. There could be one or more failures -- it's not
1562+ strictly *multiple* -- but they do all originate from comms with
1563+ remote processes.
1564+ :raises: `StaticIPAddressExhaustion` if there are not enough IP
1565+ addresses left in the static range..
1566+ """
1567+ # Avoid circular imports.
1568+ from metadataserver.models import NodeUserData
1569+
1570+ # Obtain node model objects for each node specified.
1571+ nodes = self.get_nodes(by_user, NODE_PERMISSION.EDIT, ids=ids)
1572+
1573+ # Record the same user data for all nodes we've been *requested* to
1574+ # start, regardless of whether or not we actually can; the user may
1575+ # choose to manually start them.
1576+ NodeUserData.objects.bulk_set_user_data(nodes, user_data)
1577+
1578+ # Claim static IP addresses for all nodes we've been *requested* to
1579+ # start, such that they're recorded in the database. This results in a
1580+ # mapping of nodegroups to (ips, macs).
1581+ static_mappings = defaultdict(dict)
1582+ for node in nodes:
1583+ if node.status == NODE_STATUS.ALLOCATED:
1584+ claims = node.claim_static_ip_addresses()
1585+ # If the PXE mac is on a managed interface then we can ask
1586+ # the cluster to generate the DHCP host map(s).
1587+ if node.is_pxe_mac_on_managed_interface():
1588+ static_mappings[node.nodegroup].update(claims)
1589+ node.start_deployment()
1590+
1591+ # XXX 2014-06-17 bigjools bug=1330765
1592+ # If the above fails it needs to release the static IPs back to the
1593+ # pool. An enclosing transaction or savepoint from the caller may take
1594+ # care of this, given that a serious problem above will result in an
1595+ # exception. If we're being belt-n-braces though it ought to clear up
1596+ # before returning too. As part of the robustness work coming up, it
1597+ # also needs to inform the user.
1598+
1599+ # Update host maps and wait for them so that we can report failures
1600+ # directly to the caller.
1601+ update_host_maps_failures = list(update_host_maps(static_mappings))
1602+ if len(update_host_maps_failures) != 0:
1603+ raise MultipleFailures(*update_host_maps_failures)
1604+
1605+ # Update the DNS zone with the new static IP info as necessary.
1606+ from maasserver.dns.config import change_dns_zones
1607+ change_dns_zones({node.nodegroup for node in nodes})
1608+
1609+ # Helper function to whittle the list of nodes down to those that we
1610+ # can actually start, and keep hold of their power control info.
1611+ def gen_power_info(nodes):
1612+ for node in nodes:
1613+ power_info = node.get_effective_power_info()
1614+ if power_info.can_be_started:
1615+ yield node, power_info
1616+
1617+ # Create info that we can pass into the reactor (no model objects).
1618+ nodes_start_info = list(
1619+ (node.system_id, node.hostname, node.nodegroup.uuid, power_info)
1620+ for node, power_info in gen_power_info(nodes))
1621+ powered_systems = [
1622+ system_id for system_id, _, _, _ in nodes_start_info]
1623+
1624+ # Request that these nodes be powered off and wait for the
1625+ # commands to return or fail.
1626+ deferreds = power_on_nodes(nodes_start_info).viewvalues()
1627+ wait_for_power_commands(deferreds)
1628+
1629+ # Return a list of those nodes that we've sent power commands for.
1630+ return list(
1631+ node for node in nodes if node.system_id in powered_systems)
1632+
1633+>>>>>>> MERGE-SOURCE
1634
1635 def patch_pgarray_types():
1636 """Monkey-patch incompatibility with recent versions of `djorm_pgarray`.
1637@@ -1227,6 +1367,7 @@
1638 self.distro_series = ''
1639 self.license_key = ''
1640 self.save()
1641+<<<<<<< TREE
1642
1643 # Clear installation results
1644 NodeResult.objects.filter(
1645@@ -1239,6 +1380,16 @@
1646 from maasserver.dns.config import change_dns_zones
1647 change_dns_zones([self.nodegroup])
1648
1649+=======
1650+
1651+ # Do these after updating the node to avoid creating deadlocks with
1652+ # other node editing operations.
1653+ deallocated_ips = StaticIPAddress.objects.deallocate_by_node(self)
1654+ self.delete_host_maps(deallocated_ips)
1655+ from maasserver.dns.config import change_dns_zones
1656+ change_dns_zones([self.nodegroup])
1657+
1658+>>>>>>> MERGE-SOURCE
1659 # We explicitly commit here because during bulk node actions we
1660 # want to make sure that each successful state transition is
1661 # recorded in the DB.
1662@@ -1412,6 +1563,7 @@
1663 return self.pxe_mac
1664
1665 return self.macaddress_set.first()
1666+<<<<<<< TREE
1667
1668 def is_pxe_mac_on_managed_interface(self):
1669 pxe_mac = self.get_pxe_mac()
1670@@ -1541,3 +1693,13 @@
1671 d = power_off_nodes([stop_info]).values().pop()
1672 wait_for_power_command(d)
1673 return True
1674+=======
1675+
1676+ def is_pxe_mac_on_managed_interface(self):
1677+ pxe_mac = self.get_pxe_mac()
1678+ if pxe_mac is not None:
1679+ cluster_interface = pxe_mac.cluster_interface
1680+ if cluster_interface is not None:
1681+ return cluster_interface.is_managed
1682+ return False
1683+>>>>>>> MERGE-SOURCE
1684
1685=== modified file 'src/maasserver/models/staticipaddress.py'
1686=== modified file 'src/maasserver/models/tests/test_bootsource.py'
1687--- src/maasserver/models/tests/test_bootsource.py 2014-11-03 01:36:42 +0000
1688+++ src/maasserver/models/tests/test_bootsource.py 2014-11-06 01:24:10 +0000
1689@@ -15,6 +15,7 @@
1690 __all__ = []
1691
1692 import os
1693+from unittest import skip
1694
1695 from django.core.exceptions import ValidationError
1696 from maasserver.bootsources import cache_boot_sources
1697@@ -98,6 +99,9 @@
1698 [],
1699 boot_source_dict['selections'])
1700
1701+ # XXX: GavinPanella 2014-10-28 bug=1376317: This test is fragile, possibly
1702+ # due to isolation issues.
1703+ @skip("Possible isolation issues")
1704 def test_calls_cache_boot_sources_on_create(self):
1705 mock_callLater = self.patch(reactor, 'callLater')
1706 BootSource.objects.create(
1707
1708=== modified file 'src/maasserver/models/tests/test_node.py'
1709--- src/maasserver/models/tests/test_node.py 2014-11-05 15:28:40 +0000
1710+++ src/maasserver/models/tests/test_node.py 2014-11-06 01:24:10 +0000
1711@@ -19,6 +19,7 @@
1712 timedelta,
1713 )
1714 import random
1715+from unittest import skip
1716
1717 import crochet
1718 from django.core.exceptions import ValidationError
1719@@ -2083,6 +2084,422 @@
1720 self.assertThat(erase_mock, MockNotCalled())
1721
1722
1723+<<<<<<< TREE
1724+=======
1725+class NodeManagerTest_StartNodes(MAASServerTestCase):
1726+
1727+ def setUp(self):
1728+ super(NodeManagerTest_StartNodes, self).setUp()
1729+ self.useFixture(RegionEventLoopFixture("rpc"))
1730+ self.useFixture(RunningEventLoopFixture())
1731+ self.rpc_fixture = self.useFixture(MockLiveRegionToClusterRPCFixture())
1732+
1733+ def prepare_rpc_to_cluster(self, nodegroup):
1734+ protocol = self.rpc_fixture.makeCluster(
1735+ nodegroup, cluster_module.CreateHostMaps, cluster_module.PowerOn,
1736+ cluster_module.StartMonitors)
1737+ protocol.CreateHostMaps.side_effect = always_succeed_with({})
1738+ protocol.StartMonitors.side_effect = always_succeed_with({})
1739+ protocol.PowerOn.side_effect = always_succeed_with({})
1740+ return protocol
1741+
1742+ def make_acquired_nodes_with_macs(self, user, nodegroup=None, count=3):
1743+ nodes = []
1744+ for _ in xrange(count):
1745+ node = factory.make_node_with_mac_attached_to_nodegroupinterface(
1746+ nodegroup=nodegroup, status=NODE_STATUS.READY)
1747+ self.prepare_rpc_to_cluster(node.nodegroup)
1748+ node.acquire(user)
1749+ nodes.append(node)
1750+ return nodes
1751+
1752+ def test__sets_user_data(self):
1753+ user = factory.make_User()
1754+ nodegroup = factory.make_NodeGroup()
1755+ self.prepare_rpc_to_cluster(nodegroup)
1756+ nodes = self.make_acquired_nodes_with_macs(user, nodegroup)
1757+ user_data = factory.make_bytes()
1758+
1759+ with TwistedLoggerFixture() as twisted_log:
1760+ Node.objects.start_nodes(
1761+ list(node.system_id for node in nodes),
1762+ user, user_data=user_data)
1763+
1764+ # All three nodes have been given the same user data.
1765+ nuds = NodeUserData.objects.filter(
1766+ node_id__in=(node.id for node in nodes))
1767+ self.assertEqual({user_data}, {nud.data for nud in nuds})
1768+ # No complaints are made to the Twisted log.
1769+ self.assertFalse(twisted_log.containsError(), twisted_log.output)
1770+
1771+ def test__resets_user_data(self):
1772+ user = factory.make_User()
1773+ nodegroup = factory.make_NodeGroup()
1774+ self.prepare_rpc_to_cluster(nodegroup)
1775+ nodes = self.make_acquired_nodes_with_macs(user, nodegroup)
1776+
1777+ with TwistedLoggerFixture() as twisted_log:
1778+ Node.objects.start_nodes(
1779+ list(node.system_id for node in nodes),
1780+ user, user_data=None)
1781+
1782+ # All three nodes have been given the same user data.
1783+ nuds = NodeUserData.objects.filter(
1784+ node_id__in=(node.id for node in nodes))
1785+ self.assertThat(list(nuds), HasLength(0))
1786+ # No complaints are made to the Twisted log.
1787+ self.assertFalse(twisted_log.containsError(), twisted_log.output)
1788+
1789+ def test__claims_static_ip_addresses(self):
1790+ user = factory.make_User()
1791+ nodegroup = factory.make_NodeGroup()
1792+ self.prepare_rpc_to_cluster(nodegroup)
1793+ nodes = self.make_acquired_nodes_with_macs(user, nodegroup)
1794+
1795+ claim_static_ip_addresses = self.patch_autospec(
1796+ Node, "claim_static_ip_addresses", spec_set=False)
1797+ claim_static_ip_addresses.return_value = {}
1798+
1799+ with TwistedLoggerFixture() as twisted_log:
1800+ Node.objects.start_nodes(
1801+ list(node.system_id for node in nodes), user)
1802+
1803+ for node in nodes:
1804+ self.expectThat(claim_static_ip_addresses, MockAnyCall(node))
1805+ # No complaints are made to the Twisted log.
1806+ self.assertFalse(twisted_log.containsError(), twisted_log.output)
1807+
1808+ def test__claims_static_ip_addresses_for_allocated_nodes_only(self):
1809+ user = factory.make_User()
1810+ nodegroup = factory.make_NodeGroup()
1811+ self.prepare_rpc_to_cluster(nodegroup)
1812+ nodes = self.make_acquired_nodes_with_macs(user, nodegroup, count=2)
1813+
1814+ # Change the status of the first node to something other than
1815+ # allocated.
1816+ broken_node, allocated_node = nodes
1817+ broken_node.status = NODE_STATUS.BROKEN
1818+ broken_node.save()
1819+
1820+ claim_static_ip_addresses = self.patch_autospec(
1821+ Node, "claim_static_ip_addresses", spec_set=False)
1822+ claim_static_ip_addresses.return_value = {}
1823+
1824+ with TwistedLoggerFixture() as twisted_log:
1825+ Node.objects.start_nodes(
1826+ list(node.system_id for node in nodes), user)
1827+
1828+ # Only one call is made to claim_static_ip_addresses(), for the
1829+ # still-allocated node.
1830+ self.assertThat(
1831+ claim_static_ip_addresses,
1832+ MockCalledOnceWith(allocated_node))
1833+ # No complaints are made to the Twisted log.
1834+ self.assertFalse(twisted_log.containsError(), twisted_log.output)
1835+
1836+ def test__updates_host_maps(self):
1837+ user = factory.make_User()
1838+ nodes = self.make_acquired_nodes_with_macs(user)
1839+
1840+ update_host_maps = self.patch(node_module, "update_host_maps")
1841+ update_host_maps.return_value = [] # No failures.
1842+
1843+ with TwistedLoggerFixture() as twisted_log:
1844+ Node.objects.start_nodes(
1845+ list(node.system_id for node in nodes), user)
1846+
1847+ # Host maps are updated.
1848+ self.assertThat(
1849+ update_host_maps, MockCalledOnceWith({
1850+ node.nodegroup: {
1851+ ip_address.ip: mac.mac_address
1852+ for ip_address in mac.ip_addresses.all()
1853+ }
1854+ for node in nodes
1855+ for mac in node.mac_addresses_on_managed_interfaces()
1856+ }))
1857+ # No complaints are made to the Twisted log.
1858+ self.assertFalse(twisted_log.containsError(), twisted_log.output)
1859+
1860+ def test__propagates_errors_when_updating_host_maps(self):
1861+ user = factory.make_User()
1862+ nodes = self.make_acquired_nodes_with_macs(user)
1863+
1864+ update_host_maps = self.patch(node_module, "update_host_maps")
1865+ update_host_maps.return_value = [
1866+ Failure(AssertionError("That is so not true")),
1867+ Failure(ZeroDivisionError("I cannot defy mathematics")),
1868+ ]
1869+
1870+ with TwistedLoggerFixture() as twisted_log:
1871+ error = self.assertRaises(
1872+ MultipleFailures, Node.objects.start_nodes,
1873+ list(node.system_id for node in nodes), user)
1874+
1875+ self.assertSequenceEqual(
1876+ update_host_maps.return_value, error.args)
1877+
1878+ # No complaints are made to the Twisted log.
1879+ self.assertFalse(twisted_log.containsError(), twisted_log.output)
1880+
1881+ def test__updates_dns(self):
1882+ user = factory.make_User()
1883+ nodes = self.make_acquired_nodes_with_macs(user)
1884+
1885+ change_dns_zones = self.patch(dns_config, "change_dns_zones")
1886+
1887+ with TwistedLoggerFixture() as twisted_log:
1888+ Node.objects.start_nodes(
1889+ list(node.system_id for node in nodes), user)
1890+
1891+ self.assertThat(
1892+ change_dns_zones, MockCalledOnceWith(
1893+ {node.nodegroup for node in nodes}))
1894+
1895+ # No complaints are made to the Twisted log.
1896+ self.assertFalse(twisted_log.containsError(), twisted_log.output)
1897+
1898+ def test__starts_nodes(self):
1899+ user = factory.make_User()
1900+ nodes = self.make_acquired_nodes_with_macs(user)
1901+ power_infos = list(
1902+ node.get_effective_power_info()
1903+ for node in nodes)
1904+
1905+ power_on_nodes = self.patch(node_module, "power_on_nodes")
1906+ power_on_nodes.return_value = {}
1907+
1908+ with TwistedLoggerFixture() as twisted_log:
1909+ Node.objects.start_nodes(
1910+ list(node.system_id for node in nodes), user)
1911+
1912+ self.assertThat(power_on_nodes, MockCalledOnceWith(ANY))
1913+
1914+ nodes_start_info_observed = power_on_nodes.call_args[0][0]
1915+ nodes_start_info_expected = [
1916+ (node.system_id, node.hostname, node.nodegroup.uuid, power_info)
1917+ for node, power_info in izip(nodes, power_infos)
1918+ ]
1919+
1920+ # If the following fails the diff is big, but it's useful.
1921+ self.maxDiff = None
1922+
1923+ self.assertItemsEqual(
1924+ nodes_start_info_expected,
1925+ nodes_start_info_observed)
1926+
1927+ # No complaints are made to the Twisted log.
1928+ self.assertFalse(twisted_log.containsError(), twisted_log.output)
1929+
1930+ def test__raises_failures_for_nodes_that_cannot_be_started(self):
1931+ power_on_nodes = self.patch(node_module, "power_on_nodes")
1932+ power_on_nodes.return_value = {
1933+ factory.make_name("system_id"): defer.fail(
1934+ ZeroDivisionError("Defiance is futile")),
1935+ factory.make_name("system_id"): defer.succeed({}),
1936+ }
1937+
1938+ failures = self.assertRaises(
1939+ MultipleFailures, Node.objects.start_nodes, [],
1940+ factory.make_User())
1941+ [failure] = failures.args
1942+ self.assertThat(failure.value, IsInstance(ZeroDivisionError))
1943+
1944+ def test__marks_allocated_node_as_deploying(self):
1945+ user = factory.make_User()
1946+ [node] = self.make_acquired_nodes_with_macs(user, count=1)
1947+ nodes_started = Node.objects.start_nodes([node.system_id], user)
1948+ self.assertItemsEqual([node], nodes_started)
1949+ self.assertEqual(
1950+ NODE_STATUS.DEPLOYING, reload_object(node).status)
1951+
1952+ def test__does_not_change_state_of_deployed_node(self):
1953+ user = factory.make_User()
1954+ node = factory.make_Node(
1955+ power_type='ether_wake', status=NODE_STATUS.DEPLOYED,
1956+ owner=user)
1957+ factory.make_MACAddress(node=node)
1958+ power_on_nodes = self.patch(node_module, "power_on_nodes")
1959+ power_on_nodes.return_value = {
1960+ node.system_id: defer.succeed({}),
1961+ }
1962+ nodes_started = Node.objects.start_nodes([node.system_id], user)
1963+ self.assertItemsEqual([node], nodes_started)
1964+ self.assertEqual(
1965+ NODE_STATUS.DEPLOYED, reload_object(node).status)
1966+
1967+ def test__only_returns_nodes_for_which_power_commands_have_been_sent(self):
1968+ user = factory.make_User()
1969+ node1, node2 = self.make_acquired_nodes_with_macs(user, count=2)
1970+ node1.power_type = 'ether_wake' # Can be started.
1971+ node1.save()
1972+ node2.power_type = '' # Undefined power type, cannot be started.
1973+ node2.save()
1974+ nodes_started = Node.objects.start_nodes(
1975+ [node1.system_id, node2.system_id], user)
1976+ self.assertItemsEqual([node1], nodes_started)
1977+
1978+ def test__does_not_try_to_start_nodes_not_allocated_to_user(self):
1979+ user1 = factory.make_User()
1980+ [node1] = self.make_acquired_nodes_with_macs(user1, count=1)
1981+ node1.power_type = 'ether_wake' # can be started.
1982+ node1.save()
1983+ user2 = factory.make_User()
1984+ [node2] = self.make_acquired_nodes_with_macs(user2, count=1)
1985+ node2.power_type = 'ether_wake' # can be started.
1986+ node2.save()
1987+
1988+ self.patch(node_module, 'power_on_nodes')
1989+ self.patch(node_module, 'wait_for_power_commands')
1990+ nodes_started = Node.objects.start_nodes(
1991+ [node1.system_id, node2.system_id], user1)
1992+
1993+ # Since no power commands were sent to the node, it isn't
1994+ # returned by start_nodes().
1995+ # test__only_returns_nodes_for_which_power_commands_have_been_sent()
1996+ # demonstrates this behaviour.
1997+ self.assertItemsEqual([node1], nodes_started)
1998+
1999+ # XXX: GavinPanella 2014-10-30 bug=1387696: Flaky test, returning wrong IP
2000+ # addresses. Appears unrelated to the changes that highlighted this.
2001+ @skip("Flaky; returns incorrect IP addresses")
2002+ def test__does_not_generate_host_maps_if_not_on_managed_interface(self):
2003+ cluster = factory.make_NodeGroup()
2004+ managed_interface = factory.make_NodeGroupInterface(
2005+ nodegroup=cluster,
2006+ management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS)
2007+ unmanaged_interface = factory.make_NodeGroupInterface(
2008+ nodegroup=cluster,
2009+ management=NODEGROUPINTERFACE_MANAGEMENT.DEFAULT)
2010+ user = factory.make_User()
2011+ [node1, node2] = self.make_acquired_nodes_with_macs(
2012+ user, nodegroup=cluster, count=2)
2013+ # Give the node a PXE MAC address on the cluster's interface.
2014+ node1_mac = node1.get_pxe_mac()
2015+ node1_mac.cluster_interface = managed_interface
2016+ node1_mac.save()
2017+ node2_mac = node1.get_pxe_mac()
2018+ node2_mac.cluster_interface = unmanaged_interface
2019+ node2_mac.save()
2020+
2021+ node1_ip = factory.make_ipv4_address()
2022+ claim_static_ip_addresses = self.patch(
2023+ node_module.Node, 'claim_static_ip_addresses')
2024+ claim_static_ip_addresses.side_effect = [
2025+ [(node1_ip, node1_mac.mac_address)],
2026+ [(factory.make_ipv4_address(), node2_mac.mac_address)],
2027+ ]
2028+
2029+ update_host_maps = self.patch(node_module, "update_host_maps")
2030+ Node.objects.start_nodes([node1.system_id, node2.system_id], user)
2031+ self.expectThat(update_host_maps, MockCalledOnceWith(ANY))
2032+
2033+ observed_static_mappings = update_host_maps.call_args[0][0]
2034+
2035+ [observed_cluster] = observed_static_mappings.keys()
2036+ self.expectThat(observed_cluster.uuid, Equals(cluster.uuid))
2037+
2038+ observed_claims = observed_static_mappings.values()
2039+ self.expectThat(
2040+ observed_claims,
2041+ Equals([{node1_ip: node1_mac.mac_address}]))
2042+
2043+
2044+class NodeManagerTest_StopNodes(MAASServerTestCase):
2045+
2046+ def make_nodes_with_macs(self, user, nodegroup=None, count=3):
2047+ nodes = []
2048+ for _ in xrange(count):
2049+ node = factory.make_node_with_mac_attached_to_nodegroupinterface(
2050+ nodegroup=nodegroup, status=NODE_STATUS.READY,
2051+ power_type='virsh')
2052+ node.acquire(user)
2053+ nodes.append(node)
2054+ return nodes
2055+
2056+ def test_stop_nodes_stops_nodes(self):
2057+ wait_for_power_commands = self.patch_autospec(
2058+ node_module, 'wait_for_power_commands')
2059+ power_off_nodes = self.patch_autospec(node_module, "power_off_nodes")
2060+ power_off_nodes.side_effect = lambda nodes: {
2061+ system_id: Deferred() for system_id, _, _, _ in nodes}
2062+
2063+ user = factory.make_User()
2064+ nodes = self.make_nodes_with_macs(user)
2065+ power_infos = list(node.get_effective_power_info() for node in nodes)
2066+
2067+ stop_mode = factory.make_name('stop-mode')
2068+ nodes_stopped = Node.objects.stop_nodes(
2069+ list(node.system_id for node in nodes), user, stop_mode)
2070+
2071+ self.assertItemsEqual(nodes, nodes_stopped)
2072+ self.assertThat(power_off_nodes, MockCalledOnceWith(ANY))
2073+ self.assertThat(wait_for_power_commands, MockCalledOnceWith(ANY))
2074+
2075+ nodes_stop_info_observed = power_off_nodes.call_args[0][0]
2076+ nodes_stop_info_expected = [
2077+ (node.system_id, node.hostname, node.nodegroup.uuid, power_info)
2078+ for node, power_info in izip(nodes, power_infos)
2079+ ]
2080+
2081+ # The stop mode is added into the power info that's passed.
2082+ for _, _, _, power_info in nodes_stop_info_expected:
2083+ power_info.power_parameters['power_off_mode'] = stop_mode
2084+
2085+ # If the following fails the diff is big, but it's useful.
2086+ self.maxDiff = None
2087+
2088+ self.assertItemsEqual(
2089+ nodes_stop_info_expected,
2090+ nodes_stop_info_observed)
2091+
2092+ def test_stop_nodes_ignores_uneditable_nodes(self):
2093+ owner = factory.make_User()
2094+ nodes = self.make_nodes_with_macs(owner)
2095+
2096+ user = factory.make_User()
2097+ nodes_stopped = Node.objects.stop_nodes(
2098+ list(node.system_id for node in nodes), user)
2099+
2100+ self.assertItemsEqual([], nodes_stopped)
2101+
2102+ def test_stop_nodes_does_not_attempt_power_off_if_no_power_type(self):
2103+ # If the node has a power_type set to UNKNOWN_POWER_TYPE, stop_nodes()
2104+ # won't attempt to power it off.
2105+ user = factory.make_User()
2106+ [node] = self.make_nodes_with_macs(user, count=1)
2107+ node.power_type = ""
2108+ node.save()
2109+
2110+ nodes_stopped = Node.objects.stop_nodes([node.system_id], user)
2111+ self.assertItemsEqual([], nodes_stopped)
2112+
2113+ def test_stop_nodes_does_not_attempt_power_off_if_cannot_be_stopped(self):
2114+ # If the node has a power_type that MAAS knows stopping does not work,
2115+ # stop_nodes() won't attempt to power it off.
2116+ user = factory.make_User()
2117+ [node] = self.make_nodes_with_macs(user, count=1)
2118+ node.power_type = "ether_wake"
2119+ node.save()
2120+
2121+ nodes_stopped = Node.objects.stop_nodes([node.system_id], user)
2122+ self.assertItemsEqual([], nodes_stopped)
2123+
2124+ def test__raises_failures_for_nodes_that_cannot_be_stopped(self):
2125+ power_off_nodes = self.patch(node_module, "power_off_nodes")
2126+ power_off_nodes.return_value = {
2127+ factory.make_name("system_id"): defer.fail(
2128+ ZeroDivisionError("Ee by gum lad, that's a rum 'un.")),
2129+ factory.make_name("system_id"): defer.succeed({}),
2130+ }
2131+
2132+ failures = self.assertRaises(
2133+ MultipleFailures, Node.objects.stop_nodes, [], factory.make_User())
2134+ [failure] = failures.args
2135+ self.assertThat(failure.value, IsInstance(ZeroDivisionError))
2136+
2137+
2138+>>>>>>> MERGE-SOURCE
2139 class TestNodeTransitionMonitors(MAASServerTestCase):
2140
2141 def prepare_rpc(self):
2142
2143=== modified file 'src/maasserver/node_action.py'
2144=== modified file 'src/maasserver/node_status.py'
2145=== modified file 'src/maasserver/tests/test_bootresources.py'
2146--- src/maasserver/tests/test_bootresources.py 2014-11-03 01:10:28 +0000
2147+++ src/maasserver/tests/test_bootresources.py 2014-11-06 01:24:10 +0000
2148@@ -1063,8 +1063,13 @@
2149 bootresources, 'cache_boot_sources')
2150 write_all_keyrings = self.patch(
2151 bootresources, 'write_all_keyrings')
2152+<<<<<<< TREE
2153 write_all_keyrings.return_value = [sentinel.source]
2154 image_descriptions = self.patch(
2155+=======
2156+ fake_write_all_keyrings.return_value = [sentinel.source]
2157+ fake_image_descriptions = self.patch(
2158+>>>>>>> MERGE-SOURCE
2159 bootresources, 'download_all_image_descriptions')
2160 descriptions = Mock()
2161 descriptions.is_empty.return_value = False
2162@@ -1082,15 +1087,29 @@
2163 self.expectThat(
2164 write_all_keyrings,
2165 MockCalledOnceWith(ANY, []))
2166+<<<<<<< TREE
2167 self.expectThat(
2168 image_descriptions,
2169 MockCalledOnceWith([sentinel.source]))
2170 self.expectThat(
2171 map_products,
2172+=======
2173+ self.assertThat(
2174+ fake_image_descriptions,
2175+ MockCalledOnceWith([sentinel.source]))
2176+ self.assertThat(
2177+ fake_map_products,
2178+>>>>>>> MERGE-SOURCE
2179 MockCalledOnceWith(descriptions))
2180+<<<<<<< TREE
2181 self.expectThat(
2182 download_all_boot_resources,
2183 MockCalledOnceWith([sentinel.source], sentinel.mapping))
2184+=======
2185+ self.assertThat(
2186+ fake_download_all_boot_resources,
2187+ MockCalledOnceWith([sentinel.source], sentinel.mapping))
2188+>>>>>>> MERGE-SOURCE
2189
2190 def test__import_resources_has_env_GNUPGHOME_set(self):
2191 fake_image_descriptions = self.patch(
2192
2193=== modified file 'src/maasserver/tests/test_middleware.py'
2194=== modified file 'src/maasserver/tests/test_node_action.py'
2195=== modified file 'src/maasserver/views/nodes.py'
2196--- src/maasserver/views/nodes.py 2014-11-05 15:24:51 +0000
2197+++ src/maasserver/views/nodes.py 2014-11-06 01:24:10 +0000
2198@@ -612,8 +612,13 @@
2199 context['nodecommissionresults'] = commissioning_results
2200
2201 installation_results = NodeResult.objects.filter(
2202+<<<<<<< TREE
2203 node=node, result_type=RESULT_TYPE.INSTALLATION)
2204 if len(installation_results) > 1:
2205+=======
2206+ node=node, result_type=RESULT_TYPE.INSTALLING)
2207+ if len(installation_results) > 1:
2208+>>>>>>> MERGE-SOURCE
2209 for result in installation_results:
2210 result.name = re.sub('[_.]', ' ', result.name)
2211 context['nodeinstallresults'] = installation_results
2212
2213=== modified file 'src/maasserver/views/tests/test_nodes.py'
2214--- src/maasserver/views/tests/test_nodes.py 2014-11-05 15:24:51 +0000
2215+++ src/maasserver/views/tests/test_nodes.py 2014-11-06 01:24:10 +0000
2216@@ -1639,6 +1639,7 @@
2217 else:
2218 self.fail("Found more than one link: %s" % links)
2219
2220+<<<<<<< TREE
2221 def get_installation_results_link(self, display):
2222 """Find the results link in `display`.
2223
2224@@ -1656,6 +1657,25 @@
2225 elif len(links) > 1:
2226 return links
2227
2228+=======
2229+ def get_installing_results_link(self, display):
2230+ """Find the results link in `display`.
2231+
2232+ :param display: Results display section for a node, as returned by
2233+ `request_results_display`.
2234+ :return: `lxml.html.HtmlElement` for the link to the node's
2235+ installation results, as found in `display`; or `None` if it was
2236+ not present.
2237+ """
2238+ links = display.cssselect('a')
2239+ if len(links) == 0:
2240+ return None
2241+ elif len(links) == 1:
2242+ return links[0]
2243+ elif len(links) > 1:
2244+ return links
2245+
2246+>>>>>>> MERGE-SOURCE
2247 def test_view_node_links_to_commissioning_results_if_appropriate(self):
2248 self.client_log_in(as_admin=True)
2249 result = factory.make_NodeResult_for_commissioning()
2250@@ -1735,8 +1755,13 @@
2251 self.logged_in_user = user
2252 result = factory.make_NodeResult_for_installation(node=node)
2253 section = self.request_results_display(
2254+<<<<<<< TREE
2255 result.node, RESULT_TYPE.INSTALLATION)
2256 link = self.get_installation_results_link(section)
2257+=======
2258+ result.node, RESULT_TYPE.INSTALLING)
2259+ link = self.get_installing_results_link(section)
2260+>>>>>>> MERGE-SOURCE
2261 self.assertNotIn(
2262 normalise_whitespace(link.text_content()),
2263 ('', None))
2264@@ -1778,6 +1803,35 @@
2265 ContainsAll(
2266 [normalise_whitespace(link.text_content()) for link in links]))
2267
2268+ def test_view_node_shows_single_installing_result(self):
2269+ self.client_log_in(as_admin=True)
2270+ result = factory.make_NodeResult_for_installing()
2271+ section = self.request_results_display(
2272+ result.node, RESULT_TYPE.INSTALLING)
2273+ link = self.get_installing_results_link(section)
2274+ self.assertEqual(
2275+ "install log",
2276+ normalise_whitespace(link.text_content()))
2277+
2278+ def test_view_node_shows_multiple_installing_results(self):
2279+ self.client_log_in(as_admin=True)
2280+ node = factory.make_Node()
2281+ num_results = randint(2, 5)
2282+ results_names = []
2283+ for _ in range(num_results):
2284+ node_result = factory.make_NodeResult_for_installing(node=node)
2285+ results_names.append(node_result.name)
2286+ section = self.request_results_display(
2287+ node, RESULT_TYPE.INSTALLING)
2288+ links = self.get_installing_results_link(section)
2289+ expected_results_names = list(reversed(results_names))
2290+ observed_results_names = list(
2291+ normalise_whitespace(link.text_content())
2292+ for link in links)
2293+ self.assertListEqual(
2294+ expected_results_names,
2295+ observed_results_names)
2296+
2297
2298 class NodeListingSelectionJSControls(SeleniumTestCase):
2299
2300
2301=== modified file 'src/metadataserver/api.py'
2302--- src/metadataserver/api.py 2014-11-04 07:49:58 +0000
2303+++ src/metadataserver/api.py 2014-11-06 01:24:10 +0000
2304@@ -76,7 +76,7 @@
2305 NodeUserData,
2306 )
2307 from metadataserver.models.commissioningscript import (
2308- BUILTIN_COMMISSIONING_SCRIPTS,
2309+ get_builtin_commissioning_scripts,
2310 )
2311 from piston.utils import rc
2312 from provisioningserver.events import (
2313@@ -229,8 +229,10 @@
2314 script_result = int(request.POST.get('script_result', 0))
2315 for name, uploaded_file in request.FILES.items():
2316 raw_content = uploaded_file.read()
2317- if name in BUILTIN_COMMISSIONING_SCRIPTS:
2318- postprocess_hook = BUILTIN_COMMISSIONING_SCRIPTS[name]['hook']
2319+ builtin_commissioning_scripts = (
2320+ get_builtin_commissioning_scripts())
2321+ if name in builtin_commissioning_scripts:
2322+ postprocess_hook = builtin_commissioning_scripts[name]['hook']
2323 postprocess_hook(
2324 node=node, output=raw_content,
2325 exit_status=script_result)
2326@@ -280,8 +282,20 @@
2327
2328 if node.status == NODE_STATUS.COMMISSIONING:
2329 self._store_commissioning_results(node, request)
2330+<<<<<<< TREE
2331 store_node_power_parameters(node, request)
2332 node.stop_transition_monitor()
2333+=======
2334+ # XXX 2014-10-21 newell, bug=1382075
2335+ # Auto detection for IPMI tries to save power parameters
2336+ # for Moonshot. This causes issues if the node's power type
2337+ # is already MSCM as it uses SSH instead of IPMI. This fix
2338+ # is temporary as power parameters should not be overwritten
2339+ # during commissioning because MAAS already has knowledge to
2340+ # boot the node.
2341+ if node.power_type != "mscm":
2342+ store_node_power_parameters(node, request)
2343+>>>>>>> MERGE-SOURCE
2344 target_status = self.signaling_statuses.get(status)
2345 elif node.status == NODE_STATUS.DEPLOYING:
2346 self._store_installation_results(node, request)
2347
2348=== modified file 'src/metadataserver/models/commissioningscript.py'
2349--- src/metadataserver/models/commissioningscript.py 2014-10-29 22:37:02 +0000
2350+++ src/metadataserver/models/commissioningscript.py 2014-11-06 01:24:10 +0000
2351@@ -14,8 +14,8 @@
2352
2353 __metaclass__ = type
2354 __all__ = [
2355- 'BUILTIN_COMMISSIONING_SCRIPTS',
2356 'CommissioningScript',
2357+ 'get_builtin_commissioning_scripts',
2358 'inject_lldp_result',
2359 'inject_lshw_result',
2360 'inject_result',
2361@@ -43,6 +43,7 @@
2362 )
2363 from lxml import etree
2364 from maasserver.fields import MAC
2365+from maasserver.models import Config
2366 from maasserver.models.tag import Tag
2367 from metadataserver import DefaultMeta
2368 from metadataserver.enum import RESULT_TYPE
2369@@ -352,6 +353,7 @@
2370 LIST_MODALIASES_OUTPUT_NAME = '00-maas-04-list-modaliases.out'
2371 LIST_MODALIASES_SCRIPT = \
2372 'find /sys -name modalias -print0 | xargs -0 cat | sort -u'
2373+DHCP_UNCONFIGURED_INTERFACES_NAME = '00-maas-05-dhcp-unconfigured-ifaces'
2374
2375
2376 def null_hook(node, output, exit_status):
2377@@ -397,7 +399,7 @@
2378 'content': LIST_MODALIASES_SCRIPT.encode('ascii'),
2379 'hook': null_hook,
2380 },
2381- '00-maas-05-dhcp-unconfigured-ifaces': {
2382+ DHCP_UNCONFIGURED_INTERFACES_NAME: {
2383 'content': make_function_call_script(dhcp_explore),
2384 'hook': null_hook,
2385 },
2386@@ -430,6 +432,20 @@
2387 add_names_to_scripts(BUILTIN_COMMISSIONING_SCRIPTS)
2388
2389
2390+def get_builtin_commissioning_scripts():
2391+ """Get the builtin commissioning scripts.
2392+
2393+ The builtin scripts exposed may vary based on config settings.
2394+ """
2395+ scripts = BUILTIN_COMMISSIONING_SCRIPTS.copy()
2396+
2397+ config_key = 'enable_dhcp_discovery_on_unconfigured_interfaces'
2398+ if not Config.objects.get_config(config_key):
2399+ del scripts[DHCP_UNCONFIGURED_INTERFACES_NAME]
2400+
2401+ return scripts
2402+
2403+
2404 def add_script_to_archive(tarball, name, content, mtime):
2405 """Add a commissioning script to an archive of commissioning scripts."""
2406 assert isinstance(content, bytes), "Script content must be binary."
2407@@ -447,7 +463,7 @@
2408 """Utility for the collection of `CommissioningScript`s."""
2409
2410 def _iter_builtin_scripts(self):
2411- for script in BUILTIN_COMMISSIONING_SCRIPTS.itervalues():
2412+ for script in get_builtin_commissioning_scripts().itervalues():
2413 yield script['name'], script['content']
2414
2415 def _iter_user_scripts(self):
2416@@ -500,8 +516,9 @@
2417 NodeResult.objects.store_data(
2418 node, name, script_result=exit_status,
2419 result_type=RESULT_TYPE.COMMISSIONING, data=Bin(output))
2420- if name in BUILTIN_COMMISSIONING_SCRIPTS:
2421- postprocess_hook = BUILTIN_COMMISSIONING_SCRIPTS[name]['hook']
2422+ builtin_commissioning_scripts = get_builtin_commissioning_scripts()
2423+ if name in builtin_commissioning_scripts:
2424+ postprocess_hook = builtin_commissioning_scripts[name]['hook']
2425 postprocess_hook(node=node, output=output, exit_status=exit_status)
2426
2427
2428
2429=== modified file 'src/metadataserver/models/tests/test_noderesults.py'
2430--- src/metadataserver/models/tests/test_noderesults.py 2014-10-29 22:37:02 +0000
2431+++ src/metadataserver/models/tests/test_noderesults.py 2014-11-06 01:24:10 +0000
2432@@ -35,6 +35,7 @@
2433
2434 from fixtures import FakeLogger
2435 from maasserver.fields import MAC
2436+from maasserver.models import Config
2437 from maasserver.models.tag import Tag
2438 from maasserver.testing.factory import factory
2439 from maasserver.testing.orm import reload_object
2440@@ -55,7 +56,10 @@
2441 )
2442 from metadataserver.models.commissioningscript import (
2443 ARCHIVE_PREFIX,
2444+ BUILTIN_COMMISSIONING_SCRIPTS,
2445+ DHCP_UNCONFIGURED_INTERFACES_NAME,
2446 extract_router_mac_addresses,
2447+ get_builtin_commissioning_scripts,
2448 inject_lldp_result,
2449 inject_lshw_result,
2450 inject_result,
2451@@ -676,3 +680,26 @@
2452 logger = self.useFixture(FakeLogger(name='commissioningscript'))
2453 update_hardware_details(factory.make_Node(), b"garbage", exit_status=1)
2454 self.assertEqual("", logger.output)
2455+
2456+
2457+class TestGetBuiltinCommissioningScripts(MAASServerTestCase):
2458+
2459+ def test__includes_all_builtin_commissioning_scripts_by_default(self):
2460+ self.assertItemsEqual(
2461+ BUILTIN_COMMISSIONING_SCRIPTS,
2462+ get_builtin_commissioning_scripts(),
2463+ )
2464+
2465+ def test__excludes_dhcp_discovery_when_disabled(self):
2466+ Config.objects.set_config(
2467+ 'enable_dhcp_discovery_on_unconfigured_interfaces', False)
2468+ self.assertNotIn(
2469+ DHCP_UNCONFIGURED_INTERFACES_NAME,
2470+ get_builtin_commissioning_scripts())
2471+
2472+ def test__includes_dhcp_discovery_when_enabled(self):
2473+ Config.objects.set_config(
2474+ 'enable_dhcp_discovery_on_unconfigured_interfaces', True)
2475+ self.assertIn(
2476+ DHCP_UNCONFIGURED_INTERFACES_NAME,
2477+ get_builtin_commissioning_scripts())
2478
2479=== modified file 'src/metadataserver/tests/test_api.py'
2480--- src/metadataserver/tests/test_api.py 2014-11-04 07:49:41 +0000
2481+++ src/metadataserver/tests/test_api.py 2014-11-06 01:24:10 +0000
2482@@ -812,6 +812,21 @@
2483 node = reload_object(node)
2484 self.assertEqual(0, len(node.tags.all()))
2485
2486+ def test_signal_current_power_type_mscm_does_not_store_params(self):
2487+ node = factory.make_Node(
2488+ power_type="mscm", status=NODE_STATUS.COMMISSIONING)
2489+ client = make_node_client(node=node)
2490+ params = dict(
2491+ power_address=factory.make_string(),
2492+ power_user=factory.make_string(),
2493+ power_pass=factory.make_string())
2494+ response = call_signal(
2495+ client, power_type="moonshot", power_parameters=json.dumps(params))
2496+ self.assertEqual(httplib.OK, response.status_code, response.content)
2497+ node = reload_object(node)
2498+ self.assertEqual("mscm", node.power_type)
2499+ self.assertNotEqual(params, node.power_parameters)
2500+
2501 def test_signal_refuses_bad_power_type(self):
2502 node = factory.make_Node(status=NODE_STATUS.COMMISSIONING)
2503 client = make_node_client(node=node)
2504
2505=== modified file 'src/provisioningserver/rpc/boot_images.py'
2506--- src/provisioningserver/rpc/boot_images.py 2014-10-30 13:17:51 +0000
2507+++ src/provisioningserver/rpc/boot_images.py 2014-11-06 01:24:10 +0000
2508@@ -18,8 +18,12 @@
2509 "is_import_boot_images_running",
2510 ]
2511
2512+<<<<<<< TREE
2513 import os
2514 from urlparse import urlparse
2515+=======
2516+from urlparse import urlparse
2517+>>>>>>> MERGE-SOURCE
2518
2519 from provisioningserver import concurrency
2520 from provisioningserver.auth import get_maas_user_gpghome
2521@@ -47,6 +51,16 @@
2522 return hosts
2523
2524
2525+def get_hosts_from_sources(sources):
2526+ """Return set of hosts that are contained in the given sources."""
2527+ hosts = set()
2528+ for source in sources:
2529+ url = urlparse(source['url'])
2530+ if url.hostname is not None:
2531+ hosts.add(url.hostname)
2532+ return hosts
2533+
2534+
2535 @synchronous
2536 def _run_import(sources, http_proxy=None, https_proxy=None):
2537 """Run the import.
2538
2539=== modified file 'src/provisioningserver/rpc/clusterservice.py'
2540=== modified file 'src/provisioningserver/rpc/power.py'
2541=== modified file 'src/provisioningserver/rpc/tests/test_boot_images.py'
2542--- src/provisioningserver/rpc/tests/test_boot_images.py 2014-10-30 13:17:51 +0000
2543+++ src/provisioningserver/rpc/tests/test_boot_images.py 2014-11-06 01:24:10 +0000
2544@@ -76,6 +76,13 @@
2545 self.assertItemsEqual(hosts, get_hosts_from_sources(sources))
2546
2547
2548+class TestGetHostsFromSources(PservTestCase):
2549+
2550+ def test__returns_set_of_hosts_from_sources(self):
2551+ sources, hosts = make_sources()
2552+ self.assertItemsEqual(hosts, get_hosts_from_sources(sources))
2553+
2554+
2555 class TestRunImport(PservTestCase):
2556
2557 def make_archive_url(self, name=None):
2558
2559=== modified file 'src/provisioningserver/rpc/tests/test_clusterservice.py'
2560=== modified file 'src/provisioningserver/rpc/tests/test_power.py'