Merge ~mpontillo/maas:preseed-per-interface-routes--bug-1758919--2.3 into maas:master

Proposed by Mike Pontillo
Status: Superseded
Proposed branch: ~mpontillo/maas:preseed-per-interface-routes--bug-1758919--2.3
Merge into: maas:master
Diff against target: 1785 lines (+1501/-2) (has conflicts)
17 files modified
debian/changelog (+10/-0)
snap/snapcraft.yaml (+9/-0)
src/maasserver/bootsources.py (+3/-0)
src/maasserver/models/tests/test_userprofile.py (+3/-0)
src/maasserver/rpc/boot.py (+5/-0)
src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js (+17/-0)
src/maasserver/static/partials/ipranges.html (+17/-0)
src/maasserver/static/partials/node-details.html (+134/-0)
src/maasserver/static/partials/nodes-list.html (+1017/-0)
src/maasserver/static/partials/script-results-list.html (+76/-0)
src/maasserver/tests/test_bootsources.py (+3/-0)
src/maasserver/triggers/tests/test_websocket_listener.py (+5/-0)
src/provisioningserver/import_images/boot_resources.py (+61/-0)
src/provisioningserver/import_images/tests/test_boot_resources.py (+92/-0)
src/provisioningserver/import_images/tests/test_download_resources.py (+35/-0)
src/provisioningserver/utils/tests/test_network.py (+3/-0)
utilities/release-build (+11/-2)
Conflict in debian/changelog
Conflict in snap/snapcraft.yaml
Conflict in src/maasserver/bootsources.py
Conflict in src/maasserver/models/tests/test_userprofile.py
Conflict in src/maasserver/rpc/boot.py
Conflict in src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js
Conflict in src/maasserver/static/partials/ipranges.html
Conflict in src/maasserver/static/partials/node-details.html
Conflict in src/maasserver/static/partials/nodes-list.html
Conflict in src/maasserver/static/partials/script-results-list.html
Conflict in src/maasserver/tests/test_bootsources.py
Conflict in src/maasserver/triggers/tests/test_websocket_listener.py
Conflict in src/provisioningserver/import_images/boot_resources.py
Conflict in src/provisioningserver/import_images/tests/test_boot_resources.py
Conflict in src/provisioningserver/import_images/tests/test_download_resources.py
Conflict in src/provisioningserver/utils/tests/test_network.py
Conflict in utilities/release-build
Reviewer Review Type Date Requested Status
MAAS Maintainers Pending
Review via email: mp+342242@code.launchpad.net

Commit message

LP: #1758919 - Move routes to interface context within preseed.

Backports: 9fbf7eb682c3c3ff1de08b33bb967fffdf4a61bf

To post a comment you must log in.

Unmerged commits

434ff94... by Mike Pontillo

LP: #1758919 - Move routes to interface context within preseed.

Backports: 9fbf7eb682c3c3ff1de08b33bb967fffdf4a61bf

b830483... by Newell Jensen

Backport of 611eaccf792a388fc7b5d8e242050228a6bc0e57

LP: #1753874 -- Fix typo in virsh pod driver for get_pod_pool_size_map.

a318f7c... by Newell Jensen

Backport 476dcf44f1370625d5c5637d3b459cdd6511db0c

Convert number to exponential representation in RSD Pod driver.

b3b05a9... by Andres Rodriguez

Update to reflect 2.3.1 release

036d646... by Andres Rodriguez

Clean-up build scripts

8289431... by Andres Rodriguez

debian/changelog: Update in preparation for 2.3.1 release.

d5c58de... by Lee Trager

LP: #1751946 - Only cleanup ScriptSets of the same type.

Backport 1f9751aec9041dc079b83db4e9e81485cd188ef6 from master

5a7cb12... by Newell Jensen

Backport of c5a810187578776c9dacf386d2628f23678a6aa2

LP: #1741165 -- Remove --delete-snapshots flag to workaround volumes not being deleted for node decomposition.

8442748... by Andres Rodriguez

LP: #1711203 - Use grub instead of shim to workaround issues with Secure Boot deployment

Backport 1223c03c6ae23967561ec7218344daaf3a95698d from master

d5bd3b1... by Lee Trager

Backport 02b1bf4 - LP: #1750160 - Confirm running tests on deployed hardware in the UI.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/debian/changelog b/debian/changelog
index 09263b4..ab17966 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,4 @@
1<<<<<<< debian/changelog
1maas (2.4.0~beta1-0ubuntu1) UNRELEASED; urgency=medium2maas (2.4.0~beta1-0ubuntu1) UNRELEASED; urgency=medium
23
3 * UNRELEASED4 * UNRELEASED
@@ -28,6 +29,15 @@ maas (2.4.0~alpha1-6573-g12ee2331b-0ubuntu1) bionic; urgency=medium
28 -- Andres Rodriguez <andreserl@ubuntu.com> Fri, 09 Feb 2018 18:50:10 -050029 -- Andres Rodriguez <andreserl@ubuntu.com> Fri, 09 Feb 2018 18:50:10 -0500
2930
30maas (2.3.0-6434-gd354690-0ubuntu1) bionic; urgency=medium31maas (2.3.0-6434-gd354690-0ubuntu1) bionic; urgency=medium
32=======
33maas (2.3.1-6470-g036d646-0ubuntu1) artful; urgency=medium
34
35 * New upstream release, MAAS 2.3.1
36
37 -- Andres Rodriguez <andreserl@ubuntu.com> Mon, 05 Mar 2018 10:25:44 -0500
38
39maas (2.3.0-6434-gd354690-0ubuntu1) artful; urgency=medium
40>>>>>>> debian/changelog
3141
32 * New upstream release, MAAS 2.3.0:42 * New upstream release, MAAS 2.3.0:
33 - Add support for CentOS & Windows networking.43 - Add support for CentOS & Windows networking.
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 4c39956..3b31301 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -133,6 +133,15 @@ parts:
133 source: src/maasserver/static133 source: src/maasserver/static
134 organize:134 organize:
135 '*': usr/share/maas/web/static/135 '*': usr/share/maas/web/static/
136<<<<<<< snap/snapcraft.yaml
137=======
138 twisted-plugins:
139 plugin: dump
140 source: twisted/plugins
141 organize:
142 maasrackd.py: usr/lib/python3/dist-packages/twisted/plugins/maasrackd.py
143 maasregiond.py: usr/lib/python3/dist-packages/twisted/plugins/maasregiond.py
144>>>>>>> snap/snapcraft.yaml
136 snap:145 snap:
137 plugin: dump146 plugin: dump
138 source: snap147 source: snap
diff --git a/src/maasserver/bootsources.py b/src/maasserver/bootsources.py
index f71040f..1e11784 100644
--- a/src/maasserver/bootsources.py
+++ b/src/maasserver/bootsources.py
@@ -26,7 +26,10 @@ from maasserver.models import (
26 Config,26 Config,
27 Notification,27 Notification,
28)28)
29<<<<<<< src/maasserver/bootsources.py
29from maasserver.models.timestampedmodel import now30from maasserver.models.timestampedmodel import now
31=======
32>>>>>>> src/maasserver/bootsources.py
30from maasserver.utils import get_maas_user_agent33from maasserver.utils import get_maas_user_agent
31from maasserver.utils.orm import transactional34from maasserver.utils.orm import transactional
32from maasserver.utils.threads import deferToDatabase35from maasserver.utils.threads import deferToDatabase
diff --git a/src/maasserver/models/tests/test_userprofile.py b/src/maasserver/models/tests/test_userprofile.py
index 8a0efac..4bf4730 100644
--- a/src/maasserver/models/tests/test_userprofile.py
+++ b/src/maasserver/models/tests/test_userprofile.py
@@ -139,6 +139,7 @@ class UserProfileTest(MAASServerTestCase):
139 self.assertEqual(reload_object(node).owner, new_user)139 self.assertEqual(reload_object(node).owner, new_user)
140 self.assertEqual(reload_object(ipaddress).user, new_user)140 self.assertEqual(reload_object(ipaddress).user, new_user)
141 self.assertEqual(reload_object(iprange).user, new_user)141 self.assertEqual(reload_object(iprange).user, new_user)
142<<<<<<< src/maasserver/models/tests/test_userprofile.py
142143
143 def test_transfer_resources_missing_target_access(self):144 def test_transfer_resources_missing_target_access(self):
144 user = factory.make_User()145 user = factory.make_User()
@@ -153,6 +154,8 @@ class UserProfileTest(MAASServerTestCase):
153 " resource pool(s)")154 " resource pool(s)")
154 # owner didn't change155 # owner didn't change
155 self.assertEqual(reload_object(node).owner, user)156 self.assertEqual(reload_object(node).owner, user)
157=======
158>>>>>>> src/maasserver/models/tests/test_userprofile.py
156159
157 def test_manager_all_users(self):160 def test_manager_all_users(self):
158 users = set(factory.make_User() for _ in range(3))161 users = set(factory.make_User() for _ in range(3))
diff --git a/src/maasserver/rpc/boot.py b/src/maasserver/rpc/boot.py
index 5b5f1de..9a51bf3 100644
--- a/src/maasserver/rpc/boot.py
+++ b/src/maasserver/rpc/boot.py
@@ -280,10 +280,15 @@ def get_config(
280 osystem = configs['commissioning_osystem']280 osystem = configs['commissioning_osystem']
281 series = configs['commissioning_distro_series']281 series = configs['commissioning_distro_series']
282 else:282 else:
283<<<<<<< src/maasserver/rpc/boot.py
283 osystem = machine.get_osystem(284 osystem = machine.get_osystem(
284 default=configs['default_osystem'])285 default=configs['default_osystem'])
285 series = machine.get_distro_series(286 series = machine.get_distro_series(
286 default=configs['default_distro_series'])287 default=configs['default_distro_series'])
288=======
289 osystem = machine.get_osystem()
290 series = machine.get_distro_series()
291>>>>>>> src/maasserver/rpc/boot.py
287 # XXX: roaksoax LP: #1739761 - Since the switch to squashfs (and292 # XXX: roaksoax LP: #1739761 - Since the switch to squashfs (and
288 # drop of iscsi), precise is no longer deployable. To address a293 # drop of iscsi), precise is no longer deployable. To address a
289 # squashfs image is made available allowing it to be deployed in294 # squashfs image is made available allowing it to be deployed in
diff --git a/src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js b/src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js
index 0a48138..8f8743a 100644
--- a/src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js
+++ b/src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js
@@ -1580,6 +1580,7 @@ describe("NodesListController", function() {
1580 it("sets showing_confirmation with testOptions",1580 it("sets showing_confirmation with testOptions",
1581 function() {1581 function() {
1582 var controller = makeController();1582 var controller = makeController();
1583<<<<<<< src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js
1583 var object = makeObject("machines");1584 var object = makeObject("machines");
1584 object.status_code = 6;1585 object.status_code = 6;
1585 var spy = spyOn(1586 var spy = spyOn(
@@ -1594,6 +1595,22 @@ describe("NodesListController", function() {
1594 true);1595 true);
1595 expect($scope.tabs[1596 expect($scope.tabs[
1596 "machines"].actionProgress.affected_nodes).toBe(1);1597 "machines"].actionProgress.affected_nodes).toBe(1);
1598=======
1599 var object = makeObject("nodes");
1600 object.status_code = 6;
1601 var spy = spyOn(
1602 $scope.tabs.nodes.manager,
1603 "performAction").and.returnValue(
1604 $q.defer().promise);
1605 $scope.tabs.nodes.actionOption = { name: "test" };
1606 $scope.tabs.nodes.selectedItems = [object];
1607 $scope.actionGo("nodes");
1608 expect($scope.tabs[
1609 "nodes"].actionProgress.showing_confirmation).toBe(
1610 true);
1611 expect($scope.tabs[
1612 "nodes"].actionProgress.affected_nodes).toBe(1);
1613>>>>>>> src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js
1597 expect(spy).not.toHaveBeenCalled();1614 expect(spy).not.toHaveBeenCalled();
1598 });1615 });
15991616
diff --git a/src/maasserver/static/partials/ipranges.html b/src/maasserver/static/partials/ipranges.html
index 897d959..3d48a68 100755
--- a/src/maasserver/static/partials/ipranges.html
+++ b/src/maasserver/static/partials/ipranges.html
@@ -15,6 +15,7 @@
15 <tbody>15 <tbody>
16 <tr data-ng-repeat="iprange in (subnetIPRanges = ipranges | filterBySubnetOrVlan:subnet:vlan) | orderBy:ipRangeSort"16 <tr data-ng-repeat="iprange in (subnetIPRanges = ipranges | filterBySubnetOrVlan:subnet:vlan) | orderBy:ipRangeSort"
17 data-ng-class="{ 'is-active': isIPRangeInEditMode(iprange) || isIPRangeInDeleteMode(iprange)}">17 data-ng-class="{ 'is-active': isIPRangeInEditMode(iprange) || isIPRangeInDeleteMode(iprange)}">
18<<<<<<< src/maasserver/static/partials/ipranges.html
18 <td class="col-2" aria-label="Start IP Address">{$ iprange.start_ip $}</td>19 <td class="col-2" aria-label="Start IP Address">{$ iprange.start_ip $}</td>
19 <td class="col-2" aria-label="End IP Address">{$ iprange.end_ip $}</td>20 <td class="col-2" aria-label="End IP Address">{$ iprange.end_ip $}</td>
20 <td class="col-1" aria-label="Owner">{$ iprange.type == "dynamic" ? "MAAS" : iprange.user $}</td>21 <td class="col-1" aria-label="Owner">{$ iprange.type == "dynamic" ? "MAAS" : iprange.user $}</td>
@@ -26,6 +27,22 @@
26 data-ng-click="toggleMenu(); ipRangeToggleEditMode(iprange)">Edit</button>27 data-ng-click="toggleMenu(); ipRangeToggleEditMode(iprange)">Edit</button>
27 <button class="table__controls-action u-text--error" aria-label="Remove range"28 <button class="table__controls-action u-text--error" aria-label="Remove range"
28 data-ng-click="toggleMenu(); ipRangeEnterDeleteMode(iprange)">Remove</button>29 data-ng-click="toggleMenu(); ipRangeEnterDeleteMode(iprange)">Remove</button>
30=======
31 <div class="table__data table-col--20" aria-label="Start IP Address">{$ iprange.start_ip $}</div>
32 <div class="table__data table-col--20" aria-label="End IP Address">{$ iprange.end_ip $}</div>
33 <div class="table__data table-col--10" aria-label="Owner">{$ iprange.type == "dynamic" ? "MAAS" : iprange.user $}</div>
34 <div class="table__data table-col--10" aria-label="Type">{$ iprange.type == "dynamic" ? "Dynamic" : "Reserved" $}</div>
35 <div class="table__data table-col--31" aria-label="Comment">{$ iprange.type == "dynamic" ? "Dynamic" : iprange.comment $}</div>
36 <div class="table__data table-col--9 table--mobile-controls">
37 <div class="table__controls" toggle-ctrl data-ng-if="ipRangeCanBeModified(iprange)">
38 <button class="table__controls-toggle" data-ng-click="toggleMenu()">View actions</button>
39 <div class="table__controls-menu" role="menu" data-ng-show="isToggled">
40 <button class="table__controls-action" aria-label="Edit row"
41 data-ng-click="toggleMenu(); ipRangeToggleEditMode(iprange)">Edit reserved range</button>
42 <button class="table__controls-action u-text--error" aria-label="Remove"
43 data-ng-click="toggleMenu(); ipRangeEnterDeleteMode(iprange)">Remove range</button>
44 </div>
45>>>>>>> src/maasserver/static/partials/ipranges.html
29 </div>46 </div>
30 </td>47 </td>
31 <td class="is-active p-table-expanding__panel col-12" col-span="6" data-ng-if="isIPRangeInDeleteMode(iprange)">48 <td class="is-active p-table-expanding__panel col-12" col-span="6" data-ng-if="isIPRangeInDeleteMode(iprange)">
diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html
index 33688a5..6f4de0f 100755
--- a/src/maasserver/static/partials/node-details.html
+++ b/src/maasserver/static/partials/node-details.html
@@ -98,6 +98,7 @@
98 </ul>98 </ul>
99 </div>99 </div>
100100
101<<<<<<< src/maasserver/static/partials/node-details.html
101 <div class="col-8" data-ng-show="action.option.name === 'deploy' || action.option.name === 'release'">102 <div class="col-8" data-ng-show="action.option.name === 'deploy' || action.option.name === 'release'">
102 <div class="ng-hide" data-ng-show="action.option.name === 'deploy'">103 <div class="ng-hide" data-ng-show="action.option.name === 'deploy'">
103 <div class="p-form__group">104 <div class="p-form__group">
@@ -236,6 +237,139 @@
236 </div>237 </div>
237 </div>238 </div>
238 </div>239 </div>
240=======
241 <!-- XXX blake_r 2015-02-19 - Need to add e2e test. -->
242 <div class="page-header__dropdown" data-ng-class="{ 'is-open': action.option }">
243 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-if="!isDevice && !node.dhcp_on">
244 <p class="page-header__message page-header__message--warning">MAAS is not providing DHCP.</p>
245 </div>
246
247 <!-- XXX blake_r 2015-02-19 - Need to add e2e test. -->
248 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name)">
249 <form class="form form--inline u-display--inline">
250 <fieldset class="form__fieldset ng-hide" data-ng-show="action.option.name === 'commission' || action.option.name === 'test'">
251 <div class="form__group">
252 <input class="checkbox" id="enableSSH" type="checkbox"
253 data-ng-model="commissionOptions.enableSSH">
254 <label for="enableSSH">Allow SSH access and prevent machine from powering off</label>
255 </div>
256 </fieldset>
257 <fieldset class="form__fieldset ng-hide" data-ng-show="action.option.name === 'commission'">
258 <div class="form__group">
259 <input class="checkbox" id="skipNetworking" type="checkbox"
260 data-ng-model="commissionOptions.skipNetworking">
261 <label for="skipNetworking">Retain network configuration</label>
262 </div>
263 <div class="form__group">
264 <input class="checkbox" id="skipStorage" type="checkbox"
265 data-ng-model="commissionOptions.skipStorage">
266 <label for="skipStorage">Retain storage configuration</label>
267 </div>
268 </fieldset>
269 <fieldset class="form__fieldset ng-hide" data-ng-show="action.option.name === 'deploy'">
270 <div class="form__group">
271 <label class="form__group-label">Choose your image</label>
272 <div class="form__group-input" data-maas-os-select="osinfo" data-ng-model="osSelection"></div>
273 </div>
274 </fieldset>
275 <fieldset class="form__fieldset" data-ng-if="action.option.name === 'release'">
276 <div class="form__group">
277 <div data-maas-release-options="releaseOptions"></div>
278 </div>
279 </fieldset>
280 <div class="page-header__controls" data-ng-if="action.option.name !== 'commission' && action.option.name !== 'test'">
281 <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button>
282 <button class="button--inline" data-ng-class="action.option.name === 'delete' ? 'button--destructive' : 'button--positive'" data-ng-click="actionGo('nodes')" data-ng-hide="hasActionsFailed('nodes')">
283 <span data-ng-if="action.option.name === 'acquire'">Acquire {$ type_name $}</span>
284 <span data-ng-if="action.option.name === 'deploy'">Deploy {$ type_name $}</span>
285 <span data-ng-if="action.option.name === 'release'">Release {$ type_name $}</span>
286 <span data-ng-if="action.option.name === 'set-zone'">Set zone for {$ type_name $}</span>
287 <span data-ng-if="action.option.name === 'on'">Power on {$ type_name $}</span>
288 <span data-ng-if="action.option.name === 'off'">Power off {$ type_name $}</span>
289 <span data-ng-if="action.option.name === 'abort'">Abort action on {$ type_name $}</span>
290 <span data-ng-if="action.option.name === 'rescue-mode'">Rescue {$ type_name $}</span>
291 <span data-ng-if="action.option.name === 'exit-rescue-mode'">Exit rescue mode</span>
292 <span data-ng-if="action.option.name === 'mark-broken'">Mark {$ type_name $}</span>
293 <span data-ng-if="action.option.name === 'mark-fixed'">Mark {$ type_name $}</span>
294 <span data-ng-if="action.option.name === 'override-failed-testing'">Override failed testing</span>
295 <span data-ng-if="action.option.name === 'delete'">Delete {$ type_name $}</span>
296 <span data-ng-if="action.option.name === 'import-images'">Import images</span>
297 </button>
298 </div>
299 </form>
300 </div>
301 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name) || action.option.name !== 'commission'" data-ng-if="hasCustomCommissioningScripts()">
302 <form class="form form--stack">
303 <fieldset class="form__fieldset eight-col u-margin--bottom-small">
304 <div class="form__group">
305 <label for="commissioning-scripts">Additional commissioning scripts</label>
306 <span id="commissioning-scripts" data-maas-script-select="script" data-script-type="0" data-ng-model="commissioningSelection" class="tags--inline"></span>
307 </div>
308 </fieldset>
309 </form>
310 </div>
311 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name) || (action.option.name !== 'commission' && action.option.name !== 'test')">
312 <form class="form form--stack">
313 <fieldset class="form__fieldset eight-col u-margin--bottom-small">
314 <div class="form__group">
315 <label>Hardware tests</label>
316 <span id="testing-scripts" data-maas-script-select="script" data-script-type="2" data-ng-model="testSelection" class="tags--inline"></span>
317 </div>
318 </fieldset>
319 </form>
320 </div>
321 <div class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name)" data-ng-if="action.option.name === 'commission' || action.option.name === 'test'">
322 <form class="form form--inline">
323 <div class="page-header__controls">
324 <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button>
325 <button class="button--inline" data-ng-class="action.option.name === 'delete' ? 'button--destructive' : 'button--positive'" data-ng-click="actionGo('nodes')" data-ng-hide="hasActionsFailed('nodes')">
326 <span data-ng-if="action.option.name === 'commission'">Commission {$ type_name $}</span>
327 <span data-ng-if="action.option.name === 'test'">Test {$ type_name $}</span>
328 </button>
329 </div>
330 </form>
331 </div>
332 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="action.showing_confirmation && action.option.name === 'test'">
333 <p class="page-header__message page-header__message--warning">
334 Node is currently deployed. Are you sure you want to continue to test hardware?
335 </p>
336 <div class="page-header__controls">
337 <button class="button--base button--inline" data-ng-click="actionCancel()">No</button>
338 <button class="button--secondary button--inline" data-ng-click="actionGo()">Yes</button>
339 </div>
340 </div>
341 <!-- XXX blake_r 2015-02-19 - Need to add e2e test. -->
342 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isActionError()">
343 <p class="page-header__message page-header__message--error">
344 Node failed to be {$ action.option.sentence $}, because of the following error: {$ action.error $}
345 </p>
346 <div class="page-header__controls">
347 <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button>
348 <button class="button--secondary button--inline" data-ng-click="actionGo()">Retry</button>
349 </div>
350 </div>
351
352 <!-- XXX blake_r 2015-02-19 - Need to add e2e test. -->
353 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isDeployError()">
354 <p class="page-header__message page-header__message--error">
355 Node cannot be {$ action.option.sentence $}, because the required boot images have not been imported. To import boot images, visit the <a href="images/">images page</a>.
356 </p>
357 <div class="page-header__controls">
358 <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button>
359 </div>
360 </div>
361
362 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="hasActionPowerError(action.option.name)">
363 <p class="page-header__message page-header__message--error">
364 Node cannot be {$ action.option.sentence $}, because power control software for the
365 node is missing from its rack controller. To proceed, install the
366 {$ getPowerErrors() $} on the rack controller.
367 </p>
368 <div class="page-header__controls">
369 <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button>
370 </div>
371 </div>
372>>>>>>> src/maasserver/static/partials/node-details.html
239373
240 <nav class="p-tabs">374 <nav class="p-tabs">
241 <ul class="p-tabs__list" role="tablist" data-ng-class="{ 'u-hide': action.option }">375 <ul class="p-tabs__list" role="tablist" data-ng-class="{ 'u-hide': action.option }">
diff --git a/src/maasserver/static/partials/nodes-list.html b/src/maasserver/static/partials/nodes-list.html
index 8952e21..71f4b64 100644
--- a/src/maasserver/static/partials/nodes-list.html
+++ b/src/maasserver/static/partials/nodes-list.html
@@ -1,3 +1,4 @@
1<<<<<<< src/maasserver/static/partials/nodes-list.html
1<header class="p-strip--light is-shallow is-bordered page-header" media-query="min-width: 769px">2<header class="p-strip--light is-shallow is-bordered page-header" media-query="min-width: 769px">
2 <div class="row">3 <div class="row">
3 <div class="col-8">4 <div class="col-8">
@@ -1047,3 +1048,1019 @@ sudo maas-rack register --url {$ tabs.controllers.registerUrl $} --secret {$ tab
1047 on-check-all="toggleCheckAll('switches')" on-check="toggleChecked($switch_, 'switches')"></maas-switches-table>1048 on-check-all="toggleCheckAll('switches')" on-check="toggleChecked($switch_, 'switches')"></maas-switches-table>
1048 </div>1049 </div>
1049</div>1050</div>
1051=======
1052<header class="page-header u-margin--bottom-none" sticky media-query="min-width: 769px">
1053 <div class="wrapper--inner">
1054 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1055 <h1 class="page-header__title">Nodes</h1>
1056
1057 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1058 <p class="page-header__status" data-ng-show="loading"><span class="u-text--loading"><i class="icon icon--loading u-animation--spin"></i> Loading...</span></p>
1059
1060 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1061 <div class="page-header__controls u-float--right ng-hide" data-ng-show="currentpage === 'nodes'">
1062 <div data-ng-hide="tabs.nodes.selectedItems.length">
1063 <div data-maas-cta="addHardwareOptions"
1064 data-ng-model="addHardwareOption"
1065 data-ng-change="addHardwareOptionChanged()" data-default-title="Add hardware">
1066 </div>
1067 </div>
1068 <div class="ng-hide" data-ng-show="tabs.nodes.selectedItems.length">
1069 <a class="u-display--inline u-margin--right" data-ng-click="showSelected('nodes')">{$ tabs.nodes.selectedItems.length $} Selected</a>
1070 <div data-maas-cta="tabs.nodes.takeActionOptions"
1071 data-ng-model="tabs.nodes.actionOption"
1072 data-ng-change="actionOptionSelected('nodes')">
1073 </div>
1074 </div>
1075 </div>
1076
1077 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1078 <div class="page-header__controls u-float--rightng-hide" data-ng-show="currentpage === 'devices'">
1079 <div data-ng-hide="tabs.devices.selectedItems.length">
1080 <button class="button--secondary button--inline"
1081 data-ng-click="addDevice()"
1082 data-ng-hide="addDeviceScope.viewable">Add device</button>
1083 <button class="button--secondary button--inline ng-hide"
1084 data-ng-click="cancelAddDevice()"
1085 data-ng-show="addDeviceScope.viewable">Cancel add device</button>
1086 </div>
1087 <div data-ng-show="tabs.devices.selectedItems.length">
1088 <a class="u-display--inline u-margin--right" data-ng-click="showSelected('devices')">{$ tabs.devices.selectedItems.length $} Selected</a>
1089 <div data-maas-cta="tabs.devices.takeActionOptions"
1090 data-ng-model="tabs.devices.actionOption"
1091 data-ng-change="actionOptionSelected('devices')">
1092 </div>
1093 </div>
1094 </div>
1095
1096 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1097 <div class="page-header__controls u-float--rightng-hide" data-ng-show="currentpage === 'controllers'">
1098 <div data-ng-if="!tabs.controllers.selectedItems.length">
1099 <button class="button--secondary button--inline"
1100 data-ng-click="tabs.controllers.addController = true"
1101 data-ng-hide="tabs.controllers.addController">Add rack controller</button>
1102 <button class="button--secondary button--inline ng-hide"
1103 data-ng-click="tabs.controllers.addController = false"
1104 data-ng-show="tabs.controllers.addController">Close add rack controller</button>
1105 </div>
1106 <div data-ng-show="tabs.controllers.selectedItems.length">
1107 <a class="u-display--inline u-margin--right" data-ng-click="showSelected('controllers')">{$ tabs.controllers.selectedItems.length $} Selected</a>
1108 <div data-maas-cta="tabs.controllers.takeActionOptions"
1109 data-ng-model="tabs.controllers.actionOption"
1110 data-ng-change="actionOptionSelected('controllers')">
1111 </div>
1112 </div>
1113 </div>
1114
1115 <div class="page-header__controls u-float--right ng-hide" data-ng-show="currentpage === 'switches'">
1116 <div class="ng-hide" data-ng-show="tabs.switches.selectedItems.length">
1117 <a class="u-display--inline u-margin--right" data-ng-click="showSelected('switches')">{$ tabs.switches.selectedItems.length $} Selected</a>
1118 <div data-maas-cta="tabs.switches.takeActionOptions"
1119 data-ng-model="tabs.switches.actionOption"
1120 data-ng-change="actionOptionSelected('switches')">
1121 </div>
1122 </div>
1123 </div>
1124
1125 <div data-ng-repeat="tab in ['nodes', 'switches']" data-ng-show="currentpage == tab">
1126 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1127 <div class="page-header__dropdown" data-ng-class="{ 'is-open': tabs[tab].actionOption }">
1128 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1129 <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="isActionError(tab) || hasActionsInProgress(tab)">
1130 <form class="form form--inline u-display--inline">
1131 <fieldset class="form__fieldset ng-hide" data-ng-show="tabs[tab].actionOption.name === 'commission' || tabs[tab].actionOption.name === 'test'">
1132 <div class="form__group">
1133 <input class="form__group-label" id="{$ tab $}-enableSSH" type="checkbox"
1134 data-ng-model="tabs[tab].commissionOptions.enableSSH">
1135 <label class="checkbox-label" for="{$ tab $}-enableSSH">Allow SSH access and prevent machine from powering off</label>
1136 </div>
1137 </fieldset>
1138 <fieldset class="form__fieldset ng-hide" data-ng-show="tabs[tab].actionOption.name === 'commission'">
1139 <div class="form__group">
1140 <input class="form__group-label" id="{$ tab $}-skipNetworking" type="checkbox"
1141 data-ng-model="tabs[tab].commissionOptions.skipNetworking">
1142 <label class="checkbox-label" for="{$ tab $}-skipNetworking">Retain network configuration</label>
1143 </div>
1144 <div class="form__group">
1145 <input class="form__group-label" id="{$ tab $}-skipStorage" type="checkbox"
1146 data-ng-model="tabs[tab].commissionOptions.skipStorage">
1147 <label class="checkbox-label" for="{$ tab $}-skipStorage">Retain storage configuration</label>
1148 </div>
1149 </fieldset>
1150 <fieldset class="form__fieldset ng-hide" data-ng-show="tabs[tab].actionOption.name === 'deploy'">
1151 <div class="form__group">
1152 <label for="image" class="form__group-label">Choose your image</label>
1153 <div class="form__group-input" data-maas-os-select="osinfo" data-ng-model="tabs[tab].osSelection"></div>
1154 </div>
1155 </fieldset>
1156 <fieldset class="form__fieldset" data-ng-if="tabs[tab].actionOption.name === 'release'">
1157 <div class="form__group">
1158 <div data-maas-release-options="tabs[tab].releaseOptions"></div>
1159 </div>
1160 </fieldset>
1161 <fieldset class="form__fieldset ng-hide" data-ng-show="tabs[tab].actionOption.name === 'set-zone'">
1162 <div class="form__group">
1163 <label for="{$ tab $}-zone3" class="form__group-label">Select Zone</label>
1164 <div class="form__group-input">
1165 <select name="zone" id="{$ tab $}-zone3"
1166 data-ng-model="tabs[tab].zoneSelection"
1167 data-ng-options="zone as zone.name for zone in zones">
1168 <option value="" disabled="disabled">Choose a zone</option>
1169 </select>
1170 </div>
1171 </div>
1172 </fieldset>
1173 </form>
1174 <div class="page-header__controls" data-ng-if="tabs[tab].actionOption.name !== 'commission' && tabs[tab].actionOption.name !== 'test'">
1175 <button class="button--base button--inline" data-ng-click="actionCancel(tab)">Cancel</button>
1176 <button class="button--inline" data-ng-class="tabs[tab].actionOption.name === 'delete' ? 'button--destructive' : 'button--positive'" data-ng-click="actionGo(tab)" data-ng-hide="hasActionsFailed(tab)">
1177 <span data-ng-if="tabs[tab].actionOption.name === 'acquire'">Acquire {$ tabs[tab].selectedItems.length $}
1178 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1179 </span>
1180 <span data-ng-if="tabs[tab].actionOption.name === 'deploy'">Deploy {$ tabs[tab].selectedItems.length $}
1181 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1182 </span>
1183 <span data-ng-if="tabs[tab].actionOption.name === 'release'">Release {$ tabs[tab].selectedItems.length $}
1184 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1185 </span>
1186 <span data-ng-if="tabs[tab].actionOption.name === 'set-zone'">Set zone for {$ tabs[tab].selectedItems.length $}
1187 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1188 </span>
1189 <span data-ng-if="tabs[tab].actionOption.name === 'on'">Power on {$ tabs[tab].selectedItems.length $}
1190 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1191 </span>
1192 <span data-ng-if="tabs[tab].actionOption.name === 'off'">Power off {$ tabs[tab].selectedItems.length $}
1193 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1194 </span>
1195 <span data-ng-if="tabs[tab].actionOption.name === 'abort'">Abort action for {$ tabs[tab].selectedItems.length $}
1196 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1197 </span>
1198 <span data-ng-if="tabs[tab].actionOption.name === 'rescue-mode'">Set rescue mode for {$ tabs[tab].selectedItems.length $}
1199 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1200 </span>
1201 <span data-ng-if="tabs[tab].actionOption.name === 'exit-rescue-mode'">Exit rescue mode for {$ tabs[tab].selectedItems.length $}
1202 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1203 </span>
1204 <span data-ng-if="tabs[tab].actionOption.name === 'mark-broken'">Mark {$ tabs[tab].selectedItems.length $}
1205 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> as broken
1206 </span>
1207 <span data-ng-if="tabs[tab].actionOption.name === 'mark-fixed'">Mark {$ tabs[tab].selectedItems.length $}
1208 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> as fixed
1209 </span>
1210 <span data-ng-if="tabs[tab].actionOption.name === 'override-failed-testing'">Override failed testing on {$ tabs[tab].selectedItems.length $}
1211 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1212 </span>
1213 <span data-ng-if="tabs[tab].actionOption.name === 'delete'">Delete {$ tabs[tab].selectedItems.length $}
1214 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1215 </span>
1216 </button>
1217 <button class="button--secondary button--inline" data-ng-click="actionGo(tab)" data-ng-show="hasActionsFailed(tab)">Retry</button>
1218 </div>
1219 </section>
1220 <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="isActionError(tab) || hasActionsInProgress(tab)" data-ng-if="tabs[tab].actionOption.name === 'commission' && hasCustomCommissioningScripts()">
1221 <form class="form form--stack">
1222 <fieldset class="form__fieldset eight-col u-margin--bottom-small">
1223 <div class="form__group">
1224 <label for="{$ tab $}-commissiong-scripts">Additional commissioning Scripts</label>
1225 <span id="{$ tab $}-commissioning-scripts" data-maas-script-select="script" data-script-type="0" data-ng-model="tabs[tab].commissioningSelection" class="tags--inline"></span>
1226 </div>
1227 </fieldset>
1228 </form>
1229 </section>
1230 <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="isActionError(tab) || hasActionsInProgress(tab)" data-ng-if="tabs[tab].actionOption.name === 'commission' || tabs[tab].actionOption.name ==='test'">
1231 <form class="form form--stack">
1232 <fieldset class="form__fieldset eight-col u-margin--bottom-small">
1233 <div class="form__group">
1234 <label for="{$ tab $}-testing-scripts">Hardware Tests</label>
1235 <span id="{$ tab $}-testing-scripts" data-maas-script-select="script" data-script-type="2" data-ng-model="tabs[tab].testSelection" class="tags--inline"></span>
1236 </div>
1237 </fieldset>
1238 </form>
1239 </section>
1240 <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="isActionError(tab) || hasActionsInProgress(tab)" data-ng-if="tabs[tab].actionOption.name === 'commission' || tabs[tab].actionOption.name === 'test'">
1241 <div class="page-header__controls">
1242 <button class="button--base button--inline" data-ng-click="actionCancel(tab)">Cancel</button>
1243 <button class="button--inline" data-ng-class="tabs[tab].actionOption.name === 'delete' ? 'button--destructive' : 'button--positive'" data-ng-click="actionGo(tab)" data-ng-hide="hasActionsFailed(tab)">
1244 <span data-ng-if="tabs[tab].actionOption.name === 'commission'">Commission {$ tabs[tab].selectedItems.length $}
1245 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1246 </span>
1247 <span data-ng-if="tabs[tab].actionOption.name === 'test'">Test {$ tabs[tab].selectedItems.length $}
1248 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
1249 </span>
1250 </button>
1251 <button class="button--secondary button--inline" data-ng-click="actionGo(tab)" data-ng-show="hasActionsFailed(tab)">Retry</button>
1252 </div>
1253 </section>
1254
1255 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1256 <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isActionError(tab)">
1257 <p data-ng-hide="isDeployError(tab) || isSSHKeyError(tab)"
1258 class="page-header__message page-header__message--error ng-hide">
1259 {$ tabs[tab].actionErrorCount $}
1260 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'node', 'other': 'nodes'}"></span>
1261 cannot be {$ tabs[tab].actionOption.sentence $}. To proceed, update your selection.
1262 </p>
1263 <p class="page-header__message page-header__message--error ng-hide" data-ng-show="isDeployError(tab)">
1264 {$ tabs[tab].selectedItems.length $}
1265 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'node', 'other': 'nodes'}"></span>
1266 cannot be {$ tabs[tab].actionOption.sentence $}, because the required boot images have not been imported. To import boot images, visit the <a href="#/images">images page</a>.
1267 </p>
1268 <p class="page-header__message page-header__message--error ng-hide" data-ng-show="isSSHKeyError(tab)">
1269 {$ tabs[tab].selectedItems.length $}
1270 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'node', 'other': 'nodes'}"></span>
1271 cannot be {$ tabs[tab].actionOption.sentence $}, because an SSH key has not been added to your account. To add an SSH key, visit <a href="account/prefs/">your account page</a>.
1272 </p>
1273 </section>
1274 <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="tabs[tab].actionProgress.showing_confirmation">
1275 <p class="page-header__message page-header__message--warning">
1276 {$ tabs[tab].actionProgress.affected_nodes $} of {$ tabs[tab].selectedItems.length $} are in a deployed state. Are you sure you want to continue to test hardware?
1277 </p>
1278 <div class="page-header__controls">
1279 <button class="button--base button--inline" data-ng-click="actionCancel(tab)">No</button>
1280 <button class="button--secondary button--inline" data-ng-click="actionGo(tab)">Yes</button>
1281 </div>
1282 </section>
1283 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1284 <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="hasActionsInProgress(tab) || hasActionsFailed(tab)">
1285 <p class="page-header__message" data-ng-show="hasActionsInProgress(tab)">
1286 <i class="icon icon--loading u-animation--spin"></i>
1287 {$ tabs[tab].actionProgress.completed $} of {$ tabs[tab].actionProgress.total $}
1288 nodes are transitioning to {$ tabs[tab].actionOption.sentence $}.
1289 </p>
1290 <p class="page-header__message page-header__message--error"
1291 data-ng-repeat="(error, nodes) in tabs[tab].actionProgress.errors">
1292 The {$ tabs[tab].actionOption.title.toLowerCase() $} action for {$ nodes.length $}
1293 <span data-ng-pluralize count="nodes.length" when="{'one': 'node', 'other': 'nodes'}"></span>
1294 failed with error: {$ error $}
1295 </p>
1296 </section>
1297 </div>
1298
1299 </div>
1300
1301 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1302 <div class="page-header__dropdown" data-ng-class="{ 'is-open': addHardwareScope.viewable }" data-ng-controller="AddHardwareController">
1303 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1304 <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="showMachine()">
1305 <h3 class="page-header__dropdown-title">Add machine</h3>
1306 <form class="form form--stack">
1307 <fieldset class="form__fieldset six-col">
1308 <div class="form__group">
1309 <label for="machine-name" class="form__label two-col">Machine name</label>
1310 <div class="form__group-input three-col">
1311 <input type="text" id="machine-name" placeholder="Choose a machine name (optional)"
1312 data-ng-model="machine.name">
1313 </div>
1314 </div>
1315 <div class="form__group">
1316 <label for="domain" class="form__group-label two-col">Domain</label>
1317 <div class="form__group-input three-col">
1318 <select name="domain" id="domain"
1319 data-ng-model="machine.domain"
1320 data-ng-options="domain as domain.name for domain in domains">
1321 <option value="" disabled>Choose a domain</option>
1322 </select>
1323 </div>
1324 </div>
1325 <div class="form__group">
1326 <label for="architecture" class="form__group-label two-col">Architecture</label>
1327 <div class="form__group-input three-col">
1328 <select name="architecture" id="architecture"
1329 data-ng-model="machine.architecture"
1330 data-ng-options="arch for arch in architectures">
1331 <option value="" disabled>Choose an architecture</option>
1332 </select>
1333 </div>
1334 </div>
1335 <div class="form__group">
1336 <label for="min_hwe_kernel" class="form__group-label two-col">Minimum Kernel</label>
1337 <div class="form__group-input three-col">
1338 <select name="min_hwe_kernel" id="min_hwe_kernel"
1339 data-ng-model="machine.min_hwe_kernel"
1340 data-ng-options="hwe_kernel[0] as hwe_kernel[1] for hwe_kernel in hwe_kernels">
1341 <option value="">No minimum kernel</option>
1342 </select>
1343 </div>
1344 </div>
1345 <div class="form__group">
1346 <label for="zone4" class="form__group-label two-col">Zone</label>
1347 <select name="zone" id="zone4" class="three-col"
1348 data-ng-model="machine.zone"
1349 data-ng-options="zone as zone.name for zone in zones">
1350 </select>
1351 </div>
1352 <div class="form__group" data-ng-repeat="mac in machine.macs">
1353 <label for="mac-address" class="form__group-label two-col"><span data-ng-hide="mac !== machine.macs[0]">MAC Address</span>&nbsp;</label>
1354 <div class="form__group-input three-col">
1355 <input type="text" id="mac-address" placeholder="00:00:00:00:00:00"
1356 maxlength="17"
1357 data-ng-class="{ 'has-error': mac.error }"
1358 data-ng-model="mac.mac"
1359 data-ng-pattern="macAddressRegex"
1360 data-ng-change="validateMac(mac)"
1361 mac-address>
1362 <span class="form__group-remove" title="Delete MAC"
1363 data-ng-hide="mac === machine.macs[0]" data-ng-click="removeMac(mac)"></span>
1364 </div>
1365 </div>
1366 <div class="form__group">
1367 <div class="five-col">
1368 <button class="button--secondary button--inline u-float--right" data-ng-click="addMac()">+ Add MAC Address</a>
1369 </div>
1370 </div>
1371 </fieldset>
1372 <fieldset class="form__fieldset six-col last-col"
1373 data-maas-power-parameters="power_types"
1374 data-ng-model="machine.power">
1375 </fieldset>
1376 </form>
1377 </section>
1378 <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="showChassis()">
1379 <h3 class="page-header__dropdown-title">Add chassis</h3>
1380 <form action="post" class="form form--stack">
1381 <fieldset class="form__fieldset six-col"
1382 data-maas-power-parameters="chassisPowerTypes"
1383 data-ng-model="chassis.power">
1384 </fieldset>
1385 <fieldset class="form__fieldset six-col last-col">
1386 <div class="form__group">
1387 <label for="domain2" class="form__group-label two-col">Domain</label>
1388 <div class="form__group-input three-col">
1389 <select name="domain" id="domain2"
1390 data-ng-model="chassis.domain"
1391 data-ng-options="domain as domain.name for domain in domains">
1392 </select>
1393 </div>
1394 </div>
1395 </fieldset>
1396 </form>
1397 </section>
1398
1399 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1400 <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-show="showMachine()">
1401 <p class="page-header__message page-header__message--error ng-hide" data-ng-show="error">{$ error $}</p>
1402 <div class="page-header__controls">
1403 <button class="button--base button--inline" data-ng-click="cancel()">Cancel</button>
1404 <button class="button--secondary button--inline"
1405 data-ng-disabled="machineHasError()"
1406 data-ng-click="saveMachine(true)">Save and add another</button>
1407 <button class="button--positive button--inline"
1408 data-ng-disabled="machineHasError()"
1409 data-ng-click="saveMachine(false)">Save machine</button>
1410 </div>
1411 </section>
1412
1413 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1414 <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="showChassis()">
1415 <p class="page-header__message page-header__message--error ng-hide" data-ng-show="error">{$ error $}</p>
1416 <div class="page-header__controls">
1417 <button class="button--base button--inline" data-ng-click="cancel()">Cancel</button>
1418 <button class="button--secondary button--inline"
1419 data-ng-disabled="chassisHasErrors()"
1420 data-ng-click="saveChassis(true)">Save and add another</button>
1421 <button class="button--positive button--inline"
1422 data-ng-disabled="chassisHasErrors()"
1423 data-ng-click="saveChassis(false)">Save chassis</button>
1424 </div>
1425 </section>
1426
1427 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1428 <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="architectures.length">
1429 <p class="page-header__message page-header__message--error">
1430 Cannot add {$ mode $} until boot images have been imported. To fix, visit the <a href="#/images">images page</a>.
1431 </p>
1432 </section>
1433 </div>
1434
1435 <!-- XXX blake_r 2015-04-02 - Need to add e2e test. -->
1436 <div class="page-header__dropdown" data-ng-class="{ 'is-open': tabs.devices.actionOption }">
1437 <!-- XXX blake_r 2015-04-02 - Need to add e2e test. -->
1438 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError('devices') || hasActionsInProgress('devices')">
1439 <!-- XXX blake_r 2015-04-02 - Need to add e2e test. -->
1440 <form class="form form--inline u-display--inline">
1441 <fieldset class="form__fieldset ng-hide" data-ng-show="tabs.devices.actionOption.name === 'set-zone'">
1442 <div class="form__group">
1443 <label class="form__group-label" for="zone1">Set zone</label>
1444 <select name="zone" id="zone1"
1445 data-ng-model="tabs.devices.zoneSelection"
1446 data-ng-options="zone as zone.name for zone in zones">
1447 <option value="" disabled="disabled">Choose a zone</option>
1448 </select>
1449 </div>
1450 </fieldset>
1451 <div class="page-header__controls">
1452 <button class="button--base button--inline" data-ng-click="actionCancel('devices')">Cancel</button>
1453 <button class="button--positive button--inline" data-ng-click="actionGo('devices')">
1454 <span data-ng-if="tabs.devices.actionOption.name === 'set-zone'">Set zone for {$ tabs.devices.selectedItems.length $}
1455 <span data-ng-pluralize count="tabs.devices.selectedItems.length" when="{'one': 'device', 'other': 'devices'}"></span>
1456 </span>
1457 <span data-ng-if="tabs.devices.actionOption.name === 'delete'">Delete {$ tabs.devices.selectedItems.length $}
1458 <span data-ng-pluralize count="tabs.devices.selectedItems.length" when="{'one': 'device', 'other': 'devices'}"></span>
1459 </span>
1460 </button>
1461 </div>
1462 </form>
1463 </div>
1464
1465 <!-- XXX blake_r 2015-04-02 - Need to add e2e test. -->
1466 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isActionError('devices')">
1467 <p class="page-header__message page-header__message--error">
1468 {$ tabs.devices.actionErrorCount $}
1469 <span data-ng-pluralize count="tabs.devices.selectedItems.length" when="{'one': 'device', 'other': 'devices'}"></span>
1470 cannot be {$ tabs.devices.actionOption.sentence $}. To proceed, update your selection.
1471 </p>
1472 </div>
1473
1474 <!-- XXX blake_r 2015-05-07 - Need to add e2e test. -->
1475 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="hasActionsInProgress('devices') || hasActionsFailed('devices')">
1476 <p class="page-header__message" data-ng-show="hasActionsInProgress('devices')">
1477 <i class="icon icon--loading u-animation--spin"></i>
1478 {$ tabs.devices.actionProgress.completed $} of {$ tabs.devices.actionProgress.total $}
1479 devices have been {$ tabs.devices.actionOption.sentence $}.
1480 </p>
1481 <p class="page-header__message page-header__message--error"
1482 data-ng-repeat="(error, devices) in tabs.devices.actionProgress.errors">
1483 The {$ tabs.devices.actionOption.title.toLowerCase() $} action for {$ devices.length $}
1484 <span data-ng-pluralize count="devices.length" when="{'one': 'device', 'other': 'devices'}"></span>
1485 failed with error: {$ error $}
1486 </p>
1487 </div>
1488 </div>
1489
1490 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1491 <div class="page-header__dropdown" data-ng-class="{ 'is-open': addDeviceScope.viewable }" data-ng-controller="AddDeviceController">
1492 <section class="page-header__section twelve-col u-margin--bottom-none">
1493 <h3 class="page-header__dropdown-title">Add device</h3>
1494 <form class="form form--stack">
1495 <fieldset class="form__fieldset six-col">
1496 <div class="form__group">
1497 <label for="device-name" class="form__group-label two-col">Name</label>
1498 <div class="form__group-input three-col">
1499 <input type="text" id="device-name" placeholder="Name your device"
1500 data-ng-model="device.name"
1501 data-ng-class="{ 'has-error': nameHasError() }">
1502 </div>
1503
1504 </div>
1505 <div class="form__group">
1506 <label for="domain3" class="form__group-label two-col">Domain</label>
1507 <div class="form__group-input three-col">
1508 <select name="domain" id="domain3"
1509 data-ng-model="device.domain"
1510 data-ng-options="domain as domain.name for domain in domains">
1511 </select>
1512 </div>
1513 </div>
1514 </fieldset>
1515 </form>
1516 <table>
1517 <thead>
1518 <tr>
1519 <th class="table-col--20">MAC address</th>
1520 <th class="table-col--20">IP assignment</th>
1521 <th class="table-col--20">Subnet</th>
1522 <th class="table-col--35">IP address</th>
1523 <th class="table-col--5"></th>
1524 </tr>
1525 </thead>
1526 <tbody vs-repeat vs-scroll-parent="window">
1527 <tr data-ng-repeat="interface in device.interfaces">
1528 <td class="table-col--20" aria-label="MAC address">
1529 <input type="text" id="mac-address1" placeholder="00:00:00:00:00:00"
1530 data-ng-model="interface.mac"
1531 data-ng-class="{ 'has-error': macHasError(interface) }">
1532 </td>
1533 <td class="table-col--20" aria-label="IP assignment">
1534 <select name="ip-assignment" id="ip-assignment"
1535 data-ng-model="interface.ipAssignment"
1536 data-ng-options="assigment.title for assigment in ipAssignments">
1537 <option value="" disabled selected>Select IP assignment</option>
1538 </select>
1539 </td>
1540 <td class="table-col--20" aria-label="Subnet">
1541 <select name="subnet" id="subnet"
1542 data-ng-model="interface.subnetId"
1543 data-ng-options="subnet.id as subnet.name for subnet in subnets"
1544 data-ng-show="interface.ipAssignment.name === 'static'">
1545 <option value="" disabled selected>Select subnet</option>
1546 </select>
1547 </td>
1548 <td class="table-col--35" aria-label="IP address">
1549 <input type="text" id="ip-address" placeholder="000.000.000.000"
1550 data-ng-model="interface.ipAddress"
1551 data-ng-class="{ 'has-error': ipHasError(interface) }"
1552 data-ng-show="interface.ipAssignment.name === 'external'">
1553 <input type="text" id="ip-address"
1554 data-ng-model="interface.ipAddress"
1555 data-ng-class="{ 'has-error': ipHasError(interface) }"
1556 data-ng-show="interface.ipAssignment.name === 'static'">
1557 </td>
1558 <td class="table-col--5 u-u-align---right table--mobile-controls">
1559 <!-- space needed to set correct height -->
1560 <span data-ng-show="isPrimaryInterface(interface)">&nbsp;</span>
1561 <a title="Delete" class="icon icon--remove u-display--desktop"
1562 data-ng-click="deleteInterface(interface)"
1563 data-ng-hide="isPrimaryInterface(interface)">delete</a>
1564 <button class="button--secondary u-display--mobile"
1565 data-ng-click="deleteInterface(interface)"
1566 data-ng-hide="isPrimaryInterface(interface)">delete</a>
1567 </td>
1568 </tr>
1569 </tbody>
1570 </table>
1571 <button class="button--secondary button--inline" data-ng-click="addInterface()">+ Add another interface</button>
1572 </section>
1573 <section class="page-header__section twelve-col u-margin--bottom-none twelve-col u-margin--bottom-none">
1574 <form class="form form--inline">
1575 <p class="page-header__message page-header__message--error ng-hide" data-ng-show="error">{$ error $}</p>
1576 <div class="page-header__controls">
1577 <button class="button--base button--inline" data-ng-click="cancel()">Cancel</button>
1578 <button class="button--secondary button--inline"
1579 data-ng-class="{ disabled: deviceHasError() }"
1580 data-ng-click="save(true)">Save and add another</button>
1581 <button class="button--positive button--inline"
1582 data-ng-class="{ disabled: deviceHasError() }"
1583 data-ng-click="save(false)">Save device</button>
1584 </div>
1585 </form>
1586 </section>
1587 </div>
1588
1589 <!-- XXX blake_r 2015-04-02 - Need to add e2e test. -->
1590 <div class="page-header__dropdown" data-ng-class="{ 'is-open': tabs.controllers.actionOption }">
1591 <!-- XXX blake_r 2015-04-02 - Need to add e2e test. -->
1592 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError('controllers') || hasActionsInProgress('controllers')">
1593 <!-- XXX blake_r 2015-04-02 - Need to add e2e test. -->
1594 <form class="form form--inline u-display--inline">
1595 <fieldset class="form__fieldset ng-hide" data-ng-show="tabs.controllers.actionOption.name === 'set-zone'">
1596 <div class="form__group">
1597 <label for="zone2" class="form__group-label">Select Zone</label>
1598 <div class="form__group-input">
1599 <select name="zone" id="zone2"
1600 data-ng-model="tabs.controllers.zoneSelection"
1601 data-ng-options="zone as zone.name for zone in zones">
1602 <option value="" disabled="disabled">Choose a zone</option>
1603 </select>
1604 </div>
1605 </div>
1606 </fieldset>
1607 <fieldset class="form__fieldset ng-hide" data-ng-show="tabs.controllers.actionOption.name === 'test'">
1608 <div class="form__group">
1609 <input class="form__group-label" id="enable_SSH" type="checkbox"
1610 data-ng-model="tabs.controllers.commissionOptions.enableSSH">
1611 <label class="checkbox-label" for="enable_SSH">Allow SSH access and prevent machine from powering off</label>
1612 </div>
1613 </fieldset>
1614 </form>
1615 <div class="page-header__controls" data-ng-if="tabs.controllers.actionOption.name !== 'test'">
1616 <button class="button--base button--inline" data-ng-click="actionCancel('controllers')">Cancel</button>
1617 <button class="button--positive button--inline" data-ng-click="actionGo('controllers')">
1618 <span data-ng-if="tabs.controllers.actionOption.name === 'set-zone'">Set zone for {$ tabs.controllers.selectedItems.length $}
1619 <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span>
1620 </span>
1621 <span data-ng-if="tabs.controllers.actionOption.name === 'on'">Power on {$ tabs.controllers.selectedItems.length $}
1622 <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span>
1623 </span>
1624 <span data-ng-if="tabs.controllers.actionOption.name === 'off'">Power off {$ tabs.controllers.selectedItems.length $}
1625 <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span>
1626 </span>
1627 <span data-ng-if="tabs.controllers.actionOption.name === 'delete'">Delete {$ tabs.controllers.selectedItems.length $}
1628 <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span>
1629 </span>
1630 <span data-ng-if="tabs.controllers.actionOption.name === 'import-images'">Import images for {$ tabs.controllers.selectedItems.length $}
1631 <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span>
1632 </span>
1633 </button>
1634 </div>
1635 </div>
1636 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError('controllers') || hasActionsInProgress('controllers')" data-ng-if="tabs.controllers.actionOption.name === 'test'">
1637 <form class="form form--stack">
1638 <fieldset class="form__fieldset eight-col u-margin--bottom-small">
1639 <div class="form__group">
1640 <label for="testing-scripts">Hardware Tests</label>
1641 <span id="testing-scripts" data-maas-script-select="script" data-script-type="2" data-ng-model="tabs.nodes.testSelection" class="tags--inline"></span>
1642 </div>
1643 </fieldset>
1644 </form>
1645 </div>
1646 <div class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="isActionError('controllers') || hasActionsInProgress('controllers')" data-ng-if="tabs.controllers.actionOption.name === 'test'">
1647 <div class="page-header__controls">
1648 <button class="button--base button--inline" data-ng-click="actionCancel('controllers')">Cancel</button>
1649 <button class="button--positive button--inline" data-ng-click="actionGo('controllers')">
1650 <span>Test {$ tabs.controllers.selectedItems.length $}
1651 <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span>
1652 </span>
1653 </button>
1654 </div>
1655 </div>
1656
1657 <!-- XXX blake_r 2015-04-02 - Need to add e2e test. -->
1658 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isActionError('controllers')">
1659 <form class="form form--inline">
1660 <p class="page-header__message page-header__message--error">
1661 {$ tabs.controllers.actionErrorCount $}
1662 <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span>
1663 cannot be {$ tabs.controllers.actionOption.sentence $}. To proceed, update your selection.
1664 </p>
1665 </form>
1666 </div>
1667
1668 <!-- XXX blake_r 2015-05-07 - Need to add e2e test. -->
1669 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="hasActionsInProgress('controllers') || hasActionsFailed('controllers')">
1670 <form class="form form--inline">
1671 <p class="page-header__message" data-ng-show="hasActionsInProgress('controllers')">
1672 <i class="icon icon--loading u-animation--spin u-margin--right-small"></i>
1673 {$ tabs.controllers.actionProgress.completed $} of {$ tabs.controllers.actionProgress.total $}
1674 controllers have been {$ tabs.controllers.actionOption.sentence $}.
1675 </p>
1676 <p class="page-header__message page-header__message--error"
1677 data-ng-repeat="(error, controllers) in tabs.controllers.actionProgress.errors">
1678 The {$ tabs.controllers.actionOption.title.toLowerCase() $} action for {$ controllers.length $}
1679 <span data-ng-pluralize count="controllers.length" when="{'one': 'controller', 'other': 'controllers'}"></span>
1680 failed with error: {$ error $}
1681 </p>
1682 </form>
1683 </div>
1684 </div>
1685
1686 <div class="page-header__dropdown" data-ng-class="{ 'is-open': tabs.controllers.addController }">
1687 <!-- XXX blake_r 2015-05-07 - Need to add e2e test. -->
1688 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="tabs.controllers.addController">
1689 <h3 class="page-header__dropdown-title">Add rack controller</h3>
1690 <pre class="u-margin--none">
1691 <code># To add a new rack controller, SSH into the rack controller.
1692# Install the maas-rack-controller package.
1693sudo apt install maas-rack-controller
1694# Register the rack controller with this MAAS. If the rack controller (and machines)
1695# don't have access to the URL, use a different IP address to allow connection.
1696sudo maas-rack register --url {$ tabs.controllers.registerUrl $} --secret {$ tabs.controllers.registerSecret $}</code>
1697 </pre>
1698 </div>
1699 <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="tabs.controllers.addController">
1700 <form class="form form--inline">
1701 <div class="page-header__controls">
1702 <button class="button--secondary button--inline"
1703 data-ng-click="tabs.controllers.addController = false">Close</button>
1704 </div>
1705 </form>
1706 </div>
1707 </div>
1708 </div>
1709</header>
1710<div sticky media-query="min-width: 769px" offset="89" class="page-navigation" data-ng-class="{ 'u-visibility--hidden': tabs.nodes.actionOption || addHardwareScope.viewable || tabs.devices.actionOption || addDeviceScope.viewable || tabs.controllers.actionOption || tabs.controllers.addController }">
1711 <div class="wrapper--inner">
1712 <nav class="page-navigation__links">
1713 <button class="page-navigation__link tooltip tooltip--right"
1714 aria-label="A deployable node managed by MAAS."
1715 data-ng-class="{ 'is-active': currentpage === 'nodes'}"
1716 data-ng-click="toggleTab('nodes')">{$ nodes.length $} <ng-pluralize count="nodes.length" when="{'one': 'Machine', 'other': 'Machines'}"></ng-pluralize></button>
1717 <button class="page-navigation__link tooltip"
1718 aria-label="A node known to MAAS, but is not deployable."
1719 data-ng-class="{ 'is-active': currentpage === 'devices'}"
1720 data-ng-click="toggleTab('devices')">{$ devices.length $} <ng-pluralize count="devices.length" when="{'one': 'Device', 'other': 'Devices'}"></ng-pluralize></button>
1721 <button class="page-navigation__link tooltip"
1722 data-ng-if="isSuperUser()"
1723 aria-label="A node that provides MAAS services."
1724 data-ng-class="{ 'is-active': currentpage === 'controllers'}"
1725 data-ng-click="toggleTab('controllers')">{$ controllers.length $} <ng-pluralize count="controllers.length" when="{'one': 'Controller', 'other': 'Controllers'}"></ng-pluralize></button>
1726 <button class="page-navigation__link tooltip"
1727 data-ng-if="showswitches"
1728 aria-label="A node that is a network switch."
1729 data-ng-class="{ 'is-active': currentpage === 'switches'}"
1730 data-ng-click="toggleTab('switches')">{$ switches.length $} <ng-pluralize count="switches.length" when="{'one': 'Switch', 'other': 'Switches'}"></ng-pluralize></button>
1731 </nav>
1732 </div>
1733</div>
1734<div class="u-padding--top row">
1735 <div class="wrapper--inner">
1736 <maas-notifications></maas-notifications>
1737 <aside class="three-col">
1738 <div class="accordion maas-accordion ng-hide" data-ng-show="currentpage === 'nodes'"
1739 data-ng-class="{ 'is-disabled': tabs.nodes.actionOption }">
1740 <h3 class="accordion__title"
1741 data-ng-class="{'is-active': isActive}"
1742 data-ng-init="isActive = false"
1743 data-ng-click="isActive = !isActive">Filter by</h3>
1744 <div class="accordion__content">
1745 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1746 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.status.length">
1747 <button class="accordion__tab-title maas-accordion-tab is-active">Status</button>
1748 <div class="accordion__tab-content">
1749 <ul class="accordion__tab-list">
1750 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1751 <li class="accordion__tab-item" data-ng-repeat="status in tabs.nodes.metadata.status | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('status', status.name, 'nodes') }">
1752 <button class="accordion__tab-link" data-ng-click="toggleFilter('status', status.name, 'nodes')">{$ status.name $} ({$ status.count $})</button>
1753 </li>
1754 </ul>
1755 </div>
1756 </div>
1757 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.owner.length">
1758 <button class="accordion__tab-title maas-accordion-tab">Owner</button>
1759 <div class="accordion__tab-content">
1760 <ul class="accordion__tab-list">
1761 <li class="accordion__tab-item" data-ng-repeat="owner in tabs.nodes.metadata.owner | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('owner', owner.name, 'nodes') }">
1762 <button class="accordion__tab-link" data-ng-click="toggleFilter('owner', owner.name, 'nodes')">{$ owner.name $} ({$ owner.count $})</button>
1763 </li>
1764 </ul>
1765 </div>
1766 </div>
1767 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.architecture.length">
1768 <button class="accordion__tab-title maas-accordion-tab">Architectures</button>
1769 <div class="accordion__tab-content">
1770 <ul class="accordion__tab-list">
1771 <li class="accordion__tab-item" data-ng-repeat="architecture in tabs.nodes.metadata.architecture | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('architecture', architecture.name, 'nodes') }">
1772 <button class="accordion__tab-link" data-ng-click="toggleFilter('architecture', architecture.name, 'nodes')"><span data-maas-release-name="architecture.name"></span> ({$ architecture.count $})</button>
1773 </li>
1774 </ul>
1775 </div>
1776 </div>
1777 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.release.length">
1778 <button class="accordion__tab-title maas-accordion-tab">OS/Release</button>
1779 <div class="accordion__tab-content">
1780 <ul class="accordion__tab-list">
1781 <li class="accordion__tab-item" data-ng-repeat="release in tabs.nodes.metadata.release | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('release', release.name, 'nodes') }">
1782 <button class="accordion__tab-link" data-ng-click="toggleFilter('release', release.name, 'nodes')"><span data-maas-release-name="release.name"></span> ({$ release.count $})</button>
1783 </li>
1784 </ul>
1785 </div>
1786 </div>
1787 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.tags.length">
1788 <button class="accordion__tab-title maas-accordion-tab">Tags</button>
1789 <div class="accordion__tab-content">
1790 <ul class="accordion__tab-list">
1791 <li class="accordion__tab-item" data-ng-repeat="tag in tabs.nodes.metadata.tags | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('tags', tag.name, 'nodes') }">
1792 <button class="accordion__tab-link" data-ng-click="toggleFilter('tags', tag.name, 'nodes')">{$ tag.name $} ({$ tag.count $})</button>
1793 </li>
1794 </ul>
1795 </div>
1796 </div>
1797 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.storage_tags.length">
1798 <button class="accordion__tab-title maas-accordion-tab">Storage Tags</button>
1799 <div class="accordion__tab-content">
1800 <ul class="accordion__tab-list">
1801 <li class="accordion__tab-item" data-ng-repeat="tag in tabs.nodes.metadata.storage_tags | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('storage_tags', tag.name, 'nodes') }">
1802 <button class="accordion__tab-link" data-ng-click="toggleFilter('storage_tags', tag.name, 'nodes')">{$ tag.name $} ({$ tag.count $})</button>
1803 </li>
1804 </ul>
1805 </div>
1806 </div>
1807 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.subnets.length">
1808 <button class="accordion__tab-title maas-accordion-tab">Subnets</button>
1809 <div class="accordion__tab-content">
1810 <ul class="accordion__tab-list">
1811 <li class="accordion__tab-item" data-ng-repeat="subnet in tabs.nodes.metadata.subnets | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('subnets', subnet.name, 'nodes') }">
1812 <button class="accordion__tab-link" data-ng-click="toggleFilter('subnets', subnet.name, 'nodes')">{$ subnet.name $} ({$ subnet.count $})</button>
1813 </li>
1814 </ul>
1815 </div>
1816 </div>
1817 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.fabrics.length">
1818 <button class="accordion__tab-title maas-accordion-tab">Fabrics</button>
1819 <div class="accordion__tab-content">
1820 <ul class="accordion__tab-list">
1821 <li class="accordion__tab-item" data-ng-repeat="fabric in tabs.nodes.metadata.fabrics | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('fabrics', fabric.name, 'nodes') }">
1822 <button class="accordion__tab-link" data-ng-click="toggleFilter('fabrics', fabric.name, 'nodes')">{$ fabric.name $} ({$ fabric.count $})</button>
1823 </li>
1824 </ul>
1825 </div>
1826 </div>
1827 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.spaces.length">
1828 <button class="accordion__tab-title maas-accordion-tab">Spaces</button>
1829 <div class="accordion__tab-content">
1830 <ul class="accordion__tab-list">
1831 <li class="accordion__tab-item" data-ng-repeat="space in tabs.nodes.metadata.spaces | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('spaces', space.name, 'nodes') }">
1832 <button class="accordion__tab-link" data-ng-click="toggleFilter('spaces', space.name, 'nodes')">{$ space.name $} ({$ space.count $})</button>
1833 </li>
1834 </ul>
1835 </div>
1836 </div>
1837 <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.zone.length">
1838 <button class="accordion__tab-title maas-accordion-tab">Zones</button>
1839 <div class="accordion__tab-content">
1840 <ul class="accordion__tab-list">
1841 <li class="accordion__tab-item" data-ng-repeat="zone in tabs.nodes.metadata.zone | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('zone', zone.name, 'nodes') }">
1842 <button class="accordion__tab-link" data-ng-click="toggleFilter('zone', zone.name, 'nodes')">{$ zone.name $} ({$ zone.count $})</button>
1843 </li>
1844 </ul>
1845 </div>
1846 </div>
1847 </div>
1848 </div>
1849 <div class="accordion three-col maas-accordion ng-hide" data-ng-show="currentpage === 'devices'"
1850 data-ng-class="{ 'is-disabled': tabs.devices.actionOption }">
1851 <h3 class="accordion__title">Filter by</h3>
1852 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
1853 <div class="ng-hide accordion__tab" data-ng-show="tabs.devices.metadata.owner.length">
1854 <button class="accordion__tab-title maas-accordion-tab active">Owner</button>
1855 <div class="accordion__tab-content">
1856 <ul class="accordion__tab-list">
1857 <li class="accordion__tab-item" data-ng-repeat="owner in tabs.devices.metadata.owner | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('owner', owner.name, 'devices') }">
1858 <button class="accordion__tab-link" data-ng-click="toggleFilter('owner', owner.name, 'devices')">{$ owner.name $} ({$ owner.count $})</button>
1859 </li>
1860 </ul>
1861 </div>
1862 </div>
1863 <div class="ng-hide accordion__tab" data-ng-show="tabs.devices.metadata.tags.length">
1864 <button class="accordion__tab-title maas-accordion-tab">Tags</button>
1865 <div class="accordion__tab-content">
1866 <ul class="accordion__tab-list">
1867 <li class="accordion__tab-item" data-ng-repeat="tag in tabs.devices.metadata.tags | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('tags', tag.name, 'devices') }">
1868 <button class="accordion__tab-link" data-ng-click="toggleFilter('tags', tag.name, 'devices')">{$ tag.name $} ({$ tag.count $})</button>
1869 </li>
1870 </ul>
1871 </div>
1872 </div>
1873 <div class="ng-hide accordion__tab" data-ng-show="tabs.devices.metadata.zone.length">
1874 <button class="accordion__tab-title maas-accordion-tab">Zones</button>
1875 <div class="accordion__tab-content">
1876 <ul class="accordion__tab-list">
1877 <li class="accordion__tab-item" data-ng-repeat="zone in tabs.devices.metadata.zone | orderBy:['name', '-count']" data-ng-class="{ 'is-active': isFilterActive('zone', zone.name, 'devices') }">
1878 <button class="accordion__tab-link" data-ng-click="toggleFilter('zone', zone.name, 'devices')">{$ zone.name $} ({$ zone.count $})</button>
1879 </li>
1880 </ul>
1881 </div>
1882 </div>
1883 </div>
1884 </aside>
1885 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1886 <div class="nine-col last-col ng-hide" data-ng-show="currentpage === 'nodes'">
1887 <form>
1888 <div id="search-bar" class="search nine-col">
1889 <!-- XXX ricgard 2016-06-16 - Need to add e2e test. -->
1890 <input
1891 type="search" placeholder="Search nodes" class="search__input"
1892 data-ng-model="tabs.nodes.search" data-ng-change="updateFilters('nodes')"
1893 data-ng-class="{ error: !tabs.nodes.searchValid }"
1894 data-ng-disabled="tabs.nodes.actionOption" />
1895 <input type="reset" class="search__submit"
1896 data-ng-class="{ 'search__submit--close': tabs.nodes.search.length > 0 }"
1897 data-ng-click="clearSearch('nodes')" />
1898 </div>
1899 <maas-machines-table id="nodes-listing" search="tabs.nodes.search" ng-disabled="hasActionsInProgress('nodes')"
1900 machine-has-error="!supportsAction($machine, 'nodes')" on-listing-change="onNodeListingChanged($machines, 'nodes')"
1901 on-check-all="toggleCheckAll('nodes')" on-check="toggleChecked($machine, 'nodes')"></maas-machines-table>
1902 </form>
1903 </div>
1904 <div class="nine-col last-col" class="ng-hide" data-ng-show="currentpage === 'devices'">
1905 <div class="search nine-col">
1906 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
1907 <input
1908 type="search" placeholder="Search devices" class="search__input"
1909 data-ng-model="tabs.devices.search" data-ng-change="updateFilters('devices')"
1910 data-ng-class="{ error: !tabs.devices.searchValid }"
1911 data-ng-disabled="tabs.devices.actionOption" />
1912 <input type="reset" class="search__submit"
1913 data-ng-class="{ 'search__submit--close': tabs.devices.search.length > 0 }"
1914 data-ng-click="clearSearch('devices')" />
1915 </div>
1916 <table>
1917 <thead>
1918 <tr>
1919 <th class="table-col--3">
1920 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
1921 <input type="checkbox" class="checkbox" data-ng-click="toggleCheckAll('devices')" data-ng-checked="tabs.devices.allViewableChecked" id="check-all-devices" data-ng-disabled="hasActionsInProgress('devices')" />
1922 <label for="check-all-devices" class="checkbox-label"></label>
1923 </th>
1924 <th class="table-col--24">
1925 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
1926 <a href="" data-ng-click="sortTable('fqdn', 'devices')" data-ng-class="{'is-sorted': tabs.devices.predicate === 'fqdn', 'sort-asc': tabs.devices.reverse === false, 'sort-desc': tabs.devices.reverse === true}">
1927 <span title="Fully Qualified Domain Name">FQDN</span>
1928 </a>
1929 </th>
1930 <th class="table-col--25">
1931 <a href="" data-ng-click="sortTable('primary_mac', 'devices')" data-ng-class="{'is-sorted': tabs.devices.predicate === 'primary_mac', 'sort-asc': tabs.devices.reverse === false, 'sort-desc': tabs.devices.reverse === true}">
1932 <span title="Media Access Control addresses">MAC</span>
1933 </a>
1934 </th>
1935 <th class="table-col--15">
1936 <a href="" data-ng-click="sortTable('ip_assignment', 'devices')" data-ng-class="{'is-sorted': tabs.devices.predicate === 'ip_assignment', 'sort-asc': tabs.devices.reverse === false, 'sort-desc': tabs.devices.reverse === true}">IP Assignment</a>
1937 </th>
1938 <th class="table-col--20">
1939 <a href="" data-ng-click="sortTable('ip_address', 'devices')" data-ng-class="{'is-sorted': tabs.devices.predicate === 'ip_address', 'sort-asc': tabs.devices.reverse === false, 'sort-desc': tabs.devices.reverse === true}">IP Address</a>
1940 </th>
1941 <th class="table-col--12">
1942 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
1943 <a href="" data-ng-click="sortTable('owner', 'devices')" data-ng-class="{'is-sorted': tabs.devices.predicate === 'owner', 'sort-asc': tabs.devices.reverse === false, 'sort-desc': tabs.devices.reverse === true}">Owner</a>
1944 </th>
1945 </tr>
1946 </thead>
1947 <tbody vs-repeat vs-scroll-parent="window">
1948 <!-- XXX rvba 2015-02-25 - Need to add e2e test. This really needs lots of tests. -->
1949 <tr
1950 data-ng-repeat="device in tabs.devices.filtered_items = (devices | nodesFilter:tabs.devices.search | orderBy:tabs.devices.predicate:tabs.devices.reverse) track by device.system_id"
1951 data-ng-class="{ 'table--error': !supportsAction(device, 'devices'), selected: device.$selected }">
1952 <td class="table-col--3" aria-label="Select device">
1953 <input type="checkbox" class="checkbox" data-ng-click="toggleChecked(device, 'devices')" data-ng-checked="device.$selected" id="{$ device.fqdn $}" data-ng-disabled="hasActionsInProgress('devices')"/>
1954 <label for="{$ device.fqdn $}" class="checkbox-label"></label>
1955 </td>
1956 <td class="table-col--24" aria-label="FQDN">
1957 <a href="#/node/device/{$ device.system_id $}">{$ device.fqdn $}</a>
1958 </td>
1959 <td class="table-col--25" aria-label="MAC">
1960 {$ device.primary_mac $}
1961 <span class="extra-macs" data-ng-show="device.extra_macs.length">(+{$ device.extra_macs.length $})</span>
1962 </td>
1963 <td class="table-col--15" aria-label="IP Assignment">{$ getDeviceIPAssignment(device.ip_assignment) $}</td>
1964 <td class="table-col--20" aria-label="IP Address">{$ device.ip_address $}</td>
1965 <td class="table-col--12" aria-label="Owner">{$ device.owner $}</td>
1966 </tr>
1967 </tbody>
1968 </table>
1969 </div>
1970 <div class="twelve-col last-col" class="ng-hide" data-ng-show="currentpage === 'controllers'">
1971 <div class="search twelve-col">
1972 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
1973 <input
1974 type="search" placeholder="Search controllers" class="search__input"
1975 data-ng-model="tabs.controllers.search" data-ng-change="updateFilters('controllers')"
1976 data-ng-class="{ error: !tabs.controllers.searchValid }"
1977 data-ng-disabled="tabs.controllers.actionOption" />
1978 <input type="submit" class="search__submit"
1979 data-ng-class="{ 'search__submit--close': tabs.controllers.search.length > 0 }"
1980 data-ng-click="clearSearch('controllers')" />
1981 </div>
1982 <table>
1983 <thead>
1984 <tr>
1985 <th class="table-col--2">
1986 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
1987 <input type="checkbox" class="checkbox" data-ng-click="toggleCheckAll('controllers')" data-ng-checked="tabs.controllers.allViewableChecked" id="check-all-controllers" data-ng-disabled="hasActionsInProgress('controllers')" />
1988 <label for="check-all-controllers" class="checkbox-label"></label>
1989 </th>
1990 <th class="table-col--19">
1991 <a href="" data-ng-click="sortTable('fqdn', 'controllers')" data-ng-class="{'is-sorted': tabs.controllers.predicate === 'fqdn', 'sort-asc': tabs.controllers.reverse === false, 'sort-desc': tabs.controllers.reverse === true}">
1992 <span title="Fully Qualified Domain Name">Name</span>
1993 </a>
1994 </th>
1995 <th class="table-col--4 u-align--center">
1996 <span title="Status">Status</span>
1997 </th>
1998 <th class="table-col--15">
1999 <a href="" data-ng-click="sortTable('node_type_display', 'controllers')" data-ng-class="{'is-sorted': tabs.controllers.predicate === 'node_type_display', 'sort-asc': tabs.controllers.reverse === false, 'sort-desc': tabs.controllers.reverse === true}">
2000 <span title="Controller Type">Type</span>
2001 </a>
2002 </th>
2003 <th class="table-col--10">
2004 <a href="" data-ng-click="sortTable('node_type_display', 'controllers')" data-ng-class="{'is-sorted': tabs.controllers.predicate === 'version', 'sort-asc': tabs.controllers.reverse === false, 'sort-desc': tabs.controllers.reverse === true}">
2005 <span title="Version">Version</span>
2006 </a>
2007 </th>
2008 <th class="table-col--15">
2009 <a href="" data-ng-click="sortTable('updated', 'controllers')" data-ng-class="{'is-sorted': tabs.controllers.predicate === 'updated', 'sort-asc': tabs.controllers.reverse === false, 'sort-desc': tabs.controllers.reverse === true}">Last Image Sync</a>
2010 </th>
2011 <th class="table-col--15">
2012 <a href="" data-ng-click="sortTable('updated', 'controllers')" data-ng-class="{'is-sorted': tabs.controllers.predicate === 'updated', 'sort-asc': tabs.controllers.reverse === false, 'sort-desc': tabs.controllers.reverse === true}">Images Status</a>
2013 </th>
2014 </tr>
2015 </thead>
2016 <tbody vs-repeat vs-scroll-parent="window">
2017 <!-- XXX rvba 2015-02-25 - Need to add e2e test. This really needs lots of tests. -->
2018 <tr
2019 data-ng-repeat="controller in tabs.controllers.filtered_items = (controllers | nodesFilter:tabs.controllers.search | orderBy:tabs.controllers.predicate:tabs.controllers.reverse) track by controller.system_id"
2020 data-ng-class="{ 'table--error': !supportsAction(controller, 'controllers'), selected: controller.$selected }">
2021 <td class="table-col--2" aria-label="Select controller">
2022 <input type="checkbox" class="checkbox" data-ng-click="toggleChecked(controller, 'controllers')" data-ng-checked="controller.$selected" id="{$ controller.fqdn $}" data-ng-disabled="hasActionsInProgress('controllers')"/>
2023 <label for="{$ controller.fqdn $}" class="checkbox-label"></label>
2024 </td>
2025 <td class="table-col--19" aria-label="FQDN">
2026 <a href="#/node/controller/{$ controller.system_id $}">{$ controller.fqdn $}</a>
2027 </td>
2028 <td class="table-col--4 u-align--center" data-maas-controller-status="controller" data-maas-services="services" aria-label="Status"></td>
2029 <td class="table-col--15" aria-label="Type">{$ controller.node_type_display $}</td>
2030 <td class="table-col--10" aria-label="Version">
2031 <div data-ng-show="controller.version">
2032 {$ controller.version__short $}
2033 </div>
2034 <div data-ng-show="!controller.version" class="u-text--subtle">
2035 Unknown
2036 <i class="icon icon--info u-margin--left-tiny tooltip"
2037 aria-label="Less than 2.3.0"></i>
2038 </div>
2039 </td>
2040 <td class="table-col--15" aria-label="Last image sync">{$ controller.last_image_sync || 'Never' $}</td>
2041 <td class="table-col--15" aria-label="Image status">
2042 <maas-controller-image-status system-id="controller.system_id"></maas-controller-image-status>
2043 </td>
2044 </tr>
2045 </tbody>
2046 </table>
2047 </div>
2048 <div class="twelve-col last-col" class="ng-hide" data-ng-show="currentpage === 'switches'">
2049 <div class="search twelve-col">
2050 <!-- XXX rvba 2015-02-25 - Need to add e2e test. -->
2051 <input
2052 type="search" placeholder="Search switches" class="search__input"
2053 data-ng-model="tabs.switches.search" data-ng-change="updateFilters('switches')"
2054 data-ng-class="{ error: !tabs.switches.searchValid }"
2055 data-ng-disabled="tabs.switches.actionOption" />
2056 <input type="submit" class="search__submit"
2057 data-ng-class="{ 'search__submit--close': tabs.switches.search.length > 0 }"
2058 data-ng-click="clearSearch('switches')" />
2059 </div>
2060 <maas-switches-table id="switches-listing" search="tabs.switches.search" ng-disabled="hasActionsInProgress('switches')"
2061 switch-has-error="!supportsAction($switch_, 'switches')" on-listing-change="onNodeListingChanged($switches, 'switches')"
2062 on-check-all="toggleCheckAll('switches')" on-check="toggleChecked($switch_, 'switches')"></maas-switches-table>
2063 </div>
2064 </div>
2065</div>
2066>>>>>>> src/maasserver/static/partials/nodes-list.html
diff --git a/src/maasserver/static/partials/script-results-list.html b/src/maasserver/static/partials/script-results-list.html
index bfadd34..60ad83a 100644
--- a/src/maasserver/static/partials/script-results-list.html
+++ b/src/maasserver/static/partials/script-results-list.html
@@ -4,6 +4,7 @@
4 <p class="u-text--loading"><i class="p-icon--spinner u-animation--spin"></i>&nbsp;&nbsp;Loading...</p>4 <p class="u-text--loading"><i class="p-icon--spinner u-animation--spin"></i>&nbsp;&nbsp;Loading...</p>
5 </div>5 </div>
6 </div>6 </div>
7<<<<<<< src/maasserver/static/partials/script-results-list.html
7 <div class="row">8 <div class="row">
8 <div data-ng-repeat="hardware_type in results">9 <div data-ng-repeat="hardware_type in results">
9 <div data-ng-if="resultsLoaded && (hardware_type.results | json) != '{}'">10 <div data-ng-if="resultsLoaded && (hardware_type.results | json) != '{}'">
@@ -47,12 +48,59 @@
47 <button class="p-contextual-menu__link" aria-label="View previous {$ result.result_section $}" data-ng-if="!result.showing_history" data-ng-click="toggleMenu(); loadHistory(result)">View previous {$ result.result_section $}</button>48 <button class="p-contextual-menu__link" aria-label="View previous {$ result.result_section $}" data-ng-if="!result.showing_history" data-ng-click="toggleMenu(); loadHistory(result)">View previous {$ result.result_section $}</button>
48 <button class="p-contextual-menu__link" aria-label="Hide previous {$ result.result_section $}" data-ng-if="result.showing_history" data-ng-click="toggleMenu(); result.showing_results = false; result.showing_history = false">Hide previous {$ result.result_section $}</button>49 <button class="p-contextual-menu__link" aria-label="Hide previous {$ result.result_section $}" data-ng-if="result.showing_history" data-ng-click="toggleMenu(); result.showing_results = false; result.showing_history = false">Hide previous {$ result.result_section $}</button>
49 </div>50 </div>
51=======
52 <div data-ng-repeat="hardware_type in results">
53 <div data-ng-if="resultsLoaded && (hardware_type.results | json) != '{}'">
54 <h2 data-ng-if="hardware_type.title !== 'null'">{$ hardware_type.title $}</h2>
55 <div data-ng-repeat="(title, results) in hardware_type.results">
56 <h3 data-ng-if="title !== 'null'">{$ title $}</h3>
57 <section class="table u-margin--bottom">
58 <header class="table__head">
59 <div class="table__row">
60 <div class="table__header table-col--2 u-padding--left-none"></div>
61 <div class="table__header table-col--24">Name</div>
62 <div class="table__header table-col--22">Tags</div>
63 <div class="table__header table-col--15">Runtime</div>
64 <div class="table__header table-col--20">Date</div>
65 <div class="table__header table-col--12">Result</div>
66 <div class="table__header table-col--5 u-align--right">Actions</div>
67 </div>
68 </header>
69 <main class="table__body">
70 <div data-ng-repeat="result in results">
71 <div class="table__row" data-ng-class="{'is-active': result.showing_results || result.showing_history}">
72 <div class="table__data table-col--2 u-padding--left-none" aria-label="Status">
73 <span data-maas-script-status="script-status" data-script-status="result.status"></span>
74 </div>
75 <div class="table__data table-col--24" data-ng-click="result.showing_results = !result.showing_results" aria-label="Name">
76 {$ result.name $}
77 <button class="icon u-margin--top-tiny u-float--right" data-ng-class="{'icon--open': !result.showing_results, 'icon--close': result.showing_results}" data-ng-if="!result.showing_history"></button>
78 </div>
79 <div class="table__data table-col--22" aria-label="Tags"><span data-ng-hide="result.showing_history">{$ result.tags $}</span></div>
80 <div class="table__data table-col--15" aria-label="Runtime"><span data-ng-hide="result.showing_history" data-maas-script-run-time="script-runtime" data-start-time="result.starttime" data-run-time="{{result.runtime}}" data-estimated-run-time="{{result.estimated_runtime}}" data-script-status="result.status"></span></div>
81 <div class="table__data table-col--20" aria-label="Date"><span data-ng-hide="result.showing_history">{$ result.updated $}</span></div>
82 <div class="table__data table-col--12" aria-label="Status">
83 <span data-ng-hide="result.showing_history">
84 <!-- Only link to the testing result when we've received it. This is indicated with status 2(passed), 3(failed), 4(timedout), 6(degraded), 8(failed installing)-->
85 {$ result.status_name $} <a data-ng-if="result.status === 2 || result.status === 3 || result.status === 4 || result.status === 6 || result.status === 8" href="#/node/{$ type_name $}/{$ node.system_id $}/{$ section.area $}/{$ result.id $}">View log</a>
86 </span>
87 </div>
88 <div class="table__data table-col--5 table--mobile-controls">
89 <div class="table__controls u-align--right" toggle-ctrl>
90 <button class="table__controls-toggle" data-ng-click="toggleMenu()">View actions</button>
91 <div class="table__controls-menu ng-hide" role="menu" data-ng-show="isToggled">
92 <button class="table__controls-action" aria-label="View metrics" data-ng-if="!result.showing_results" data-ng-click="toggleMenu(); result.showing_history = false; result.showing_results = true">View metrics</button>
93 <button class="table__controls-action" aria-label="Hide metrics" data-ng-if="result.showing_results" data-ng-click="toggleMenu(); result.showing_history = false; result.showing_results = false">Hide metrics</button>
94 <button class="table__controls-action" aria-label="View previous {$ result.result_section $}" data-ng-if="!result.showing_history" data-ng-click="toggleMenu(); result.showing_results = false; result.showing_history = true">View previous {$ result.result_section $}</button>
95 <button class="table__controls-action" aria-label="Hide previous {$ result.result_section $}" data-ng-if="result.showing_history" data-ng-click="toggleMenu(); result.showing_results = false; result.showing_history = false">Hide previous {$ result.result_section $}</button>
96>>>>>>> src/maasserver/static/partials/script-results-list.html
50 </div>97 </div>
51 </td>98 </td>
52 <td class="p-table-expanding__panel col-12" aria-label="results" data-ng-if="result.showing_results && !result.showing_history">99 <td class="p-table-expanding__panel col-12" aria-label="results" data-ng-if="result.showing_results && !result.showing_history">
53 <div class="row">100 <div class="row">
54 <div class="col-12" data-ng-if="result.results.length === 0">No metrics provided</div>101 <div class="col-12" data-ng-if="result.results.length === 0">No metrics provided</div>
55 </div>102 </div>
103<<<<<<< src/maasserver/static/partials/script-results-list.html
56 <div class="row" data-ng-if="result.results">104 <div class="row" data-ng-if="result.results">
57 <dl data-ng-repeat="item in result.results">105 <dl data-ng-repeat="item in result.results">
58 <dt class="p-tooltip p-tooltip--top-center">106 <dt class="p-tooltip p-tooltip--top-center">
@@ -66,6 +114,34 @@
66 <td class="p-table-expanding__panel col-12" aria-label="loading history" data-ng-if="result.loading_history">114 <td class="p-table-expanding__panel col-12" aria-label="loading history" data-ng-if="result.loading_history">
67 <div class="col-12">115 <div class="col-12">
68 <p class="u-text--loading"><i class="p-icon--spinner u-animation--spin"></i>&nbsp;&nbsp;Loading...</p>116 <p class="u-text--loading"><i class="p-icon--spinner u-animation--spin"></i>&nbsp;&nbsp;Loading...</p>
117=======
118 </div>
119 </div>
120
121 <div class="table__dropdown" aria-label="history" data-ng-if="result.showing_history">
122 <div class="table__row is-active">
123 <div class="table__data table-col--100">
124 <section class="table u-margin--bottom">
125 <main class="table__body">
126 <div class="table__row is-active u-border--none" data-ng-repeat="item in result.history_list">
127 <div class="table__data table-col--2 u-padding--left-none" aria-label="Status">
128 <span data-maas-script-status="script-status" data-script-status="item.status"></span>
129 </div>
130 <div class="table__data table-col--24" aria-label="Name">{$ result.name $}</div>
131 <div class="table__data table-col--24" aria-label="Tags">{$ result.tags $}</div>
132 <div class="table__data table-col--15" aria-label="Runtime"><span data-maas-script-run-time="script-runtime" data-start-time="item.starttime" data-run-time="{{item.runtime}}" data-estimated-run-time="{{item.estimated_runtime}}" data-script-status="item.status"></span></div>
133 <div class="table__data table-col--20" aria-label="Date">{$ item.updated $}</div>
134 <div class="table__data table-col--10" aria-label="Status">
135 <!-- Only link to the testing result when we've received it. This is indicated with status 2(passed), 3(failed), 4(timedout), 6(degraded), 8(failed installing)-->
136 {$ item.status_name $} <a data-ng-if="item.status === 2 || item.status === 3 || item.status === 4 || item.status === 6 || item.status === 8" href="#/node/{$ type_name $}/{$ node.system_id $}/{$ section.area $}/{$ item.id $}">View log</a>
137 </div>
138 </div>
139 </main>
140 </section>
141 <p class="u-align--center u-margin--bottom">
142 <button class="button--secondary button--inline" data-ng-click="result.showing_history = false">Hide previous {$ result.result_section $}</button>
143 </p>
144>>>>>>> src/maasserver/static/partials/script-results-list.html
69 </div>145 </div>
70 </div>146 </div>
71 <td class="p-table-expanding__panel col-12" aria-label="history" data-ng-if="result.showing_history">147 <td class="p-table-expanding__panel col-12" aria-label="history" data-ng-if="result.showing_history">
diff --git a/src/maasserver/tests/test_bootsources.py b/src/maasserver/tests/test_bootsources.py
index a83c9c1..3629529 100644
--- a/src/maasserver/tests/test_bootsources.py
+++ b/src/maasserver/tests/test_bootsources.py
@@ -44,7 +44,10 @@ from maasserver.testing.testcase import (
44)44)
45from maasserver.tests.test_bootresources import SimplestreamsEnvFixture45from maasserver.tests.test_bootresources import SimplestreamsEnvFixture
46from maasserver.utils import get_maas_user_agent46from maasserver.utils import get_maas_user_agent
47<<<<<<< src/maasserver/tests/test_bootsources.py
47from maastesting.djangotestcase import count_queries48from maastesting.djangotestcase import count_queries
49=======
50>>>>>>> src/maasserver/tests/test_bootsources.py
48from maastesting.matchers import MockCalledOnceWith51from maastesting.matchers import MockCalledOnceWith
49from provisioningserver.config import DEFAULT_IMAGES_URL52from provisioningserver.config import DEFAULT_IMAGES_URL
50from provisioningserver.import_images import (53from provisioningserver.import_images import (
diff --git a/src/maasserver/triggers/tests/test_websocket_listener.py b/src/maasserver/triggers/tests/test_websocket_listener.py
index 6297f0e..5ba464b 100644
--- a/src/maasserver/triggers/tests/test_websocket_listener.py
+++ b/src/maasserver/triggers/tests/test_websocket_listener.py
@@ -1714,8 +1714,13 @@ class TestIPRangeListener(
1714 self.create_subnet, {'cidr': str(network)})1714 self.create_subnet, {'cidr': str(network)})
1715 params = {1715 params = {
1716 'subnet': subnet,1716 'subnet': subnet,
1717<<<<<<< src/maasserver/triggers/tests/test_websocket_listener.py
1717 'start_ip': IPAddress(network.first + 2),1718 'start_ip': IPAddress(network.first + 2),
1718 'end_ip': IPAddress(network.last - 1)}1719 'end_ip': IPAddress(network.last - 1)}
1720=======
1721 'start_ip': str(IPAddress(network.first + 2)),
1722 'end_ip': str(IPAddress(network.last - 1))}
1723>>>>>>> src/maasserver/triggers/tests/test_websocket_listener.py
1719 yield listener.startService()1724 yield listener.startService()
1720 try:1725 try:
1721 iprange = yield deferToDatabase(1726 iprange = yield deferToDatabase(
diff --git a/src/provisioningserver/import_images/boot_resources.py b/src/provisioningserver/import_images/boot_resources.py
index f985df6..0a5b3c2 100644
--- a/src/provisioningserver/import_images/boot_resources.py
+++ b/src/provisioningserver/import_images/boot_resources.py
@@ -9,6 +9,7 @@ __all__ = [
9 ]9 ]
1010
11from argparse import ArgumentParser11from argparse import ArgumentParser
12import copy
12import errno13import errno
13from io import StringIO14from io import StringIO
14import os15import os
@@ -41,6 +42,18 @@ from provisioningserver.utils.fs import (
41 read_text_file,42 read_text_file,
42 tempdir,43 tempdir,
43)44)
45<<<<<<< src/provisioningserver/import_images/boot_resources.py
46=======
47from provisioningserver.utils.service_monitor import ServiceActionError
48from provisioningserver.utils.shell import (
49 call_and_check,
50 ExternalProcessError,
51)
52from provisioningserver.utils.snappy import (
53 get_snap_data_path,
54 running_in_snap,
55)
56>>>>>>> src/provisioningserver/import_images/boot_resources.py
44from twisted.internet.defer import inlineCallbacks57from twisted.internet.defer import inlineCallbacks
45from twisted.python.filepath import FilePath58from twisted.python.filepath import FilePath
4659
@@ -109,6 +122,54 @@ def write_snapshot_metadata(snapshot, meta_file_content):
109 atomic_write(meta_file_content.encode("ascii"), meta_file, mode=0o644)122 atomic_write(meta_file_content.encode("ascii"), meta_file, mode=0o644)
110123
111124
125<<<<<<< src/provisioningserver/import_images/boot_resources.py
126=======
127def write_targets_conf(snapshot):
128 """Write "maas.tgt" file."""
129 targets_conf = os.path.join(snapshot, 'maas.tgt')
130 targets_conf_content = compose_targets_conf(snapshot)
131 atomic_write(targets_conf_content, targets_conf, mode=0o644)
132
133
134def update_targets_conf(snapshot):
135 """Runs tgt-admin to update the new targets from "maas.tgt"."""
136 # Ensure that tgt is running before tgt-admin is used.
137 try:
138 service_monitor.ensureService("tgt").wait(30)
139 except ServiceActionError:
140 msg = "Unable to start tgt"
141 try_send_rack_event(EVENT_TYPES.RACK_IMPORT_WARNING, msg)
142 maaslog.warning(msg)
143 return
144
145 # Update the tgt config.
146 targets_conf = os.path.join(snapshot, 'maas.tgt')
147
148 # The targets_conf may not exist in the event the BootSource is broken
149 # and images havn't been imported yet. This fixes LP:1655721
150 if not os.path.exists(targets_conf):
151 return
152
153 try:
154 env = copy.deepcopy(os.environ)
155 # LP:1718706 - When TGT is run in a snap the socket is stored in the
156 # SNAP_DATA path. Define it before calling tgt-admin otherwise the
157 # standard path is used and the call will fail.
158 if running_in_snap():
159 env['TGT_IPC_SOCKET'] = os.path.join(
160 get_snap_data_path(), 'tgtd-socket')
161 call_and_check(sudo([
162 get_path('/usr/sbin/tgt-admin'),
163 '--conf', targets_conf,
164 '--update', 'ALL',
165 ]), env=env)
166 except ExternalProcessError as e:
167 msg = "Unable to update TGT config: %s" % e
168 try_send_rack_event(EVENT_TYPES.RACK_IMPORT_WARNING, msg)
169 maaslog.warning(msg)
170
171
172>>>>>>> src/provisioningserver/import_images/boot_resources.py
112def read_sources(sources_yaml):173def read_sources(sources_yaml):
113 """Read boot resources config file.174 """Read boot resources config file.
114175
diff --git a/src/provisioningserver/import_images/tests/test_boot_resources.py b/src/provisioningserver/import_images/tests/test_boot_resources.py
index 28635e7..31fa955 100644
--- a/src/provisioningserver/import_images/tests/test_boot_resources.py
+++ b/src/provisioningserver/import_images/tests/test_boot_resources.py
@@ -43,7 +43,18 @@ from provisioningserver.testing.config import (
43 BootSourcesFixture,43 BootSourcesFixture,
44 ClusterConfigurationFixture,44 ClusterConfigurationFixture,
45)45)
46<<<<<<< src/provisioningserver/import_images/tests/test_boot_resources.py
46from provisioningserver.utils.fs import write_text_file47from provisioningserver.utils.fs import write_text_file
48=======
49from provisioningserver.utils.fs import (
50 tempdir,
51 write_text_file,
52)
53from provisioningserver.utils.service_monitor import ServiceActionError
54from provisioningserver.utils.shell import ExternalProcessError
55from testtools.content import Content
56from testtools.content_type import UTF8_TEXT
57>>>>>>> src/provisioningserver/import_images/tests/test_boot_resources.py
47from testtools.matchers import (58from testtools.matchers import (
48 DirExists,59 DirExists,
49 FileExists,60 FileExists,
@@ -477,6 +488,87 @@ class TestMain(MAASTestCase):
477 boot_resources.NoConfigFile,488 boot_resources.NoConfigFile,
478 boot_resources.main, self.make_args(sources="", sources_file=""))489 boot_resources.main, self.make_args(sources="", sources_file=""))
479490
491<<<<<<< src/provisioningserver/import_images/tests/test_boot_resources.py
492=======
493 def test_update_targets_conf_ensures_tgt_service(self):
494 mock_ensureService = self.patch(
495 boot_resources.service_monitor, "ensureService")
496 self.patch(boot_resources, "call_and_check")
497 boot_resources.update_targets_conf(factory.make_name("snapshot"))
498 self.assertThat(mock_ensureService, MockCalledOnceWith("tgt"))
499
500 def test_update_targets_conf_logs_tgt_service_check_error(self):
501 # Regression test for LP:1735025
502 mock_ensureService = self.patch(
503 boot_resources.service_monitor, "ensureService")
504 mock_ensureService.side_effect = ServiceActionError()
505 mock_try_send_rack_event = self.patch(
506 boot_resources, 'try_send_rack_event')
507 mock_maaslog = self.patch(boot_resources.maaslog, 'warning')
508 boot_resources.update_targets_conf(factory.make_name("snapshot"))
509 self.assertThat(mock_try_send_rack_event, MockCalledOnce())
510 self.assertThat(mock_maaslog, MockCalledOnce())
511
512 def test_update_targets_conf_logs_error(self):
513 self.patch(boot_resources.service_monitor, "ensureService")
514 mock_try_send_rack_event = self.patch(
515 boot_resources, 'try_send_rack_event')
516 mock_maaslog = self.patch(boot_resources.maaslog, 'warning')
517 self.patch(boot_resources.os.path, 'exists').return_value = True
518 self.patch(boot_resources, 'call_and_check').side_effect = (
519 ExternalProcessError(
520 returncode=2, cmd=('tgt-admin',), output='error'))
521 snapshot = factory.make_name("snapshot")
522 boot_resources.update_targets_conf(snapshot)
523 self.assertThat(mock_try_send_rack_event, MockCalledOnce())
524 self.assertThat(mock_maaslog, MockCalledOnce())
525 self.assertThat(
526 boot_resources.call_and_check,
527 MockCalledOnceWith([
528 'sudo', '-n', '/usr/sbin/tgt-admin',
529 '--conf', os.path.join(snapshot, 'maas.tgt'),
530 '--update', 'ALL'], env=os.environ))
531
532 def test_update_targets_only_runs_when_conf_exists(self):
533 # Regression test for LP:1655721
534 temp_dir = self.useFixture(TempDirectory()).path
535 self.useFixture(ClusterConfigurationFixture(tftp_root=temp_dir))
536 mock_ensureService = self.patch(
537 boot_resources.service_monitor, "ensureService")
538 mock_call_and_check = self.patch(boot_resources, "call_and_check")
539 mock_path_exists = self.patch(boot_resources.os.path, 'exists')
540 mock_path_exists.return_value = False
541 boot_resources.update_targets_conf(temp_dir)
542 self.assertThat(mock_ensureService, MockCalledOnceWith("tgt"))
543 self.assertThat(
544 mock_path_exists,
545 MockCalledOnceWith(os.path.join(temp_dir, 'maas.tgt')))
546 self.assertThat(mock_call_and_check, MockNotCalled())
547
548 def test_update_targets_conf_sets_env_var_in_snap(self):
549 # Regression test for LP:1718706
550 self.patch(boot_resources.service_monitor, "ensureService")
551 self.patch(boot_resources.os.path, 'exists').return_value = True
552 self.patch(boot_resources, 'call_and_check')
553 self.patch(boot_resources, 'running_in_snap').return_value = True
554 snap_data_path = factory.make_name('snap_data_path')
555 self.patch(boot_resources, 'get_snap_data_path').return_value = (
556 snap_data_path)
557 snapshot = factory.make_name("snapshot")
558 boot_resources.update_targets_conf(snapshot)
559 self.assertThat(
560 boot_resources.call_and_check,
561 MockCalledOnceWith([
562 'sudo', '-n', '/usr/sbin/tgt-admin',
563 '--conf', os.path.join(snapshot, 'maas.tgt'),
564 '--update', 'ALL'
565 ], env={
566 'TGT_IPC_SOCKET': os.path.join(
567 snap_data_path, 'tgtd-socket'),
568 **os.environ,
569 }))
570
571>>>>>>> src/provisioningserver/import_images/tests/test_boot_resources.py
480572
481class TestMetaContains(MAASTestCase):573class TestMetaContains(MAASTestCase):
482 """Tests for the `meta_contains` function."""574 """Tests for the `meta_contains` function."""
diff --git a/src/provisioningserver/import_images/tests/test_download_resources.py b/src/provisioningserver/import_images/tests/test_download_resources.py
index 9f40e29..81bc3c1 100644
--- a/src/provisioningserver/import_images/tests/test_download_resources.py
+++ b/src/provisioningserver/import_images/tests/test_download_resources.py
@@ -359,6 +359,41 @@ class TestRepoWriter(MAASTestCase):
359 label=product['label'], subarches={'ga-16.04', 'generic'},359 label=product['label'], subarches={'ga-16.04', 'generic'},
360 bootloader_type=None))360 bootloader_type=None))
361361
362<<<<<<< src/provisioningserver/import_images/tests/test_download_resources.py
363 def test_inserts_no_generic_link_for_generic_non_ga_kflavor(self):
364 # Regression test for LP:1749246
365=======
366 def test_inserts_generic_link_for_generic_ga_kflavor(self):
367>>>>>>> src/provisioningserver/import_images/tests/test_download_resources.py
368 product_mapping = ProductMapping()
369 product = self.make_product(subarch='hwe-16.04', kflavor='generic')
370 product_mapping.add(product, 'hwe-16.04')
371 repo_writer = download_resources.RepoWriter(
372 None, None, product_mapping)
373 self.patch(
374 download_resources, 'products_exdata').return_value = product
375 # Prevent MAAS from trying to actually write the file.
376 mock_insert_file = self.patch(download_resources, 'insert_file')
377 mock_link_resources = self.patch(download_resources, 'link_resources')
378 # We only need to provide the product as the other fields are only used
379 # when writing the actual files to disk.
380 repo_writer.insert_item(product, None, None, None, None)
381 # None is used for the store and the content source as we're not
382 # writing anything to disk.
383 self.assertThat(
384 mock_insert_file,
385 MockCalledOnceWith(
386 None, os.path.basename(product['path']), product['sha256'],
387 {'sha256': product['sha256']}, product['size'], None))
388 # links are mocked out by the mock_insert_file above.
389 self.assertThat(
390 mock_link_resources,
391 MockCalledOnceWith(
392 snapshot_path=None, links=mock.ANY, osystem=product['os'],
393 arch=product['arch'], release=product['release'],
394 label=product['label'], subarches={'hwe-16.04'},
395 bootloader_type=None))
396
362 def test_inserts_no_generic_link_for_generic_non_ga_kflavor(self):397 def test_inserts_no_generic_link_for_generic_non_ga_kflavor(self):
363 # Regression test for LP:1749246398 # Regression test for LP:1749246
364 product_mapping = ProductMapping()399 product_mapping = ProductMapping()
diff --git a/src/provisioningserver/utils/tests/test_network.py b/src/provisioningserver/utils/tests/test_network.py
index bda51e1..3b3c6b0 100644
--- a/src/provisioningserver/utils/tests/test_network.py
+++ b/src/provisioningserver/utils/tests/test_network.py
@@ -83,7 +83,10 @@ from provisioningserver.utils.network import (
83 resolves_to_loopback_address,83 resolves_to_loopback_address,
84 reverseResolve,84 reverseResolve,
85)85)
86<<<<<<< src/provisioningserver/utils/tests/test_network.py
86from provisioningserver.utils.shell import get_env_with_locale87from provisioningserver.utils.shell import get_env_with_locale
88=======
89>>>>>>> src/provisioningserver/utils/tests/test_network.py
87from testtools import ExpectedException90from testtools import ExpectedException
88from testtools.matchers import (91from testtools.matchers import (
89 Contains,92 Contains,
diff --git a/utilities/release-build b/utilities/release-build
index 3c0a5af..49f32cc 100755
--- a/utilities/release-build
+++ b/utilities/release-build
@@ -15,7 +15,7 @@ usage () {
15 exit 115 exit 1
16}16}
1717
18DEFAULT_DISTROS="$(ubuntu-distro-info --supported | grep -v trusty)"18DEFAULT_DISTROS="$(ubuntu-distro-info --supported | grep -v trusty | grep -v bionic)"
19PACKAGE_DISTROS=""19PACKAGE_DISTROS=""
20KEEP_CHANGELOG=020KEEP_CHANGELOG=0
2121
@@ -61,7 +61,7 @@ dch -a "" --release-heuristic log --nomultimaint
61PKG=$(head -n1 debian/changelog | awk '{print $1}')61PKG=$(head -n1 debian/changelog | awk '{print $1}')
62MAJOR_VER=$(head -n 1 debian/changelog | sed 's/^.*(//' | sed 's/).*//' | sed 's/-.*//')62MAJOR_VER=$(head -n 1 debian/changelog | sed 's/^.*(//' | sed 's/).*//' | sed 's/-.*//')
63REV_COUNT=$(git rev-list --count $COMMIT)63REV_COUNT=$(git rev-list --count $COMMIT)
64REV_SHORT=$(git rev-parse --short $COMMIT)64REV_SHORT=$(git rev-parse --short=7 $COMMIT)
65FULL_VER="$MAJOR_VER-$REV_COUNT-g$REV_SHORT"65FULL_VER="$MAJOR_VER-$REV_COUNT-g$REV_SHORT"
66TARBALL="maas_$FULL_VER.orig.tar.gz"66TARBALL="maas_$FULL_VER.orig.tar.gz"
6767
@@ -89,3 +89,12 @@ if echo "$PACKAGE_DISTROS" | grep -q "bionic"; then
89 sed -i "s/) UNRELEASED;/~18.04.1) bionic;/i" debian/changelog89 sed -i "s/) UNRELEASED;/~18.04.1) bionic;/i" debian/changelog
90 debuild -S -sa90 debuild -S -sa
91fi91fi
92<<<<<<< utilities/release-build
93=======
94
95if echo "$PACKAGE_DISTROS" | grep -q "artful"; then
96 cp $ROOTDIR/debian/changelog debian/changelog
97 sed -i "s/) UNRELEASED;/~17.10.1) artful;/" debian/changelog
98 debuild -S
99fi
100>>>>>>> utilities/release-build

Subscribers

People subscribed via source and target branches