Merge ~andreserl/maas:2.3_chassis_config_on_power_on into maas:master
- Git
- lp:~andreserl/maas
- 2.3_chassis_config_on_power_on
- Merge into master
Status: | Superseded |
---|---|
Proposed branch: | ~andreserl/maas:2.3_chassis_config_on_power_on |
Merge into: | maas:master |
Diff against target: |
1981 lines (+1604/-3) (has conflicts) 22 files modified
debian/changelog (+22/-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/static/partials/subnet-details.html (+40/-0) src/maasserver/static/partials/vlan-details.html (+42/-0) src/maasserver/tests/test_bootsources.py (+3/-0) src/maasserver/tests/test_stats.py (+1/-1) src/maasserver/triggers/tests/test_websocket_listener.py (+5/-0) src/metadataserver/user_data/templates/commissioning.template (+4/-0) src/provisioningserver/drivers/power/ipmi.py (+4/-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/static/partials/subnet-details.html Conflict in src/maasserver/static/partials/vlan-details.html Conflict in src/maasserver/tests/test_bootsources.py Conflict in src/maasserver/triggers/tests/test_websocket_listener.py Conflict in src/provisioningserver/drivers/power/ipmi.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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Maintainers | Pending | ||
Review via email: mp+343188@code.launchpad.net |
Commit message
Description of the change
Unmerged commits
- 8c67adc... by Andres Rodriguez
-
Only issue ipmi_chassis_config on power on.
- 914c0b2... by Mike Pontillo
-
LP: #1753493 - Make region IP addresses consistent for HA.
Backports: 5000bb58ea91af4
1b2465dc3d17aa2 388a5edbaa - f745516... by Andres Rodriguez
-
Open 2.3.3; Update changelog to reflect release
- e93e044... by Lee Trager
-
Backport: 89f12d9 LP: #1738127 Add favicon to the UI
- c948da2... by Andres Rodriguez
-
Backport c70ed068a and 84a9fd27e
LP: #1750622 - Add ability to force the BIOS boot method, and auto discover it during enlistment/
commissioning - 7b7104a... by Mike Pontillo
-
Fix random failures in test_stats.py.
- 007a4fe... by Andres Rodriguez
-
Backport d2f6dd9cff9072c
27549fd9aff6e82 994a5f0ebe from master Calculate the total amount of machine resources.-
- 7eb70a8... by Mike Pontillo
-
LP: #1704501 - Allow users to change which Fabric a VLAN is on.
Backports: 4083dc063595810
9a28ef44a84df63 e855007335 - 06b71f4... by Mike Pontillo
-
LP #1755587 - Allow moving a subnet to a different fabric in the UI.
Backports: 544aaae99fdb595
4762980c2d93f5a e5b12d0b8a - 13492bb... by Andres Rodriguez
-
debian/changelog: Update to correctly create dailybuilds
Preview Diff
1 | diff --git a/debian/changelog b/debian/changelog |
2 | index 35034de..725fbd6 100644 |
3 | --- a/debian/changelog |
4 | +++ b/debian/changelog |
5 | @@ -1,3 +1,4 @@ |
6 | +<<<<<<< debian/changelog |
7 | maas (2.4.0~beta2-0ubuntu1) UNRELEASED; urgency=medium |
8 | |
9 | * UNRELEASED |
10 | @@ -36,6 +37,27 @@ maas (2.4.0~alpha1-6573-g12ee2331b-0ubuntu1) bionic; urgency=medium |
11 | -- Andres Rodriguez <andreserl@ubuntu.com> Fri, 09 Feb 2018 18:50:10 -0500 |
12 | |
13 | maas (2.3.0-6434-gd354690-0ubuntu1) bionic; urgency=medium |
14 | +======= |
15 | +maas (2.3.2-0ubuntu1) UNRELEASED; urgency=medium |
16 | + |
17 | + * UNRELEASED |
18 | + |
19 | + -- Andres Rodriguez <andreserl@ubuntu.com> Fri, 06 Apr 2018 09:56:53 -0400 |
20 | + |
21 | +maas (2.3.2-6485-ge93e044-0ubuntu1) artful; urgency=medium |
22 | + |
23 | + * New upstream release, MAAS 2.3.2 |
24 | + |
25 | + -- Andres Rodriguez <andreserl@ubuntu.com> Fri, 06 Apr 2018 09:55:14 -0400 |
26 | + |
27 | +maas (2.3.1-6470-g036d646-0ubuntu1) artful; urgency=medium |
28 | + |
29 | + * New upstream release, MAAS 2.3.1 |
30 | + |
31 | + -- Andres Rodriguez <andreserl@ubuntu.com> Mon, 05 Mar 2018 10:25:44 -0500 |
32 | + |
33 | +maas (2.3.0-6434-gd354690-0ubuntu1) artful; urgency=medium |
34 | +>>>>>>> debian/changelog |
35 | |
36 | * New upstream release, MAAS 2.3.0: |
37 | - Add support for CentOS & Windows networking. |
38 | diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml |
39 | index 913fb0b..e277a0c 100644 |
40 | --- a/snap/snapcraft.yaml |
41 | +++ b/snap/snapcraft.yaml |
42 | @@ -131,6 +131,15 @@ parts: |
43 | source: src/maasserver/static |
44 | organize: |
45 | '*': usr/share/maas/web/static/ |
46 | +<<<<<<< snap/snapcraft.yaml |
47 | +======= |
48 | + twisted-plugins: |
49 | + plugin: dump |
50 | + source: twisted/plugins |
51 | + organize: |
52 | + maasrackd.py: usr/lib/python3/dist-packages/twisted/plugins/maasrackd.py |
53 | + maasregiond.py: usr/lib/python3/dist-packages/twisted/plugins/maasregiond.py |
54 | +>>>>>>> snap/snapcraft.yaml |
55 | snap: |
56 | plugin: dump |
57 | source: snap |
58 | diff --git a/src/maasserver/bootsources.py b/src/maasserver/bootsources.py |
59 | index f71040f..1e11784 100644 |
60 | --- a/src/maasserver/bootsources.py |
61 | +++ b/src/maasserver/bootsources.py |
62 | @@ -26,7 +26,10 @@ from maasserver.models import ( |
63 | Config, |
64 | Notification, |
65 | ) |
66 | +<<<<<<< src/maasserver/bootsources.py |
67 | from maasserver.models.timestampedmodel import now |
68 | +======= |
69 | +>>>>>>> src/maasserver/bootsources.py |
70 | from maasserver.utils import get_maas_user_agent |
71 | from maasserver.utils.orm import transactional |
72 | from maasserver.utils.threads import deferToDatabase |
73 | diff --git a/src/maasserver/models/tests/test_userprofile.py b/src/maasserver/models/tests/test_userprofile.py |
74 | index 8a0efac..4bf4730 100644 |
75 | --- a/src/maasserver/models/tests/test_userprofile.py |
76 | +++ b/src/maasserver/models/tests/test_userprofile.py |
77 | @@ -139,6 +139,7 @@ class UserProfileTest(MAASServerTestCase): |
78 | self.assertEqual(reload_object(node).owner, new_user) |
79 | self.assertEqual(reload_object(ipaddress).user, new_user) |
80 | self.assertEqual(reload_object(iprange).user, new_user) |
81 | +<<<<<<< src/maasserver/models/tests/test_userprofile.py |
82 | |
83 | def test_transfer_resources_missing_target_access(self): |
84 | user = factory.make_User() |
85 | @@ -153,6 +154,8 @@ class UserProfileTest(MAASServerTestCase): |
86 | " resource pool(s)") |
87 | # owner didn't change |
88 | self.assertEqual(reload_object(node).owner, user) |
89 | +======= |
90 | +>>>>>>> src/maasserver/models/tests/test_userprofile.py |
91 | |
92 | def test_manager_all_users(self): |
93 | users = set(factory.make_User() for _ in range(3)) |
94 | diff --git a/src/maasserver/rpc/boot.py b/src/maasserver/rpc/boot.py |
95 | index 5b5f1de..9a51bf3 100644 |
96 | --- a/src/maasserver/rpc/boot.py |
97 | +++ b/src/maasserver/rpc/boot.py |
98 | @@ -280,10 +280,15 @@ def get_config( |
99 | osystem = configs['commissioning_osystem'] |
100 | series = configs['commissioning_distro_series'] |
101 | else: |
102 | +<<<<<<< src/maasserver/rpc/boot.py |
103 | osystem = machine.get_osystem( |
104 | default=configs['default_osystem']) |
105 | series = machine.get_distro_series( |
106 | default=configs['default_distro_series']) |
107 | +======= |
108 | + osystem = machine.get_osystem() |
109 | + series = machine.get_distro_series() |
110 | +>>>>>>> src/maasserver/rpc/boot.py |
111 | # XXX: roaksoax LP: #1739761 - Since the switch to squashfs (and |
112 | # drop of iscsi), precise is no longer deployable. To address a |
113 | # squashfs image is made available allowing it to be deployed in |
114 | 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 |
115 | index 90845d5..101cefd 100644 |
116 | --- a/src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js |
117 | +++ b/src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js |
118 | @@ -1584,6 +1584,7 @@ describe("NodesListController", function() { |
119 | it("sets showing_confirmation with testOptions", |
120 | function() { |
121 | var controller = makeController(); |
122 | +<<<<<<< src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js |
123 | var object = makeObject("machines"); |
124 | object.status_code = 6; |
125 | var spy = spyOn( |
126 | @@ -1598,6 +1599,22 @@ describe("NodesListController", function() { |
127 | true); |
128 | expect($scope.tabs[ |
129 | "machines"].actionProgress.affected_nodes).toBe(1); |
130 | +======= |
131 | + var object = makeObject("nodes"); |
132 | + object.status_code = 6; |
133 | + var spy = spyOn( |
134 | + $scope.tabs.nodes.manager, |
135 | + "performAction").and.returnValue( |
136 | + $q.defer().promise); |
137 | + $scope.tabs.nodes.actionOption = { name: "test" }; |
138 | + $scope.tabs.nodes.selectedItems = [object]; |
139 | + $scope.actionGo("nodes"); |
140 | + expect($scope.tabs[ |
141 | + "nodes"].actionProgress.showing_confirmation).toBe( |
142 | + true); |
143 | + expect($scope.tabs[ |
144 | + "nodes"].actionProgress.affected_nodes).toBe(1); |
145 | +>>>>>>> src/maasserver/static/js/angular/controllers/tests/test_nodes_list.js |
146 | expect(spy).not.toHaveBeenCalled(); |
147 | }); |
148 | |
149 | diff --git a/src/maasserver/static/partials/ipranges.html b/src/maasserver/static/partials/ipranges.html |
150 | index b159b50..a8cb02b 100755 |
151 | --- a/src/maasserver/static/partials/ipranges.html |
152 | +++ b/src/maasserver/static/partials/ipranges.html |
153 | @@ -15,6 +15,7 @@ |
154 | <tbody> |
155 | <tr data-ng-repeat="iprange in (subnetIPRanges = ipranges | filterBySubnetOrVlan:subnet:vlan) | orderBy:ipRangeSort" |
156 | data-ng-class="{ 'is-active': isIPRangeInEditMode(iprange) || isIPRangeInDeleteMode(iprange)}"> |
157 | +<<<<<<< src/maasserver/static/partials/ipranges.html |
158 | <td class="col-2" aria-label="Start IP Address">{$ iprange.start_ip $}</td> |
159 | <td class="col-2" aria-label="End IP Address">{$ iprange.end_ip $}</td> |
160 | <td class="col-1" aria-label="Owner">{$ iprange.type == "dynamic" ? "MAAS" : iprange.user $}</td> |
161 | @@ -30,6 +31,22 @@ |
162 | data-ng-click="toggleMenu(); ipRangeEnterDeleteMode(iprange)"> |
163 | <i class="p-icon--delete">Remove</i> |
164 | </button> |
165 | +======= |
166 | + <div class="table__data table-col--20" aria-label="Start IP Address">{$ iprange.start_ip $}</div> |
167 | + <div class="table__data table-col--20" aria-label="End IP Address">{$ iprange.end_ip $}</div> |
168 | + <div class="table__data table-col--10" aria-label="Owner">{$ iprange.type == "dynamic" ? "MAAS" : iprange.user $}</div> |
169 | + <div class="table__data table-col--10" aria-label="Type">{$ iprange.type == "dynamic" ? "Dynamic" : "Reserved" $}</div> |
170 | + <div class="table__data table-col--31" aria-label="Comment">{$ iprange.type == "dynamic" ? "Dynamic" : iprange.comment $}</div> |
171 | + <div class="table__data table-col--9 table--mobile-controls"> |
172 | + <div class="table__controls" toggle-ctrl data-ng-if="ipRangeCanBeModified(iprange)"> |
173 | + <button class="table__controls-toggle" data-ng-click="toggleMenu()">View actions</button> |
174 | + <div class="table__controls-menu" role="menu" data-ng-show="isToggled"> |
175 | + <button class="table__controls-action" aria-label="Edit row" |
176 | + data-ng-click="toggleMenu(); ipRangeToggleEditMode(iprange)">Edit reserved range</button> |
177 | + <button class="table__controls-action u-text--error" aria-label="Remove" |
178 | + data-ng-click="toggleMenu(); ipRangeEnterDeleteMode(iprange)">Remove range</button> |
179 | + </div> |
180 | +>>>>>>> src/maasserver/static/partials/ipranges.html |
181 | </div> |
182 | </td> |
183 | <td class="is-active p-table-expanding__panel col-12" col-span="6" data-ng-if="isIPRangeInDeleteMode(iprange)"> |
184 | diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html |
185 | index ed62f6f..c52716d 100755 |
186 | --- a/src/maasserver/static/partials/node-details.html |
187 | +++ b/src/maasserver/static/partials/node-details.html |
188 | @@ -98,6 +98,7 @@ |
189 | </ul> |
190 | </div> |
191 | |
192 | +<<<<<<< src/maasserver/static/partials/node-details.html |
193 | <div class="col-12 u-no-margin--left" data-ng-show="action.option.name === 'deploy' || action.option.name === 'release'"> |
194 | <div class="ng-hide" data-ng-show="action.option.name === 'deploy'"> |
195 | <div data-maas-os-select="osinfo" data-ng-model="osSelection"></div> |
196 | @@ -235,6 +236,139 @@ |
197 | </div> |
198 | </div> |
199 | </div> |
200 | +======= |
201 | + <!-- XXX blake_r 2015-02-19 - Need to add e2e test. --> |
202 | + <div class="page-header__dropdown" data-ng-class="{ 'is-open': action.option }"> |
203 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-if="!isDevice && !node.dhcp_on"> |
204 | + <p class="page-header__message page-header__message--warning">MAAS is not providing DHCP.</p> |
205 | + </div> |
206 | + |
207 | + <!-- XXX blake_r 2015-02-19 - Need to add e2e test. --> |
208 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name)"> |
209 | + <form class="form form--inline u-display--inline"> |
210 | + <fieldset class="form__fieldset ng-hide" data-ng-show="action.option.name === 'commission' || action.option.name === 'test'"> |
211 | + <div class="form__group"> |
212 | + <input class="checkbox" id="enableSSH" type="checkbox" |
213 | + data-ng-model="commissionOptions.enableSSH"> |
214 | + <label for="enableSSH">Allow SSH access and prevent machine from powering off</label> |
215 | + </div> |
216 | + </fieldset> |
217 | + <fieldset class="form__fieldset ng-hide" data-ng-show="action.option.name === 'commission'"> |
218 | + <div class="form__group"> |
219 | + <input class="checkbox" id="skipNetworking" type="checkbox" |
220 | + data-ng-model="commissionOptions.skipNetworking"> |
221 | + <label for="skipNetworking">Retain network configuration</label> |
222 | + </div> |
223 | + <div class="form__group"> |
224 | + <input class="checkbox" id="skipStorage" type="checkbox" |
225 | + data-ng-model="commissionOptions.skipStorage"> |
226 | + <label for="skipStorage">Retain storage configuration</label> |
227 | + </div> |
228 | + </fieldset> |
229 | + <fieldset class="form__fieldset ng-hide" data-ng-show="action.option.name === 'deploy'"> |
230 | + <div class="form__group"> |
231 | + <label class="form__group-label">Choose your image</label> |
232 | + <div class="form__group-input" data-maas-os-select="osinfo" data-ng-model="osSelection"></div> |
233 | + </div> |
234 | + </fieldset> |
235 | + <fieldset class="form__fieldset" data-ng-if="action.option.name === 'release'"> |
236 | + <div class="form__group"> |
237 | + <div data-maas-release-options="releaseOptions"></div> |
238 | + </div> |
239 | + </fieldset> |
240 | + <div class="page-header__controls" data-ng-if="action.option.name !== 'commission' && action.option.name !== 'test'"> |
241 | + <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button> |
242 | + <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')"> |
243 | + <span data-ng-if="action.option.name === 'acquire'">Acquire {$ type_name $}</span> |
244 | + <span data-ng-if="action.option.name === 'deploy'">Deploy {$ type_name $}</span> |
245 | + <span data-ng-if="action.option.name === 'release'">Release {$ type_name $}</span> |
246 | + <span data-ng-if="action.option.name === 'set-zone'">Set zone for {$ type_name $}</span> |
247 | + <span data-ng-if="action.option.name === 'on'">Power on {$ type_name $}</span> |
248 | + <span data-ng-if="action.option.name === 'off'">Power off {$ type_name $}</span> |
249 | + <span data-ng-if="action.option.name === 'abort'">Abort action on {$ type_name $}</span> |
250 | + <span data-ng-if="action.option.name === 'rescue-mode'">Rescue {$ type_name $}</span> |
251 | + <span data-ng-if="action.option.name === 'exit-rescue-mode'">Exit rescue mode</span> |
252 | + <span data-ng-if="action.option.name === 'mark-broken'">Mark {$ type_name $}</span> |
253 | + <span data-ng-if="action.option.name === 'mark-fixed'">Mark {$ type_name $}</span> |
254 | + <span data-ng-if="action.option.name === 'override-failed-testing'">Override failed testing</span> |
255 | + <span data-ng-if="action.option.name === 'delete'">Delete {$ type_name $}</span> |
256 | + <span data-ng-if="action.option.name === 'import-images'">Import images</span> |
257 | + </button> |
258 | + </div> |
259 | + </form> |
260 | + </div> |
261 | + <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()"> |
262 | + <form class="form form--stack"> |
263 | + <fieldset class="form__fieldset eight-col u-margin--bottom-small"> |
264 | + <div class="form__group"> |
265 | + <label for="commissioning-scripts">Additional commissioning scripts</label> |
266 | + <span id="commissioning-scripts" data-maas-script-select="script" data-script-type="0" data-ng-model="commissioningSelection" class="tags--inline"></span> |
267 | + </div> |
268 | + </fieldset> |
269 | + </form> |
270 | + </div> |
271 | + <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')"> |
272 | + <form class="form form--stack"> |
273 | + <fieldset class="form__fieldset eight-col u-margin--bottom-small"> |
274 | + <div class="form__group"> |
275 | + <label>Hardware tests</label> |
276 | + <span id="testing-scripts" data-maas-script-select="script" data-script-type="2" data-ng-model="testSelection" class="tags--inline"></span> |
277 | + </div> |
278 | + </fieldset> |
279 | + </form> |
280 | + </div> |
281 | + <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'"> |
282 | + <form class="form form--inline"> |
283 | + <div class="page-header__controls"> |
284 | + <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button> |
285 | + <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')"> |
286 | + <span data-ng-if="action.option.name === 'commission'">Commission {$ type_name $}</span> |
287 | + <span data-ng-if="action.option.name === 'test'">Test {$ type_name $}</span> |
288 | + </button> |
289 | + </div> |
290 | + </form> |
291 | + </div> |
292 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="action.showing_confirmation && action.option.name === 'test'"> |
293 | + <p class="page-header__message page-header__message--warning"> |
294 | + Node is currently deployed. Are you sure you want to continue to test hardware? |
295 | + </p> |
296 | + <div class="page-header__controls"> |
297 | + <button class="button--base button--inline" data-ng-click="actionCancel()">No</button> |
298 | + <button class="button--secondary button--inline" data-ng-click="actionGo()">Yes</button> |
299 | + </div> |
300 | + </div> |
301 | + <!-- XXX blake_r 2015-02-19 - Need to add e2e test. --> |
302 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isActionError()"> |
303 | + <p class="page-header__message page-header__message--error"> |
304 | + Node failed to be {$ action.option.sentence $}, because of the following error: {$ action.error $} |
305 | + </p> |
306 | + <div class="page-header__controls"> |
307 | + <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button> |
308 | + <button class="button--secondary button--inline" data-ng-click="actionGo()">Retry</button> |
309 | + </div> |
310 | + </div> |
311 | + |
312 | + <!-- XXX blake_r 2015-02-19 - Need to add e2e test. --> |
313 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isDeployError()"> |
314 | + <p class="page-header__message page-header__message--error"> |
315 | + 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>. |
316 | + </p> |
317 | + <div class="page-header__controls"> |
318 | + <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button> |
319 | + </div> |
320 | + </div> |
321 | + |
322 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="hasActionPowerError(action.option.name)"> |
323 | + <p class="page-header__message page-header__message--error"> |
324 | + Node cannot be {$ action.option.sentence $}, because power control software for the |
325 | + node is missing from its rack controller. To proceed, install the |
326 | + {$ getPowerErrors() $} on the rack controller. |
327 | + </p> |
328 | + <div class="page-header__controls"> |
329 | + <button class="button--base button--inline" data-ng-click="actionCancel()">Cancel</button> |
330 | + </div> |
331 | + </div> |
332 | +>>>>>>> src/maasserver/static/partials/node-details.html |
333 | |
334 | <nav class="p-tabs"> |
335 | <ul class="p-tabs__list" role="tablist" data-ng-class="{ 'u-hide': action.option }"> |
336 | diff --git a/src/maasserver/static/partials/nodes-list.html b/src/maasserver/static/partials/nodes-list.html |
337 | index d49f11c..e0ff5be 100644 |
338 | --- a/src/maasserver/static/partials/nodes-list.html |
339 | +++ b/src/maasserver/static/partials/nodes-list.html |
340 | @@ -1,3 +1,4 @@ |
341 | +<<<<<<< src/maasserver/static/partials/nodes-list.html |
342 | <header class="p-strip--light is-shallow is-bordered page-header" media-query="min-width: 769px"> |
343 | <div class="row"> |
344 | <div class="col-8"> |
345 | @@ -1072,3 +1073,1019 @@ sudo maas-rack register --url {$ tabs.controllers.registerUrl $} --secret {$ tab |
346 | on-check-all="toggleCheckAll('switches')" on-check="toggleChecked($switch_, 'switches')"></maas-switches-table> |
347 | </div> |
348 | </div> |
349 | +======= |
350 | +<header class="page-header u-margin--bottom-none" sticky media-query="min-width: 769px"> |
351 | + <div class="wrapper--inner"> |
352 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
353 | + <h1 class="page-header__title">Nodes</h1> |
354 | + |
355 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
356 | + <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> |
357 | + |
358 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
359 | + <div class="page-header__controls u-float--right ng-hide" data-ng-show="currentpage === 'nodes'"> |
360 | + <div data-ng-hide="tabs.nodes.selectedItems.length"> |
361 | + <div data-maas-cta="addHardwareOptions" |
362 | + data-ng-model="addHardwareOption" |
363 | + data-ng-change="addHardwareOptionChanged()" data-default-title="Add hardware"> |
364 | + </div> |
365 | + </div> |
366 | + <div class="ng-hide" data-ng-show="tabs.nodes.selectedItems.length"> |
367 | + <a class="u-display--inline u-margin--right" data-ng-click="showSelected('nodes')">{$ tabs.nodes.selectedItems.length $} Selected</a> |
368 | + <div data-maas-cta="tabs.nodes.takeActionOptions" |
369 | + data-ng-model="tabs.nodes.actionOption" |
370 | + data-ng-change="actionOptionSelected('nodes')"> |
371 | + </div> |
372 | + </div> |
373 | + </div> |
374 | + |
375 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
376 | + <div class="page-header__controls u-float--rightng-hide" data-ng-show="currentpage === 'devices'"> |
377 | + <div data-ng-hide="tabs.devices.selectedItems.length"> |
378 | + <button class="button--secondary button--inline" |
379 | + data-ng-click="addDevice()" |
380 | + data-ng-hide="addDeviceScope.viewable">Add device</button> |
381 | + <button class="button--secondary button--inline ng-hide" |
382 | + data-ng-click="cancelAddDevice()" |
383 | + data-ng-show="addDeviceScope.viewable">Cancel add device</button> |
384 | + </div> |
385 | + <div data-ng-show="tabs.devices.selectedItems.length"> |
386 | + <a class="u-display--inline u-margin--right" data-ng-click="showSelected('devices')">{$ tabs.devices.selectedItems.length $} Selected</a> |
387 | + <div data-maas-cta="tabs.devices.takeActionOptions" |
388 | + data-ng-model="tabs.devices.actionOption" |
389 | + data-ng-change="actionOptionSelected('devices')"> |
390 | + </div> |
391 | + </div> |
392 | + </div> |
393 | + |
394 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
395 | + <div class="page-header__controls u-float--rightng-hide" data-ng-show="currentpage === 'controllers'"> |
396 | + <div data-ng-if="!tabs.controllers.selectedItems.length"> |
397 | + <button class="button--secondary button--inline" |
398 | + data-ng-click="tabs.controllers.addController = true" |
399 | + data-ng-hide="tabs.controllers.addController">Add rack controller</button> |
400 | + <button class="button--secondary button--inline ng-hide" |
401 | + data-ng-click="tabs.controllers.addController = false" |
402 | + data-ng-show="tabs.controllers.addController">Close add rack controller</button> |
403 | + </div> |
404 | + <div data-ng-show="tabs.controllers.selectedItems.length"> |
405 | + <a class="u-display--inline u-margin--right" data-ng-click="showSelected('controllers')">{$ tabs.controllers.selectedItems.length $} Selected</a> |
406 | + <div data-maas-cta="tabs.controllers.takeActionOptions" |
407 | + data-ng-model="tabs.controllers.actionOption" |
408 | + data-ng-change="actionOptionSelected('controllers')"> |
409 | + </div> |
410 | + </div> |
411 | + </div> |
412 | + |
413 | + <div class="page-header__controls u-float--right ng-hide" data-ng-show="currentpage === 'switches'"> |
414 | + <div class="ng-hide" data-ng-show="tabs.switches.selectedItems.length"> |
415 | + <a class="u-display--inline u-margin--right" data-ng-click="showSelected('switches')">{$ tabs.switches.selectedItems.length $} Selected</a> |
416 | + <div data-maas-cta="tabs.switches.takeActionOptions" |
417 | + data-ng-model="tabs.switches.actionOption" |
418 | + data-ng-change="actionOptionSelected('switches')"> |
419 | + </div> |
420 | + </div> |
421 | + </div> |
422 | + |
423 | + <div data-ng-repeat="tab in ['nodes', 'switches']" data-ng-show="currentpage == tab"> |
424 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
425 | + <div class="page-header__dropdown" data-ng-class="{ 'is-open': tabs[tab].actionOption }"> |
426 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
427 | + <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="isActionError(tab) || hasActionsInProgress(tab)"> |
428 | + <form class="form form--inline u-display--inline"> |
429 | + <fieldset class="form__fieldset ng-hide" data-ng-show="tabs[tab].actionOption.name === 'commission' || tabs[tab].actionOption.name === 'test'"> |
430 | + <div class="form__group"> |
431 | + <input class="form__group-label" id="{$ tab $}-enableSSH" type="checkbox" |
432 | + data-ng-model="tabs[tab].commissionOptions.enableSSH"> |
433 | + <label class="checkbox-label" for="{$ tab $}-enableSSH">Allow SSH access and prevent machine from powering off</label> |
434 | + </div> |
435 | + </fieldset> |
436 | + <fieldset class="form__fieldset ng-hide" data-ng-show="tabs[tab].actionOption.name === 'commission'"> |
437 | + <div class="form__group"> |
438 | + <input class="form__group-label" id="{$ tab $}-skipNetworking" type="checkbox" |
439 | + data-ng-model="tabs[tab].commissionOptions.skipNetworking"> |
440 | + <label class="checkbox-label" for="{$ tab $}-skipNetworking">Retain network configuration</label> |
441 | + </div> |
442 | + <div class="form__group"> |
443 | + <input class="form__group-label" id="{$ tab $}-skipStorage" type="checkbox" |
444 | + data-ng-model="tabs[tab].commissionOptions.skipStorage"> |
445 | + <label class="checkbox-label" for="{$ tab $}-skipStorage">Retain storage configuration</label> |
446 | + </div> |
447 | + </fieldset> |
448 | + <fieldset class="form__fieldset ng-hide" data-ng-show="tabs[tab].actionOption.name === 'deploy'"> |
449 | + <div class="form__group"> |
450 | + <label for="image" class="form__group-label">Choose your image</label> |
451 | + <div class="form__group-input" data-maas-os-select="osinfo" data-ng-model="tabs[tab].osSelection"></div> |
452 | + </div> |
453 | + </fieldset> |
454 | + <fieldset class="form__fieldset" data-ng-if="tabs[tab].actionOption.name === 'release'"> |
455 | + <div class="form__group"> |
456 | + <div data-maas-release-options="tabs[tab].releaseOptions"></div> |
457 | + </div> |
458 | + </fieldset> |
459 | + <fieldset class="form__fieldset ng-hide" data-ng-show="tabs[tab].actionOption.name === 'set-zone'"> |
460 | + <div class="form__group"> |
461 | + <label for="{$ tab $}-zone3" class="form__group-label">Select Zone</label> |
462 | + <div class="form__group-input"> |
463 | + <select name="zone" id="{$ tab $}-zone3" |
464 | + data-ng-model="tabs[tab].zoneSelection" |
465 | + data-ng-options="zone as zone.name for zone in zones"> |
466 | + <option value="" disabled="disabled">Choose a zone</option> |
467 | + </select> |
468 | + </div> |
469 | + </div> |
470 | + </fieldset> |
471 | + </form> |
472 | + <div class="page-header__controls" data-ng-if="tabs[tab].actionOption.name !== 'commission' && tabs[tab].actionOption.name !== 'test'"> |
473 | + <button class="button--base button--inline" data-ng-click="actionCancel(tab)">Cancel</button> |
474 | + <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)"> |
475 | + <span data-ng-if="tabs[tab].actionOption.name === 'acquire'">Acquire {$ tabs[tab].selectedItems.length $} |
476 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
477 | + </span> |
478 | + <span data-ng-if="tabs[tab].actionOption.name === 'deploy'">Deploy {$ tabs[tab].selectedItems.length $} |
479 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
480 | + </span> |
481 | + <span data-ng-if="tabs[tab].actionOption.name === 'release'">Release {$ tabs[tab].selectedItems.length $} |
482 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
483 | + </span> |
484 | + <span data-ng-if="tabs[tab].actionOption.name === 'set-zone'">Set zone for {$ tabs[tab].selectedItems.length $} |
485 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
486 | + </span> |
487 | + <span data-ng-if="tabs[tab].actionOption.name === 'on'">Power on {$ tabs[tab].selectedItems.length $} |
488 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
489 | + </span> |
490 | + <span data-ng-if="tabs[tab].actionOption.name === 'off'">Power off {$ tabs[tab].selectedItems.length $} |
491 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
492 | + </span> |
493 | + <span data-ng-if="tabs[tab].actionOption.name === 'abort'">Abort action for {$ tabs[tab].selectedItems.length $} |
494 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
495 | + </span> |
496 | + <span data-ng-if="tabs[tab].actionOption.name === 'rescue-mode'">Set rescue mode for {$ tabs[tab].selectedItems.length $} |
497 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
498 | + </span> |
499 | + <span data-ng-if="tabs[tab].actionOption.name === 'exit-rescue-mode'">Exit rescue mode for {$ tabs[tab].selectedItems.length $} |
500 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
501 | + </span> |
502 | + <span data-ng-if="tabs[tab].actionOption.name === 'mark-broken'">Mark {$ tabs[tab].selectedItems.length $} |
503 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> as broken |
504 | + </span> |
505 | + <span data-ng-if="tabs[tab].actionOption.name === 'mark-fixed'">Mark {$ tabs[tab].selectedItems.length $} |
506 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> as fixed |
507 | + </span> |
508 | + <span data-ng-if="tabs[tab].actionOption.name === 'override-failed-testing'">Override failed testing on {$ tabs[tab].selectedItems.length $} |
509 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
510 | + </span> |
511 | + <span data-ng-if="tabs[tab].actionOption.name === 'delete'">Delete {$ tabs[tab].selectedItems.length $} |
512 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
513 | + </span> |
514 | + </button> |
515 | + <button class="button--secondary button--inline" data-ng-click="actionGo(tab)" data-ng-show="hasActionsFailed(tab)">Retry</button> |
516 | + </div> |
517 | + </section> |
518 | + <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()"> |
519 | + <form class="form form--stack"> |
520 | + <fieldset class="form__fieldset eight-col u-margin--bottom-small"> |
521 | + <div class="form__group"> |
522 | + <label for="{$ tab $}-commissiong-scripts">Additional commissioning Scripts</label> |
523 | + <span id="{$ tab $}-commissioning-scripts" data-maas-script-select="script" data-script-type="0" data-ng-model="tabs[tab].commissioningSelection" class="tags--inline"></span> |
524 | + </div> |
525 | + </fieldset> |
526 | + </form> |
527 | + </section> |
528 | + <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'"> |
529 | + <form class="form form--stack"> |
530 | + <fieldset class="form__fieldset eight-col u-margin--bottom-small"> |
531 | + <div class="form__group"> |
532 | + <label for="{$ tab $}-testing-scripts">Hardware Tests</label> |
533 | + <span id="{$ tab $}-testing-scripts" data-maas-script-select="script" data-script-type="2" data-ng-model="tabs[tab].testSelection" class="tags--inline"></span> |
534 | + </div> |
535 | + </fieldset> |
536 | + </form> |
537 | + </section> |
538 | + <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'"> |
539 | + <div class="page-header__controls"> |
540 | + <button class="button--base button--inline" data-ng-click="actionCancel(tab)">Cancel</button> |
541 | + <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)"> |
542 | + <span data-ng-if="tabs[tab].actionOption.name === 'commission'">Commission {$ tabs[tab].selectedItems.length $} |
543 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
544 | + </span> |
545 | + <span data-ng-if="tabs[tab].actionOption.name === 'test'">Test {$ tabs[tab].selectedItems.length $} |
546 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
547 | + </span> |
548 | + </button> |
549 | + <button class="button--secondary button--inline" data-ng-click="actionGo(tab)" data-ng-show="hasActionsFailed(tab)">Retry</button> |
550 | + </div> |
551 | + </section> |
552 | + |
553 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
554 | + <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isActionError(tab)"> |
555 | + <p data-ng-hide="isDeployError(tab) || isSSHKeyError(tab)" |
556 | + class="page-header__message page-header__message--error ng-hide"> |
557 | + {$ tabs[tab].actionErrorCount $} |
558 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'node', 'other': 'nodes'}"></span> |
559 | + cannot be {$ tabs[tab].actionOption.sentence $}. To proceed, update your selection. |
560 | + </p> |
561 | + <p class="page-header__message page-header__message--error ng-hide" data-ng-show="isDeployError(tab)"> |
562 | + {$ tabs[tab].selectedItems.length $} |
563 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'node', 'other': 'nodes'}"></span> |
564 | + 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>. |
565 | + </p> |
566 | + <p class="page-header__message page-header__message--error ng-hide" data-ng-show="isSSHKeyError(tab)"> |
567 | + {$ tabs[tab].selectedItems.length $} |
568 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'node', 'other': 'nodes'}"></span> |
569 | + 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>. |
570 | + </p> |
571 | + </section> |
572 | + <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="tabs[tab].actionProgress.showing_confirmation"> |
573 | + <p class="page-header__message page-header__message--warning"> |
574 | + {$ 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? |
575 | + </p> |
576 | + <div class="page-header__controls"> |
577 | + <button class="button--base button--inline" data-ng-click="actionCancel(tab)">No</button> |
578 | + <button class="button--secondary button--inline" data-ng-click="actionGo(tab)">Yes</button> |
579 | + </div> |
580 | + </section> |
581 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
582 | + <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="hasActionsInProgress(tab) || hasActionsFailed(tab)"> |
583 | + <p class="page-header__message" data-ng-show="hasActionsInProgress(tab)"> |
584 | + <i class="icon icon--loading u-animation--spin"></i> |
585 | + {$ tabs[tab].actionProgress.completed $} of {$ tabs[tab].actionProgress.total $} |
586 | + nodes are transitioning to {$ tabs[tab].actionOption.sentence $}. |
587 | + </p> |
588 | + <p class="page-header__message page-header__message--error" |
589 | + data-ng-repeat="(error, nodes) in tabs[tab].actionProgress.errors"> |
590 | + The {$ tabs[tab].actionOption.title.toLowerCase() $} action for {$ nodes.length $} |
591 | + <span data-ng-pluralize count="nodes.length" when="{'one': 'node', 'other': 'nodes'}"></span> |
592 | + failed with error: {$ error $} |
593 | + </p> |
594 | + </section> |
595 | + </div> |
596 | + |
597 | + </div> |
598 | + |
599 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
600 | + <div class="page-header__dropdown" data-ng-class="{ 'is-open': addHardwareScope.viewable }" data-ng-controller="AddHardwareController"> |
601 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
602 | + <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="showMachine()"> |
603 | + <h3 class="page-header__dropdown-title">Add machine</h3> |
604 | + <form class="form form--stack"> |
605 | + <fieldset class="form__fieldset six-col"> |
606 | + <div class="form__group"> |
607 | + <label for="machine-name" class="form__label two-col">Machine name</label> |
608 | + <div class="form__group-input three-col"> |
609 | + <input type="text" id="machine-name" placeholder="Choose a machine name (optional)" |
610 | + data-ng-model="machine.name"> |
611 | + </div> |
612 | + </div> |
613 | + <div class="form__group"> |
614 | + <label for="domain" class="form__group-label two-col">Domain</label> |
615 | + <div class="form__group-input three-col"> |
616 | + <select name="domain" id="domain" |
617 | + data-ng-model="machine.domain" |
618 | + data-ng-options="domain as domain.name for domain in domains"> |
619 | + <option value="" disabled>Choose a domain</option> |
620 | + </select> |
621 | + </div> |
622 | + </div> |
623 | + <div class="form__group"> |
624 | + <label for="architecture" class="form__group-label two-col">Architecture</label> |
625 | + <div class="form__group-input three-col"> |
626 | + <select name="architecture" id="architecture" |
627 | + data-ng-model="machine.architecture" |
628 | + data-ng-options="arch for arch in architectures"> |
629 | + <option value="" disabled>Choose an architecture</option> |
630 | + </select> |
631 | + </div> |
632 | + </div> |
633 | + <div class="form__group"> |
634 | + <label for="min_hwe_kernel" class="form__group-label two-col">Minimum Kernel</label> |
635 | + <div class="form__group-input three-col"> |
636 | + <select name="min_hwe_kernel" id="min_hwe_kernel" |
637 | + data-ng-model="machine.min_hwe_kernel" |
638 | + data-ng-options="hwe_kernel[0] as hwe_kernel[1] for hwe_kernel in hwe_kernels"> |
639 | + <option value="">No minimum kernel</option> |
640 | + </select> |
641 | + </div> |
642 | + </div> |
643 | + <div class="form__group"> |
644 | + <label for="zone4" class="form__group-label two-col">Zone</label> |
645 | + <select name="zone" id="zone4" class="three-col" |
646 | + data-ng-model="machine.zone" |
647 | + data-ng-options="zone as zone.name for zone in zones"> |
648 | + </select> |
649 | + </div> |
650 | + <div class="form__group" data-ng-repeat="mac in machine.macs"> |
651 | + <label for="mac-address" class="form__group-label two-col"><span data-ng-hide="mac !== machine.macs[0]">MAC Address</span> </label> |
652 | + <div class="form__group-input three-col"> |
653 | + <input type="text" id="mac-address" placeholder="00:00:00:00:00:00" |
654 | + maxlength="17" |
655 | + data-ng-class="{ 'has-error': mac.error }" |
656 | + data-ng-model="mac.mac" |
657 | + data-ng-pattern="macAddressRegex" |
658 | + data-ng-change="validateMac(mac)" |
659 | + mac-address> |
660 | + <span class="form__group-remove" title="Delete MAC" |
661 | + data-ng-hide="mac === machine.macs[0]" data-ng-click="removeMac(mac)"></span> |
662 | + </div> |
663 | + </div> |
664 | + <div class="form__group"> |
665 | + <div class="five-col"> |
666 | + <button class="button--secondary button--inline u-float--right" data-ng-click="addMac()">+ Add MAC Address</a> |
667 | + </div> |
668 | + </div> |
669 | + </fieldset> |
670 | + <fieldset class="form__fieldset six-col last-col" |
671 | + data-maas-power-parameters="power_types" |
672 | + data-ng-model="machine.power"> |
673 | + </fieldset> |
674 | + </form> |
675 | + </section> |
676 | + <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="showChassis()"> |
677 | + <h3 class="page-header__dropdown-title">Add chassis</h3> |
678 | + <form action="post" class="form form--stack"> |
679 | + <fieldset class="form__fieldset six-col" |
680 | + data-maas-power-parameters="chassisPowerTypes" |
681 | + data-ng-model="chassis.power"> |
682 | + </fieldset> |
683 | + <fieldset class="form__fieldset six-col last-col"> |
684 | + <div class="form__group"> |
685 | + <label for="domain2" class="form__group-label two-col">Domain</label> |
686 | + <div class="form__group-input three-col"> |
687 | + <select name="domain" id="domain2" |
688 | + data-ng-model="chassis.domain" |
689 | + data-ng-options="domain as domain.name for domain in domains"> |
690 | + </select> |
691 | + </div> |
692 | + </div> |
693 | + </fieldset> |
694 | + </form> |
695 | + </section> |
696 | + |
697 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
698 | + <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-show="showMachine()"> |
699 | + <p class="page-header__message page-header__message--error ng-hide" data-ng-show="error">{$ error $}</p> |
700 | + <div class="page-header__controls"> |
701 | + <button class="button--base button--inline" data-ng-click="cancel()">Cancel</button> |
702 | + <button class="button--secondary button--inline" |
703 | + data-ng-disabled="machineHasError()" |
704 | + data-ng-click="saveMachine(true)">Save and add another</button> |
705 | + <button class="button--positive button--inline" |
706 | + data-ng-disabled="machineHasError()" |
707 | + data-ng-click="saveMachine(false)">Save machine</button> |
708 | + </div> |
709 | + </section> |
710 | + |
711 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
712 | + <section class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="showChassis()"> |
713 | + <p class="page-header__message page-header__message--error ng-hide" data-ng-show="error">{$ error $}</p> |
714 | + <div class="page-header__controls"> |
715 | + <button class="button--base button--inline" data-ng-click="cancel()">Cancel</button> |
716 | + <button class="button--secondary button--inline" |
717 | + data-ng-disabled="chassisHasErrors()" |
718 | + data-ng-click="saveChassis(true)">Save and add another</button> |
719 | + <button class="button--positive button--inline" |
720 | + data-ng-disabled="chassisHasErrors()" |
721 | + data-ng-click="saveChassis(false)">Save chassis</button> |
722 | + </div> |
723 | + </section> |
724 | + |
725 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
726 | + <section class="page-header__section twelve-col u-margin--bottom-none" data-ng-hide="architectures.length"> |
727 | + <p class="page-header__message page-header__message--error"> |
728 | + Cannot add {$ mode $} until boot images have been imported. To fix, visit the <a href="#/images">images page</a>. |
729 | + </p> |
730 | + </section> |
731 | + </div> |
732 | + |
733 | + <!-- XXX blake_r 2015-04-02 - Need to add e2e test. --> |
734 | + <div class="page-header__dropdown" data-ng-class="{ 'is-open': tabs.devices.actionOption }"> |
735 | + <!-- XXX blake_r 2015-04-02 - Need to add e2e test. --> |
736 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError('devices') || hasActionsInProgress('devices')"> |
737 | + <!-- XXX blake_r 2015-04-02 - Need to add e2e test. --> |
738 | + <form class="form form--inline u-display--inline"> |
739 | + <fieldset class="form__fieldset ng-hide" data-ng-show="tabs.devices.actionOption.name === 'set-zone'"> |
740 | + <div class="form__group"> |
741 | + <label class="form__group-label" for="zone1">Set zone</label> |
742 | + <select name="zone" id="zone1" |
743 | + data-ng-model="tabs.devices.zoneSelection" |
744 | + data-ng-options="zone as zone.name for zone in zones"> |
745 | + <option value="" disabled="disabled">Choose a zone</option> |
746 | + </select> |
747 | + </div> |
748 | + </fieldset> |
749 | + <div class="page-header__controls"> |
750 | + <button class="button--base button--inline" data-ng-click="actionCancel('devices')">Cancel</button> |
751 | + <button class="button--positive button--inline" data-ng-click="actionGo('devices')"> |
752 | + <span data-ng-if="tabs.devices.actionOption.name === 'set-zone'">Set zone for {$ tabs.devices.selectedItems.length $} |
753 | + <span data-ng-pluralize count="tabs.devices.selectedItems.length" when="{'one': 'device', 'other': 'devices'}"></span> |
754 | + </span> |
755 | + <span data-ng-if="tabs.devices.actionOption.name === 'delete'">Delete {$ tabs.devices.selectedItems.length $} |
756 | + <span data-ng-pluralize count="tabs.devices.selectedItems.length" when="{'one': 'device', 'other': 'devices'}"></span> |
757 | + </span> |
758 | + </button> |
759 | + </div> |
760 | + </form> |
761 | + </div> |
762 | + |
763 | + <!-- XXX blake_r 2015-04-02 - Need to add e2e test. --> |
764 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isActionError('devices')"> |
765 | + <p class="page-header__message page-header__message--error"> |
766 | + {$ tabs.devices.actionErrorCount $} |
767 | + <span data-ng-pluralize count="tabs.devices.selectedItems.length" when="{'one': 'device', 'other': 'devices'}"></span> |
768 | + cannot be {$ tabs.devices.actionOption.sentence $}. To proceed, update your selection. |
769 | + </p> |
770 | + </div> |
771 | + |
772 | + <!-- XXX blake_r 2015-05-07 - Need to add e2e test. --> |
773 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="hasActionsInProgress('devices') || hasActionsFailed('devices')"> |
774 | + <p class="page-header__message" data-ng-show="hasActionsInProgress('devices')"> |
775 | + <i class="icon icon--loading u-animation--spin"></i> |
776 | + {$ tabs.devices.actionProgress.completed $} of {$ tabs.devices.actionProgress.total $} |
777 | + devices have been {$ tabs.devices.actionOption.sentence $}. |
778 | + </p> |
779 | + <p class="page-header__message page-header__message--error" |
780 | + data-ng-repeat="(error, devices) in tabs.devices.actionProgress.errors"> |
781 | + The {$ tabs.devices.actionOption.title.toLowerCase() $} action for {$ devices.length $} |
782 | + <span data-ng-pluralize count="devices.length" when="{'one': 'device', 'other': 'devices'}"></span> |
783 | + failed with error: {$ error $} |
784 | + </p> |
785 | + </div> |
786 | + </div> |
787 | + |
788 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
789 | + <div class="page-header__dropdown" data-ng-class="{ 'is-open': addDeviceScope.viewable }" data-ng-controller="AddDeviceController"> |
790 | + <section class="page-header__section twelve-col u-margin--bottom-none"> |
791 | + <h3 class="page-header__dropdown-title">Add device</h3> |
792 | + <form class="form form--stack"> |
793 | + <fieldset class="form__fieldset six-col"> |
794 | + <div class="form__group"> |
795 | + <label for="device-name" class="form__group-label two-col">Name</label> |
796 | + <div class="form__group-input three-col"> |
797 | + <input type="text" id="device-name" placeholder="Name your device" |
798 | + data-ng-model="device.name" |
799 | + data-ng-class="{ 'has-error': nameHasError() }"> |
800 | + </div> |
801 | + |
802 | + </div> |
803 | + <div class="form__group"> |
804 | + <label for="domain3" class="form__group-label two-col">Domain</label> |
805 | + <div class="form__group-input three-col"> |
806 | + <select name="domain" id="domain3" |
807 | + data-ng-model="device.domain" |
808 | + data-ng-options="domain as domain.name for domain in domains"> |
809 | + </select> |
810 | + </div> |
811 | + </div> |
812 | + </fieldset> |
813 | + </form> |
814 | + <table> |
815 | + <thead> |
816 | + <tr> |
817 | + <th class="table-col--20">MAC address</th> |
818 | + <th class="table-col--20">IP assignment</th> |
819 | + <th class="table-col--20">Subnet</th> |
820 | + <th class="table-col--35">IP address</th> |
821 | + <th class="table-col--5"></th> |
822 | + </tr> |
823 | + </thead> |
824 | + <tbody vs-repeat vs-scroll-parent="window"> |
825 | + <tr data-ng-repeat="interface in device.interfaces"> |
826 | + <td class="table-col--20" aria-label="MAC address"> |
827 | + <input type="text" id="mac-address1" placeholder="00:00:00:00:00:00" |
828 | + data-ng-model="interface.mac" |
829 | + data-ng-class="{ 'has-error': macHasError(interface) }"> |
830 | + </td> |
831 | + <td class="table-col--20" aria-label="IP assignment"> |
832 | + <select name="ip-assignment" id="ip-assignment" |
833 | + data-ng-model="interface.ipAssignment" |
834 | + data-ng-options="assigment.title for assigment in ipAssignments"> |
835 | + <option value="" disabled selected>Select IP assignment</option> |
836 | + </select> |
837 | + </td> |
838 | + <td class="table-col--20" aria-label="Subnet"> |
839 | + <select name="subnet" id="subnet" |
840 | + data-ng-model="interface.subnetId" |
841 | + data-ng-options="subnet.id as subnet.name for subnet in subnets" |
842 | + data-ng-show="interface.ipAssignment.name === 'static'"> |
843 | + <option value="" disabled selected>Select subnet</option> |
844 | + </select> |
845 | + </td> |
846 | + <td class="table-col--35" aria-label="IP address"> |
847 | + <input type="text" id="ip-address" placeholder="000.000.000.000" |
848 | + data-ng-model="interface.ipAddress" |
849 | + data-ng-class="{ 'has-error': ipHasError(interface) }" |
850 | + data-ng-show="interface.ipAssignment.name === 'external'"> |
851 | + <input type="text" id="ip-address" |
852 | + data-ng-model="interface.ipAddress" |
853 | + data-ng-class="{ 'has-error': ipHasError(interface) }" |
854 | + data-ng-show="interface.ipAssignment.name === 'static'"> |
855 | + </td> |
856 | + <td class="table-col--5 u-u-align---right table--mobile-controls"> |
857 | + <!-- space needed to set correct height --> |
858 | + <span data-ng-show="isPrimaryInterface(interface)"> </span> |
859 | + <a title="Delete" class="icon icon--remove u-display--desktop" |
860 | + data-ng-click="deleteInterface(interface)" |
861 | + data-ng-hide="isPrimaryInterface(interface)">delete</a> |
862 | + <button class="button--secondary u-display--mobile" |
863 | + data-ng-click="deleteInterface(interface)" |
864 | + data-ng-hide="isPrimaryInterface(interface)">delete</a> |
865 | + </td> |
866 | + </tr> |
867 | + </tbody> |
868 | + </table> |
869 | + <button class="button--secondary button--inline" data-ng-click="addInterface()">+ Add another interface</button> |
870 | + </section> |
871 | + <section class="page-header__section twelve-col u-margin--bottom-none twelve-col u-margin--bottom-none"> |
872 | + <form class="form form--inline"> |
873 | + <p class="page-header__message page-header__message--error ng-hide" data-ng-show="error">{$ error $}</p> |
874 | + <div class="page-header__controls"> |
875 | + <button class="button--base button--inline" data-ng-click="cancel()">Cancel</button> |
876 | + <button class="button--secondary button--inline" |
877 | + data-ng-class="{ disabled: deviceHasError() }" |
878 | + data-ng-click="save(true)">Save and add another</button> |
879 | + <button class="button--positive button--inline" |
880 | + data-ng-class="{ disabled: deviceHasError() }" |
881 | + data-ng-click="save(false)">Save device</button> |
882 | + </div> |
883 | + </form> |
884 | + </section> |
885 | + </div> |
886 | + |
887 | + <!-- XXX blake_r 2015-04-02 - Need to add e2e test. --> |
888 | + <div class="page-header__dropdown" data-ng-class="{ 'is-open': tabs.controllers.actionOption }"> |
889 | + <!-- XXX blake_r 2015-04-02 - Need to add e2e test. --> |
890 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-hide="isActionError('controllers') || hasActionsInProgress('controllers')"> |
891 | + <!-- XXX blake_r 2015-04-02 - Need to add e2e test. --> |
892 | + <form class="form form--inline u-display--inline"> |
893 | + <fieldset class="form__fieldset ng-hide" data-ng-show="tabs.controllers.actionOption.name === 'set-zone'"> |
894 | + <div class="form__group"> |
895 | + <label for="zone2" class="form__group-label">Select Zone</label> |
896 | + <div class="form__group-input"> |
897 | + <select name="zone" id="zone2" |
898 | + data-ng-model="tabs.controllers.zoneSelection" |
899 | + data-ng-options="zone as zone.name for zone in zones"> |
900 | + <option value="" disabled="disabled">Choose a zone</option> |
901 | + </select> |
902 | + </div> |
903 | + </div> |
904 | + </fieldset> |
905 | + <fieldset class="form__fieldset ng-hide" data-ng-show="tabs.controllers.actionOption.name === 'test'"> |
906 | + <div class="form__group"> |
907 | + <input class="form__group-label" id="enable_SSH" type="checkbox" |
908 | + data-ng-model="tabs.controllers.commissionOptions.enableSSH"> |
909 | + <label class="checkbox-label" for="enable_SSH">Allow SSH access and prevent machine from powering off</label> |
910 | + </div> |
911 | + </fieldset> |
912 | + </form> |
913 | + <div class="page-header__controls" data-ng-if="tabs.controllers.actionOption.name !== 'test'"> |
914 | + <button class="button--base button--inline" data-ng-click="actionCancel('controllers')">Cancel</button> |
915 | + <button class="button--positive button--inline" data-ng-click="actionGo('controllers')"> |
916 | + <span data-ng-if="tabs.controllers.actionOption.name === 'set-zone'">Set zone for {$ tabs.controllers.selectedItems.length $} |
917 | + <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span> |
918 | + </span> |
919 | + <span data-ng-if="tabs.controllers.actionOption.name === 'on'">Power on {$ tabs.controllers.selectedItems.length $} |
920 | + <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span> |
921 | + </span> |
922 | + <span data-ng-if="tabs.controllers.actionOption.name === 'off'">Power off {$ tabs.controllers.selectedItems.length $} |
923 | + <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span> |
924 | + </span> |
925 | + <span data-ng-if="tabs.controllers.actionOption.name === 'delete'">Delete {$ tabs.controllers.selectedItems.length $} |
926 | + <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span> |
927 | + </span> |
928 | + <span data-ng-if="tabs.controllers.actionOption.name === 'import-images'">Import images for {$ tabs.controllers.selectedItems.length $} |
929 | + <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span> |
930 | + </span> |
931 | + </button> |
932 | + </div> |
933 | + </div> |
934 | + <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'"> |
935 | + <form class="form form--stack"> |
936 | + <fieldset class="form__fieldset eight-col u-margin--bottom-small"> |
937 | + <div class="form__group"> |
938 | + <label for="testing-scripts">Hardware Tests</label> |
939 | + <span id="testing-scripts" data-maas-script-select="script" data-script-type="2" data-ng-model="tabs.nodes.testSelection" class="tags--inline"></span> |
940 | + </div> |
941 | + </fieldset> |
942 | + </form> |
943 | + </div> |
944 | + <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'"> |
945 | + <div class="page-header__controls"> |
946 | + <button class="button--base button--inline" data-ng-click="actionCancel('controllers')">Cancel</button> |
947 | + <button class="button--positive button--inline" data-ng-click="actionGo('controllers')"> |
948 | + <span>Test {$ tabs.controllers.selectedItems.length $} |
949 | + <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span> |
950 | + </span> |
951 | + </button> |
952 | + </div> |
953 | + </div> |
954 | + |
955 | + <!-- XXX blake_r 2015-04-02 - Need to add e2e test. --> |
956 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="isActionError('controllers')"> |
957 | + <form class="form form--inline"> |
958 | + <p class="page-header__message page-header__message--error"> |
959 | + {$ tabs.controllers.actionErrorCount $} |
960 | + <span data-ng-pluralize count="tabs.controllers.selectedItems.length" when="{'one': 'controller', 'other': 'controllers'}"></span> |
961 | + cannot be {$ tabs.controllers.actionOption.sentence $}. To proceed, update your selection. |
962 | + </p> |
963 | + </form> |
964 | + </div> |
965 | + |
966 | + <!-- XXX blake_r 2015-05-07 - Need to add e2e test. --> |
967 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="hasActionsInProgress('controllers') || hasActionsFailed('controllers')"> |
968 | + <form class="form form--inline"> |
969 | + <p class="page-header__message" data-ng-show="hasActionsInProgress('controllers')"> |
970 | + <i class="icon icon--loading u-animation--spin u-margin--right-small"></i> |
971 | + {$ tabs.controllers.actionProgress.completed $} of {$ tabs.controllers.actionProgress.total $} |
972 | + controllers have been {$ tabs.controllers.actionOption.sentence $}. |
973 | + </p> |
974 | + <p class="page-header__message page-header__message--error" |
975 | + data-ng-repeat="(error, controllers) in tabs.controllers.actionProgress.errors"> |
976 | + The {$ tabs.controllers.actionOption.title.toLowerCase() $} action for {$ controllers.length $} |
977 | + <span data-ng-pluralize count="controllers.length" when="{'one': 'controller', 'other': 'controllers'}"></span> |
978 | + failed with error: {$ error $} |
979 | + </p> |
980 | + </form> |
981 | + </div> |
982 | + </div> |
983 | + |
984 | + <div class="page-header__dropdown" data-ng-class="{ 'is-open': tabs.controllers.addController }"> |
985 | + <!-- XXX blake_r 2015-05-07 - Need to add e2e test. --> |
986 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="tabs.controllers.addController"> |
987 | + <h3 class="page-header__dropdown-title">Add rack controller</h3> |
988 | + <pre class="u-margin--none"> |
989 | + <code># To add a new rack controller, SSH into the rack controller. |
990 | +# Install the maas-rack-controller package. |
991 | +sudo apt install maas-rack-controller |
992 | +# Register the rack controller with this MAAS. If the rack controller (and machines) |
993 | +# don't have access to the URL, use a different IP address to allow connection. |
994 | +sudo maas-rack register --url {$ tabs.controllers.registerUrl $} --secret {$ tabs.controllers.registerSecret $}</code> |
995 | + </pre> |
996 | + </div> |
997 | + <div class="page-header__section twelve-col u-margin--bottom-none ng-hide" data-ng-show="tabs.controllers.addController"> |
998 | + <form class="form form--inline"> |
999 | + <div class="page-header__controls"> |
1000 | + <button class="button--secondary button--inline" |
1001 | + data-ng-click="tabs.controllers.addController = false">Close</button> |
1002 | + </div> |
1003 | + </form> |
1004 | + </div> |
1005 | + </div> |
1006 | + </div> |
1007 | +</header> |
1008 | +<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 }"> |
1009 | + <div class="wrapper--inner"> |
1010 | + <nav class="page-navigation__links"> |
1011 | + <button class="page-navigation__link tooltip tooltip--right" |
1012 | + aria-label="A deployable node managed by MAAS." |
1013 | + data-ng-class="{ 'is-active': currentpage === 'nodes'}" |
1014 | + data-ng-click="toggleTab('nodes')">{$ nodes.length $} <ng-pluralize count="nodes.length" when="{'one': 'Machine', 'other': 'Machines'}"></ng-pluralize></button> |
1015 | + <button class="page-navigation__link tooltip" |
1016 | + aria-label="A node known to MAAS, but is not deployable." |
1017 | + data-ng-class="{ 'is-active': currentpage === 'devices'}" |
1018 | + data-ng-click="toggleTab('devices')">{$ devices.length $} <ng-pluralize count="devices.length" when="{'one': 'Device', 'other': 'Devices'}"></ng-pluralize></button> |
1019 | + <button class="page-navigation__link tooltip" |
1020 | + data-ng-if="isSuperUser()" |
1021 | + aria-label="A node that provides MAAS services." |
1022 | + data-ng-class="{ 'is-active': currentpage === 'controllers'}" |
1023 | + data-ng-click="toggleTab('controllers')">{$ controllers.length $} <ng-pluralize count="controllers.length" when="{'one': 'Controller', 'other': 'Controllers'}"></ng-pluralize></button> |
1024 | + <button class="page-navigation__link tooltip" |
1025 | + data-ng-if="showswitches" |
1026 | + aria-label="A node that is a network switch." |
1027 | + data-ng-class="{ 'is-active': currentpage === 'switches'}" |
1028 | + data-ng-click="toggleTab('switches')">{$ switches.length $} <ng-pluralize count="switches.length" when="{'one': 'Switch', 'other': 'Switches'}"></ng-pluralize></button> |
1029 | + </nav> |
1030 | + </div> |
1031 | +</div> |
1032 | +<div class="u-padding--top row"> |
1033 | + <div class="wrapper--inner"> |
1034 | + <maas-notifications></maas-notifications> |
1035 | + <aside class="three-col"> |
1036 | + <div class="accordion maas-accordion ng-hide" data-ng-show="currentpage === 'nodes'" |
1037 | + data-ng-class="{ 'is-disabled': tabs.nodes.actionOption }"> |
1038 | + <h3 class="accordion__title" |
1039 | + data-ng-class="{'is-active': isActive}" |
1040 | + data-ng-init="isActive = false" |
1041 | + data-ng-click="isActive = !isActive">Filter by</h3> |
1042 | + <div class="accordion__content"> |
1043 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
1044 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.status.length"> |
1045 | + <button class="accordion__tab-title maas-accordion-tab is-active">Status</button> |
1046 | + <div class="accordion__tab-content"> |
1047 | + <ul class="accordion__tab-list"> |
1048 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
1049 | + <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') }"> |
1050 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('status', status.name, 'nodes')">{$ status.name $} ({$ status.count $})</button> |
1051 | + </li> |
1052 | + </ul> |
1053 | + </div> |
1054 | + </div> |
1055 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.owner.length"> |
1056 | + <button class="accordion__tab-title maas-accordion-tab">Owner</button> |
1057 | + <div class="accordion__tab-content"> |
1058 | + <ul class="accordion__tab-list"> |
1059 | + <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') }"> |
1060 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('owner', owner.name, 'nodes')">{$ owner.name $} ({$ owner.count $})</button> |
1061 | + </li> |
1062 | + </ul> |
1063 | + </div> |
1064 | + </div> |
1065 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.architecture.length"> |
1066 | + <button class="accordion__tab-title maas-accordion-tab">Architectures</button> |
1067 | + <div class="accordion__tab-content"> |
1068 | + <ul class="accordion__tab-list"> |
1069 | + <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') }"> |
1070 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('architecture', architecture.name, 'nodes')"><span data-maas-release-name="architecture.name"></span> ({$ architecture.count $})</button> |
1071 | + </li> |
1072 | + </ul> |
1073 | + </div> |
1074 | + </div> |
1075 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.release.length"> |
1076 | + <button class="accordion__tab-title maas-accordion-tab">OS/Release</button> |
1077 | + <div class="accordion__tab-content"> |
1078 | + <ul class="accordion__tab-list"> |
1079 | + <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') }"> |
1080 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('release', release.name, 'nodes')"><span data-maas-release-name="release.name"></span> ({$ release.count $})</button> |
1081 | + </li> |
1082 | + </ul> |
1083 | + </div> |
1084 | + </div> |
1085 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.tags.length"> |
1086 | + <button class="accordion__tab-title maas-accordion-tab">Tags</button> |
1087 | + <div class="accordion__tab-content"> |
1088 | + <ul class="accordion__tab-list"> |
1089 | + <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') }"> |
1090 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('tags', tag.name, 'nodes')">{$ tag.name $} ({$ tag.count $})</button> |
1091 | + </li> |
1092 | + </ul> |
1093 | + </div> |
1094 | + </div> |
1095 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.storage_tags.length"> |
1096 | + <button class="accordion__tab-title maas-accordion-tab">Storage Tags</button> |
1097 | + <div class="accordion__tab-content"> |
1098 | + <ul class="accordion__tab-list"> |
1099 | + <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') }"> |
1100 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('storage_tags', tag.name, 'nodes')">{$ tag.name $} ({$ tag.count $})</button> |
1101 | + </li> |
1102 | + </ul> |
1103 | + </div> |
1104 | + </div> |
1105 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.subnets.length"> |
1106 | + <button class="accordion__tab-title maas-accordion-tab">Subnets</button> |
1107 | + <div class="accordion__tab-content"> |
1108 | + <ul class="accordion__tab-list"> |
1109 | + <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') }"> |
1110 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('subnets', subnet.name, 'nodes')">{$ subnet.name $} ({$ subnet.count $})</button> |
1111 | + </li> |
1112 | + </ul> |
1113 | + </div> |
1114 | + </div> |
1115 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.fabrics.length"> |
1116 | + <button class="accordion__tab-title maas-accordion-tab">Fabrics</button> |
1117 | + <div class="accordion__tab-content"> |
1118 | + <ul class="accordion__tab-list"> |
1119 | + <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') }"> |
1120 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('fabrics', fabric.name, 'nodes')">{$ fabric.name $} ({$ fabric.count $})</button> |
1121 | + </li> |
1122 | + </ul> |
1123 | + </div> |
1124 | + </div> |
1125 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.spaces.length"> |
1126 | + <button class="accordion__tab-title maas-accordion-tab">Spaces</button> |
1127 | + <div class="accordion__tab-content"> |
1128 | + <ul class="accordion__tab-list"> |
1129 | + <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') }"> |
1130 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('spaces', space.name, 'nodes')">{$ space.name $} ({$ space.count $})</button> |
1131 | + </li> |
1132 | + </ul> |
1133 | + </div> |
1134 | + </div> |
1135 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.nodes.metadata.zone.length"> |
1136 | + <button class="accordion__tab-title maas-accordion-tab">Zones</button> |
1137 | + <div class="accordion__tab-content"> |
1138 | + <ul class="accordion__tab-list"> |
1139 | + <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') }"> |
1140 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('zone', zone.name, 'nodes')">{$ zone.name $} ({$ zone.count $})</button> |
1141 | + </li> |
1142 | + </ul> |
1143 | + </div> |
1144 | + </div> |
1145 | + </div> |
1146 | + </div> |
1147 | + <div class="accordion three-col maas-accordion ng-hide" data-ng-show="currentpage === 'devices'" |
1148 | + data-ng-class="{ 'is-disabled': tabs.devices.actionOption }"> |
1149 | + <h3 class="accordion__title">Filter by</h3> |
1150 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. --> |
1151 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.devices.metadata.owner.length"> |
1152 | + <button class="accordion__tab-title maas-accordion-tab active">Owner</button> |
1153 | + <div class="accordion__tab-content"> |
1154 | + <ul class="accordion__tab-list"> |
1155 | + <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') }"> |
1156 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('owner', owner.name, 'devices')">{$ owner.name $} ({$ owner.count $})</button> |
1157 | + </li> |
1158 | + </ul> |
1159 | + </div> |
1160 | + </div> |
1161 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.devices.metadata.tags.length"> |
1162 | + <button class="accordion__tab-title maas-accordion-tab">Tags</button> |
1163 | + <div class="accordion__tab-content"> |
1164 | + <ul class="accordion__tab-list"> |
1165 | + <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') }"> |
1166 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('tags', tag.name, 'devices')">{$ tag.name $} ({$ tag.count $})</button> |
1167 | + </li> |
1168 | + </ul> |
1169 | + </div> |
1170 | + </div> |
1171 | + <div class="ng-hide accordion__tab" data-ng-show="tabs.devices.metadata.zone.length"> |
1172 | + <button class="accordion__tab-title maas-accordion-tab">Zones</button> |
1173 | + <div class="accordion__tab-content"> |
1174 | + <ul class="accordion__tab-list"> |
1175 | + <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') }"> |
1176 | + <button class="accordion__tab-link" data-ng-click="toggleFilter('zone', zone.name, 'devices')">{$ zone.name $} ({$ zone.count $})</button> |
1177 | + </li> |
1178 | + </ul> |
1179 | + </div> |
1180 | + </div> |
1181 | + </div> |
1182 | + </aside> |
1183 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
1184 | + <div class="nine-col last-col ng-hide" data-ng-show="currentpage === 'nodes'"> |
1185 | + <form> |
1186 | + <div id="search-bar" class="search nine-col"> |
1187 | + <!-- XXX ricgard 2016-06-16 - Need to add e2e test. --> |
1188 | + <input |
1189 | + type="search" placeholder="Search nodes" class="search__input" |
1190 | + data-ng-model="tabs.nodes.search" data-ng-change="updateFilters('nodes')" |
1191 | + data-ng-class="{ error: !tabs.nodes.searchValid }" |
1192 | + data-ng-disabled="tabs.nodes.actionOption" /> |
1193 | + <input type="reset" class="search__submit" |
1194 | + data-ng-class="{ 'search__submit--close': tabs.nodes.search.length > 0 }" |
1195 | + data-ng-click="clearSearch('nodes')" /> |
1196 | + </div> |
1197 | + <maas-machines-table id="nodes-listing" search="tabs.nodes.search" ng-disabled="hasActionsInProgress('nodes')" |
1198 | + machine-has-error="!supportsAction($machine, 'nodes')" on-listing-change="onNodeListingChanged($machines, 'nodes')" |
1199 | + on-check-all="toggleCheckAll('nodes')" on-check="toggleChecked($machine, 'nodes')"></maas-machines-table> |
1200 | + </form> |
1201 | + </div> |
1202 | + <div class="nine-col last-col" class="ng-hide" data-ng-show="currentpage === 'devices'"> |
1203 | + <div class="search nine-col"> |
1204 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. --> |
1205 | + <input |
1206 | + type="search" placeholder="Search devices" class="search__input" |
1207 | + data-ng-model="tabs.devices.search" data-ng-change="updateFilters('devices')" |
1208 | + data-ng-class="{ error: !tabs.devices.searchValid }" |
1209 | + data-ng-disabled="tabs.devices.actionOption" /> |
1210 | + <input type="reset" class="search__submit" |
1211 | + data-ng-class="{ 'search__submit--close': tabs.devices.search.length > 0 }" |
1212 | + data-ng-click="clearSearch('devices')" /> |
1213 | + </div> |
1214 | + <table> |
1215 | + <thead> |
1216 | + <tr> |
1217 | + <th class="table-col--3"> |
1218 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. --> |
1219 | + <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')" /> |
1220 | + <label for="check-all-devices" class="checkbox-label"></label> |
1221 | + </th> |
1222 | + <th class="table-col--24"> |
1223 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. --> |
1224 | + <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}"> |
1225 | + <span title="Fully Qualified Domain Name">FQDN</span> |
1226 | + </a> |
1227 | + </th> |
1228 | + <th class="table-col--25"> |
1229 | + <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}"> |
1230 | + <span title="Media Access Control addresses">MAC</span> |
1231 | + </a> |
1232 | + </th> |
1233 | + <th class="table-col--15"> |
1234 | + <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> |
1235 | + </th> |
1236 | + <th class="table-col--20"> |
1237 | + <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> |
1238 | + </th> |
1239 | + <th class="table-col--12"> |
1240 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. --> |
1241 | + <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> |
1242 | + </th> |
1243 | + </tr> |
1244 | + </thead> |
1245 | + <tbody vs-repeat vs-scroll-parent="window"> |
1246 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. This really needs lots of tests. --> |
1247 | + <tr |
1248 | + 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" |
1249 | + data-ng-class="{ 'table--error': !supportsAction(device, 'devices'), selected: device.$selected }"> |
1250 | + <td class="table-col--3" aria-label="Select device"> |
1251 | + <input type="checkbox" class="checkbox" data-ng-click="toggleChecked(device, 'devices')" data-ng-checked="device.$selected" id="{$ device.fqdn $}" data-ng-disabled="hasActionsInProgress('devices')"/> |
1252 | + <label for="{$ device.fqdn $}" class="checkbox-label"></label> |
1253 | + </td> |
1254 | + <td class="table-col--24" aria-label="FQDN"> |
1255 | + <a href="#/node/device/{$ device.system_id $}">{$ device.fqdn $}</a> |
1256 | + </td> |
1257 | + <td class="table-col--25" aria-label="MAC"> |
1258 | + {$ device.primary_mac $} |
1259 | + <span class="extra-macs" data-ng-show="device.extra_macs.length">(+{$ device.extra_macs.length $})</span> |
1260 | + </td> |
1261 | + <td class="table-col--15" aria-label="IP Assignment">{$ getDeviceIPAssignment(device.ip_assignment) $}</td> |
1262 | + <td class="table-col--20" aria-label="IP Address">{$ device.ip_address $}</td> |
1263 | + <td class="table-col--12" aria-label="Owner">{$ device.owner $}</td> |
1264 | + </tr> |
1265 | + </tbody> |
1266 | + </table> |
1267 | + </div> |
1268 | + <div class="twelve-col last-col" class="ng-hide" data-ng-show="currentpage === 'controllers'"> |
1269 | + <div class="search twelve-col"> |
1270 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. --> |
1271 | + <input |
1272 | + type="search" placeholder="Search controllers" class="search__input" |
1273 | + data-ng-model="tabs.controllers.search" data-ng-change="updateFilters('controllers')" |
1274 | + data-ng-class="{ error: !tabs.controllers.searchValid }" |
1275 | + data-ng-disabled="tabs.controllers.actionOption" /> |
1276 | + <input type="submit" class="search__submit" |
1277 | + data-ng-class="{ 'search__submit--close': tabs.controllers.search.length > 0 }" |
1278 | + data-ng-click="clearSearch('controllers')" /> |
1279 | + </div> |
1280 | + <table> |
1281 | + <thead> |
1282 | + <tr> |
1283 | + <th class="table-col--2"> |
1284 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. --> |
1285 | + <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')" /> |
1286 | + <label for="check-all-controllers" class="checkbox-label"></label> |
1287 | + </th> |
1288 | + <th class="table-col--19"> |
1289 | + <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}"> |
1290 | + <span title="Fully Qualified Domain Name">Name</span> |
1291 | + </a> |
1292 | + </th> |
1293 | + <th class="table-col--4 u-align--center"> |
1294 | + <span title="Status">Status</span> |
1295 | + </th> |
1296 | + <th class="table-col--15"> |
1297 | + <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}"> |
1298 | + <span title="Controller Type">Type</span> |
1299 | + </a> |
1300 | + </th> |
1301 | + <th class="table-col--10"> |
1302 | + <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}"> |
1303 | + <span title="Version">Version</span> |
1304 | + </a> |
1305 | + </th> |
1306 | + <th class="table-col--15"> |
1307 | + <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> |
1308 | + </th> |
1309 | + <th class="table-col--15"> |
1310 | + <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> |
1311 | + </th> |
1312 | + </tr> |
1313 | + </thead> |
1314 | + <tbody vs-repeat vs-scroll-parent="window"> |
1315 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. This really needs lots of tests. --> |
1316 | + <tr |
1317 | + 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" |
1318 | + data-ng-class="{ 'table--error': !supportsAction(controller, 'controllers'), selected: controller.$selected }"> |
1319 | + <td class="table-col--2" aria-label="Select controller"> |
1320 | + <input type="checkbox" class="checkbox" data-ng-click="toggleChecked(controller, 'controllers')" data-ng-checked="controller.$selected" id="{$ controller.fqdn $}" data-ng-disabled="hasActionsInProgress('controllers')"/> |
1321 | + <label for="{$ controller.fqdn $}" class="checkbox-label"></label> |
1322 | + </td> |
1323 | + <td class="table-col--19" aria-label="FQDN"> |
1324 | + <a href="#/node/controller/{$ controller.system_id $}">{$ controller.fqdn $}</a> |
1325 | + </td> |
1326 | + <td class="table-col--4 u-align--center" data-maas-controller-status="controller" data-maas-services="services" aria-label="Status"></td> |
1327 | + <td class="table-col--15" aria-label="Type">{$ controller.node_type_display $}</td> |
1328 | + <td class="table-col--10" aria-label="Version"> |
1329 | + <div data-ng-show="controller.version"> |
1330 | + {$ controller.version__short $} |
1331 | + </div> |
1332 | + <div data-ng-show="!controller.version" class="u-text--subtle"> |
1333 | + Unknown |
1334 | + <i class="icon icon--info u-margin--left-tiny tooltip" |
1335 | + aria-label="Less than 2.3.0"></i> |
1336 | + </div> |
1337 | + </td> |
1338 | + <td class="table-col--15" aria-label="Last image sync">{$ controller.last_image_sync || 'Never' $}</td> |
1339 | + <td class="table-col--15" aria-label="Image status"> |
1340 | + <maas-controller-image-status system-id="controller.system_id"></maas-controller-image-status> |
1341 | + </td> |
1342 | + </tr> |
1343 | + </tbody> |
1344 | + </table> |
1345 | + </div> |
1346 | + <div class="twelve-col last-col" class="ng-hide" data-ng-show="currentpage === 'switches'"> |
1347 | + <div class="search twelve-col"> |
1348 | + <!-- XXX rvba 2015-02-25 - Need to add e2e test. --> |
1349 | + <input |
1350 | + type="search" placeholder="Search switches" class="search__input" |
1351 | + data-ng-model="tabs.switches.search" data-ng-change="updateFilters('switches')" |
1352 | + data-ng-class="{ error: !tabs.switches.searchValid }" |
1353 | + data-ng-disabled="tabs.switches.actionOption" /> |
1354 | + <input type="submit" class="search__submit" |
1355 | + data-ng-class="{ 'search__submit--close': tabs.switches.search.length > 0 }" |
1356 | + data-ng-click="clearSearch('switches')" /> |
1357 | + </div> |
1358 | + <maas-switches-table id="switches-listing" search="tabs.switches.search" ng-disabled="hasActionsInProgress('switches')" |
1359 | + switch-has-error="!supportsAction($switch_, 'switches')" on-listing-change="onNodeListingChanged($switches, 'switches')" |
1360 | + on-check-all="toggleCheckAll('switches')" on-check="toggleChecked($switch_, 'switches')"></maas-switches-table> |
1361 | + </div> |
1362 | + </div> |
1363 | +</div> |
1364 | +>>>>>>> src/maasserver/static/partials/nodes-list.html |
1365 | diff --git a/src/maasserver/static/partials/script-results-list.html b/src/maasserver/static/partials/script-results-list.html |
1366 | index 12d1175..bcf7b40 100644 |
1367 | --- a/src/maasserver/static/partials/script-results-list.html |
1368 | +++ b/src/maasserver/static/partials/script-results-list.html |
1369 | @@ -4,6 +4,7 @@ |
1370 | <p class="u-text--loading"><i class="p-icon--spinner u-animation--spin"></i> Loading...</p> |
1371 | </div> |
1372 | </div> |
1373 | +<<<<<<< src/maasserver/static/partials/script-results-list.html |
1374 | <div class="row"> |
1375 | <div data-ng-repeat="hardware_type in results"> |
1376 | <div data-ng-if="resultsLoaded && (hardware_type.results | json) != '{}'"> |
1377 | @@ -49,12 +50,59 @@ |
1378 | <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> |
1379 | </div> |
1380 | </div> |
1381 | +======= |
1382 | + <div data-ng-repeat="hardware_type in results"> |
1383 | + <div data-ng-if="resultsLoaded && (hardware_type.results | json) != '{}'"> |
1384 | + <h2 data-ng-if="hardware_type.title !== 'null'">{$ hardware_type.title $}</h2> |
1385 | + <div data-ng-repeat="(title, results) in hardware_type.results"> |
1386 | + <h3 data-ng-if="title !== 'null'">{$ title $}</h3> |
1387 | + <section class="table u-margin--bottom"> |
1388 | + <header class="table__head"> |
1389 | + <div class="table__row"> |
1390 | + <div class="table__header table-col--2 u-padding--left-none"></div> |
1391 | + <div class="table__header table-col--24">Name</div> |
1392 | + <div class="table__header table-col--22">Tags</div> |
1393 | + <div class="table__header table-col--15">Runtime</div> |
1394 | + <div class="table__header table-col--20">Date</div> |
1395 | + <div class="table__header table-col--12">Result</div> |
1396 | + <div class="table__header table-col--5 u-align--right">Actions</div> |
1397 | + </div> |
1398 | + </header> |
1399 | + <main class="table__body"> |
1400 | + <div data-ng-repeat="result in results"> |
1401 | + <div class="table__row" data-ng-class="{'is-active': result.showing_results || result.showing_history}"> |
1402 | + <div class="table__data table-col--2 u-padding--left-none" aria-label="Status"> |
1403 | + <span data-maas-script-status="script-status" data-script-status="result.status"></span> |
1404 | + </div> |
1405 | + <div class="table__data table-col--24" data-ng-click="result.showing_results = !result.showing_results" aria-label="Name"> |
1406 | + {$ result.name $} |
1407 | + <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> |
1408 | + </div> |
1409 | + <div class="table__data table-col--22" aria-label="Tags"><span data-ng-hide="result.showing_history">{$ result.tags $}</span></div> |
1410 | + <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> |
1411 | + <div class="table__data table-col--20" aria-label="Date"><span data-ng-hide="result.showing_history">{$ result.updated $}</span></div> |
1412 | + <div class="table__data table-col--12" aria-label="Status"> |
1413 | + <span data-ng-hide="result.showing_history"> |
1414 | + <!-- 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)--> |
1415 | + {$ 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> |
1416 | + </span> |
1417 | + </div> |
1418 | + <div class="table__data table-col--5 table--mobile-controls"> |
1419 | + <div class="table__controls u-align--right" toggle-ctrl> |
1420 | + <button class="table__controls-toggle" data-ng-click="toggleMenu()">View actions</button> |
1421 | + <div class="table__controls-menu ng-hide" role="menu" data-ng-show="isToggled"> |
1422 | + <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> |
1423 | + <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> |
1424 | + <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> |
1425 | + <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> |
1426 | +>>>>>>> src/maasserver/static/partials/script-results-list.html |
1427 | </div> |
1428 | </td> |
1429 | <td class="p-table-expanding__panel col-12" aria-label="results" data-ng-if="result.showing_results && !result.showing_history"> |
1430 | <div class="row"> |
1431 | <div class="col-12" data-ng-if="result.results.length === 0">No metrics provided</div> |
1432 | </div> |
1433 | +<<<<<<< src/maasserver/static/partials/script-results-list.html |
1434 | <div class="row" data-ng-if="result.results"> |
1435 | <dl data-ng-repeat="item in result.results"> |
1436 | <dt class="p-tooltip p-tooltip--top-center"> |
1437 | @@ -68,6 +116,34 @@ |
1438 | <td class="p-table-expanding__panel col-12" aria-label="loading history" data-ng-if="result.loading_history"> |
1439 | <div class="col-12"> |
1440 | <p class="u-text--loading"><i class="p-icon--spinner u-animation--spin"></i> Loading...</p> |
1441 | +======= |
1442 | + </div> |
1443 | + </div> |
1444 | + |
1445 | + <div class="table__dropdown" aria-label="history" data-ng-if="result.showing_history"> |
1446 | + <div class="table__row is-active"> |
1447 | + <div class="table__data table-col--100"> |
1448 | + <section class="table u-margin--bottom"> |
1449 | + <main class="table__body"> |
1450 | + <div class="table__row is-active u-border--none" data-ng-repeat="item in result.history_list"> |
1451 | + <div class="table__data table-col--2 u-padding--left-none" aria-label="Status"> |
1452 | + <span data-maas-script-status="script-status" data-script-status="item.status"></span> |
1453 | + </div> |
1454 | + <div class="table__data table-col--24" aria-label="Name">{$ result.name $}</div> |
1455 | + <div class="table__data table-col--24" aria-label="Tags">{$ result.tags $}</div> |
1456 | + <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> |
1457 | + <div class="table__data table-col--20" aria-label="Date">{$ item.updated $}</div> |
1458 | + <div class="table__data table-col--10" aria-label="Status"> |
1459 | + <!-- 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)--> |
1460 | + {$ 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> |
1461 | + </div> |
1462 | + </div> |
1463 | + </main> |
1464 | + </section> |
1465 | + <p class="u-align--center u-margin--bottom"> |
1466 | + <button class="button--secondary button--inline" data-ng-click="result.showing_history = false">Hide previous {$ result.result_section $}</button> |
1467 | + </p> |
1468 | +>>>>>>> src/maasserver/static/partials/script-results-list.html |
1469 | </div> |
1470 | </div> |
1471 | <td class="p-table-expanding__panel col-12" aria-label="history" data-ng-if="result.showing_history"> |
1472 | diff --git a/src/maasserver/static/partials/subnet-details.html b/src/maasserver/static/partials/subnet-details.html |
1473 | index dc22777..5415f96 100755 |
1474 | --- a/src/maasserver/static/partials/subnet-details.html |
1475 | +++ b/src/maasserver/static/partials/subnet-details.html |
1476 | @@ -201,6 +201,7 @@ |
1477 | <maas-obj-field type="text" key="cidr" label="CIDR" placeholder="Subnet CIDR" |
1478 | label-width="2" input-width="4"></maas-obj-field> |
1479 | <maas-obj-field type="text" key="gateway_ip" label="Gateway IP" placeholder="Gateway IP" |
1480 | +<<<<<<< src/maasserver/static/partials/subnet-details.html |
1481 | label-width="2" input-width="4"></maas-obj-field> |
1482 | <maas-obj-field type="text" key="dns_servers" label="DNS" placeholder="DNS nameservers for subnet" |
1483 | label-width="2" input-width="4"></maas-obj-field> |
1484 | @@ -237,6 +238,45 @@ |
1485 | <p>Space</p> |
1486 | </div> |
1487 | <div class="p-form__control"> |
1488 | +======= |
1489 | + label-width="two" input-width="three" class="u-margin--bottom-none"></maas-obj-field> |
1490 | + </maas-obj-field-group> |
1491 | + <maas-obj-field type="text" key="dns_servers" label="DNS" placeholder="DNS nameservers for subnet" |
1492 | + label-width="two" input-width="three"></maas-obj-field> |
1493 | + <maas-obj-field type="textarea" key="description" label="Description" placeholder="Subnet description" |
1494 | + label-width="two" input-width="three"></maas-obj-field> |
1495 | + </fieldset> |
1496 | + <fieldset class="form__fieldset six-col last-col"> |
1497 | + <maas-obj-field type="onoffswitch" key="managed" label="Managed allocation" |
1498 | + label-width="two" input-width="three" |
1499 | + label-info="When enabled, MAAS will assume it may take full control of DHCP and
IP address management on this subnet. When disabled, MAAS will only
allocate addresses from reserved IP ranges on this subnet, and will not
include this subnet's dynamic ranges in the DHCP configuration."> |
1500 | + |
1501 | + </maas-obj-field> |
1502 | + <span data-ng-if="subnet.$maasForm.getValue('managed') != subnet.managed"> |
1503 | + <div class="form__group-label two-col"> </div> |
1504 | + <div class="form__group-input three-col last-col"> |
1505 | + <span data-ng-if="subnet.$maasForm.getValue('managed') == false" class="u-text--subtle"> |
1506 | + <i class="icon icon--warning"></i> |
1507 | + <b>Warning:</b> MAAS will now start allocating IP addresses from reserved ranges. If a reserved range has been defined to prevent MAAS from allocating IP addresses, this behavior will change. |
1508 | + </span> |
1509 | + <span data-ng-if="subnet.$maasForm.getValue('managed') == true" class="u-text--subtle"> |
1510 | + <i class="icon icon--warning"></i> |
1511 | + <b>Warning:</b> MAAS will now allocate IP addresses from the entire subnet, excluding any reserved ranges. |
1512 | + </span> |
1513 | + </div> |
1514 | + </span> |
1515 | + <maas-obj-field data-ng-if="subnet.version === 4" type="onoffswitch" key="active_discovery" label="Active mapping" |
1516 | + label-width="two" input-width="three" label-info="When enabled, MAAS will scan this subnet {$ active_discovery_interval | lowercase $}
to discover hosts that have not been discovered passively."></maas-obj-field> |
1517 | + <maas-obj-field type="options" key="fabric" label="Fabric" placeholder="Choose fabric" |
1518 | + options="fabric.id as fabric.name for fabric in fabrics | orderBy:'name'" |
1519 | + label-width="two" input-width="three"></maas-obj-field> |
1520 | + <maas-obj-field type="options" key="vlan" class="u-margin--bottom" label="VLAN" placeholder="Choose VLAN" |
1521 | + options="v.id as getVLANName(v) for v in vlans | filterByFabric:subnet.$maasForm.getValue('fabric')" |
1522 | + label-width="two" input-width="three"></maas-obj-field> |
1523 | + <dl> |
1524 | + <dt class="two-col u-text--subtle">Space</dt> |
1525 | + <dd class="four-col last-col"> |
1526 | +>>>>>>> src/maasserver/static/partials/subnet-details.html |
1527 | <a data-ng-if="space !== null" href="#/space/{$ space.id $}">{$ space.name $}</a> |
1528 | <span data-ng-if="space === null"> |
1529 | (undefined) |
1530 | diff --git a/src/maasserver/static/partials/vlan-details.html b/src/maasserver/static/partials/vlan-details.html |
1531 | index 8a2826a..404a6e6 100644 |
1532 | --- a/src/maasserver/static/partials/vlan-details.html |
1533 | +++ b/src/maasserver/static/partials/vlan-details.html |
1534 | @@ -231,9 +231,15 @@ |
1535 | <!-- End of "Take action" dropdown --> |
1536 | </div> |
1537 | </header> |
1538 | +<<<<<<< src/maasserver/static/partials/vlan-details.html |
1539 | <section class="p-strip is-bordered"> |
1540 | <div class="row"> |
1541 | <div class="col-6"> |
1542 | +======= |
1543 | + <section class="row"> |
1544 | + <fieldset class="wrapper--inner"> |
1545 | + <div class="twelve-col"> |
1546 | +>>>>>>> src/maasserver/static/partials/vlan-details.html |
1547 | <h2 class="u-float--left">VLAN Summary</h2> |
1548 | </div> |
1549 | <div class="col-6"> |
1550 | @@ -310,6 +316,7 @@ |
1551 | </div> |
1552 | <div class="row"> |
1553 | <maas-obj-form obj="vlanDetails.vlan" manager="vlanDetails.vlanManager" data-ng-disabled="!vlanDetails.isSuperUser()" data-ng-if="vlanDetails.editSummary" |
1554 | +<<<<<<< src/maasserver/static/partials/vlan-details.html |
1555 | table-form="true" save-on-blur="false" after-save="vlanDetails.exitEditSummary"> |
1556 | <div class="row"> |
1557 | <div class="col-6"> |
1558 | @@ -337,11 +344,35 @@ |
1559 | </span></p> |
1560 | </div> |
1561 | <div class="p-form__control"> |
1562 | +======= |
1563 | + table-form="true" save-on-blur="false" after-save="vlanDetails.exitEditSummary"> |
1564 | + <fieldset class="form__fieldset six-col"> |
1565 | + <maas-obj-field type="text" key="vid" label="VID" placeholder="VLAN VID" |
1566 | + label-width="two" input-width="three" blur-on-enter="true"></maas-obj-field> |
1567 | + <maas-obj-field type="text" key="name" label="Name" placeholder="VLAN name" |
1568 | + label-width="two" input-width="three" blur-on-enter="true"></maas-obj-field> |
1569 | + <maas-obj-field type="text" key="mtu" label="MTU" placeholder="VLAN MTU" |
1570 | + label-width="two" input-width="three" blur-on-enter="true"></maas-obj-field> |
1571 | + <maas-obj-field type="options" key="space" label="Space" placeholder="(undefined)" placeholder-enabled="true" |
1572 | + options="space.id as space.name for space in vlanDetails.spaces" |
1573 | + label-width="two" input-width="three"></maas-obj-field> |
1574 | + <maas-obj-field type="textarea" key="description" label="Description" placeholder="VLAN description" |
1575 | + label-width="two" input-width="three" blur-on-enter="true"></maas-obj-field> |
1576 | + </fieldset> |
1577 | + <fieldset class="form__fieldset six-col last-col"> |
1578 | + <dl> |
1579 | + <maas-obj-field type="options" key="fabric" label="Fabric" label-width="two" input-width="three" |
1580 | + options="fabric.id as fabric.name for fabric in vlanDetails.fabrics"></maas-obj-field> |
1581 | + <div data-ng-if="vlanDetails.relatedControllers"> |
1582 | + <dt class="two-col">Rack controllers <span class="icon icon--help tooltip" aria-label="A rack controller controls hosts and images and runs network services
like DHCP for connected VLANs."></span></dt> |
1583 | + <dd class="four-col last-col"> |
1584 | +>>>>>>> src/maasserver/static/partials/vlan-details.html |
1585 | <span data-ng-repeat="rack in vlanDetails.relatedControllers"> |
1586 | <a href="#/controller/{$ rack.system_id $}">{$ rack.hostname $}</a>, |
1587 | </span> |
1588 | </div> |
1589 | </div> |
1590 | +<<<<<<< src/maasserver/static/partials/vlan-details.html |
1591 | </div> |
1592 | </div> |
1593 | <div class="row u-no-margin--top"> |
1594 | @@ -353,6 +384,17 @@ |
1595 | data-ng-click="vlanDetails.exitEditSummary()">Cancel</button> |
1596 | <button class="p-button--positive" maas-obj-save>Save summary</button> |
1597 | </div> |
1598 | +======= |
1599 | + </dl> |
1600 | + </fieldset> |
1601 | + <div class="six-col"> |
1602 | + <maas-obj-errors></maas-obj-errors> |
1603 | + </div> |
1604 | + <div class="six-col last-col u-align--right"> |
1605 | + <button class="button--base button--inline" |
1606 | + data-ng-click="vlanDetails.exitEditSummary()">Cancel</button> |
1607 | + <button class="button--positive button--inline" maas-obj-save>Save summary</button> |
1608 | +>>>>>>> src/maasserver/static/partials/vlan-details.html |
1609 | </div> |
1610 | </maas-obj-form> |
1611 | </div> |
1612 | diff --git a/src/maasserver/tests/test_bootsources.py b/src/maasserver/tests/test_bootsources.py |
1613 | index a83c9c1..3629529 100644 |
1614 | --- a/src/maasserver/tests/test_bootsources.py |
1615 | +++ b/src/maasserver/tests/test_bootsources.py |
1616 | @@ -44,7 +44,10 @@ from maasserver.testing.testcase import ( |
1617 | ) |
1618 | from maasserver.tests.test_bootresources import SimplestreamsEnvFixture |
1619 | from maasserver.utils import get_maas_user_agent |
1620 | +<<<<<<< src/maasserver/tests/test_bootsources.py |
1621 | from maastesting.djangotestcase import count_queries |
1622 | +======= |
1623 | +>>>>>>> src/maasserver/tests/test_bootsources.py |
1624 | from maastesting.matchers import MockCalledOnceWith |
1625 | from provisioningserver.config import DEFAULT_IMAGES_URL |
1626 | from provisioningserver.import_images import ( |
1627 | diff --git a/src/maasserver/tests/test_stats.py b/src/maasserver/tests/test_stats.py |
1628 | index d0200f7..43aae70 100644 |
1629 | --- a/src/maasserver/tests/test_stats.py |
1630 | +++ b/src/maasserver/tests/test_stats.py |
1631 | @@ -69,7 +69,7 @@ class TestMAASStats(MAASServerTestCase): |
1632 | "total_storage": total_storage, |
1633 | }, |
1634 | } |
1635 | - self.assertEquals(stats, json.dumps(compare)) |
1636 | + self.assertEquals(json.loads(stats), compare) |
1637 | |
1638 | def test_get_request_params_returns_params(self): |
1639 | factory.make_RegionRackController() |
1640 | diff --git a/src/maasserver/triggers/tests/test_websocket_listener.py b/src/maasserver/triggers/tests/test_websocket_listener.py |
1641 | index 5e69c2d..5594db9 100644 |
1642 | --- a/src/maasserver/triggers/tests/test_websocket_listener.py |
1643 | +++ b/src/maasserver/triggers/tests/test_websocket_listener.py |
1644 | @@ -1714,8 +1714,13 @@ class TestIPRangeListener( |
1645 | self.create_subnet, {'cidr': str(network)}) |
1646 | params = { |
1647 | 'subnet': subnet, |
1648 | +<<<<<<< src/maasserver/triggers/tests/test_websocket_listener.py |
1649 | 'start_ip': IPAddress(network.first + 2), |
1650 | 'end_ip': IPAddress(network.last - 1)} |
1651 | +======= |
1652 | + 'start_ip': str(IPAddress(network.first + 2)), |
1653 | + 'end_ip': str(IPAddress(network.last - 1))} |
1654 | +>>>>>>> src/maasserver/triggers/tests/test_websocket_listener.py |
1655 | yield listener.startService() |
1656 | try: |
1657 | iprange = yield deferToDatabase( |
1658 | diff --git a/src/metadataserver/user_data/templates/commissioning.template b/src/metadataserver/user_data/templates/commissioning.template |
1659 | index 1ba2510..a1f6ce9 100644 |
1660 | --- a/src/metadataserver/user_data/templates/commissioning.template |
1661 | +++ b/src/metadataserver/user_data/templates/commissioning.template |
1662 | @@ -31,6 +31,10 @@ main() { |
1663 | # the BMC username and password. |
1664 | signal WORKING "Starting [maas-bmc-autodetect]" || exit 1 |
1665 | |
1666 | + # LP:1730524 - Make sure we can signal the metadata service before updating |
1667 | + # the BMC username and password. |
1668 | + signal WORKING "Starting [maas-bmc-autodetect]" || exit 1 |
1669 | + |
1670 | # Install IPMI deps |
1671 | aptget install freeipmi-tools openipmi ipmitool sshpass |
1672 | |
1673 | diff --git a/src/provisioningserver/drivers/power/ipmi.py b/src/provisioningserver/drivers/power/ipmi.py |
1674 | index e3b1561..3783152 100644 |
1675 | --- a/src/provisioningserver/drivers/power/ipmi.py |
1676 | +++ b/src/provisioningserver/drivers/power/ipmi.py |
1677 | @@ -219,7 +219,11 @@ class IPMIPowerDriver(PowerDriver): |
1678 | @staticmethod |
1679 | def _issue_ipmi_chassis_config_command( |
1680 | command, power_change, power_address, power_boot_type=None): |
1681 | +<<<<<<< src/provisioningserver/drivers/power/ipmi.py |
1682 | env = shell.get_env_with_locale() |
1683 | +======= |
1684 | + env = shell.select_c_utf8_locale() |
1685 | +>>>>>>> src/provisioningserver/drivers/power/ipmi.py |
1686 | with NamedTemporaryFile("w+", encoding="utf-8") as tmp_config: |
1687 | # Write out the chassis configuration. |
1688 | if (power_boot_type is None or |
1689 | diff --git a/src/provisioningserver/import_images/boot_resources.py b/src/provisioningserver/import_images/boot_resources.py |
1690 | index f985df6..0a5b3c2 100644 |
1691 | --- a/src/provisioningserver/import_images/boot_resources.py |
1692 | +++ b/src/provisioningserver/import_images/boot_resources.py |
1693 | @@ -9,6 +9,7 @@ __all__ = [ |
1694 | ] |
1695 | |
1696 | from argparse import ArgumentParser |
1697 | +import copy |
1698 | import errno |
1699 | from io import StringIO |
1700 | import os |
1701 | @@ -41,6 +42,18 @@ from provisioningserver.utils.fs import ( |
1702 | read_text_file, |
1703 | tempdir, |
1704 | ) |
1705 | +<<<<<<< src/provisioningserver/import_images/boot_resources.py |
1706 | +======= |
1707 | +from provisioningserver.utils.service_monitor import ServiceActionError |
1708 | +from provisioningserver.utils.shell import ( |
1709 | + call_and_check, |
1710 | + ExternalProcessError, |
1711 | +) |
1712 | +from provisioningserver.utils.snappy import ( |
1713 | + get_snap_data_path, |
1714 | + running_in_snap, |
1715 | +) |
1716 | +>>>>>>> src/provisioningserver/import_images/boot_resources.py |
1717 | from twisted.internet.defer import inlineCallbacks |
1718 | from twisted.python.filepath import FilePath |
1719 | |
1720 | @@ -109,6 +122,54 @@ def write_snapshot_metadata(snapshot, meta_file_content): |
1721 | atomic_write(meta_file_content.encode("ascii"), meta_file, mode=0o644) |
1722 | |
1723 | |
1724 | +<<<<<<< src/provisioningserver/import_images/boot_resources.py |
1725 | +======= |
1726 | +def write_targets_conf(snapshot): |
1727 | + """Write "maas.tgt" file.""" |
1728 | + targets_conf = os.path.join(snapshot, 'maas.tgt') |
1729 | + targets_conf_content = compose_targets_conf(snapshot) |
1730 | + atomic_write(targets_conf_content, targets_conf, mode=0o644) |
1731 | + |
1732 | + |
1733 | +def update_targets_conf(snapshot): |
1734 | + """Runs tgt-admin to update the new targets from "maas.tgt".""" |
1735 | + # Ensure that tgt is running before tgt-admin is used. |
1736 | + try: |
1737 | + service_monitor.ensureService("tgt").wait(30) |
1738 | + except ServiceActionError: |
1739 | + msg = "Unable to start tgt" |
1740 | + try_send_rack_event(EVENT_TYPES.RACK_IMPORT_WARNING, msg) |
1741 | + maaslog.warning(msg) |
1742 | + return |
1743 | + |
1744 | + # Update the tgt config. |
1745 | + targets_conf = os.path.join(snapshot, 'maas.tgt') |
1746 | + |
1747 | + # The targets_conf may not exist in the event the BootSource is broken |
1748 | + # and images havn't been imported yet. This fixes LP:1655721 |
1749 | + if not os.path.exists(targets_conf): |
1750 | + return |
1751 | + |
1752 | + try: |
1753 | + env = copy.deepcopy(os.environ) |
1754 | + # LP:1718706 - When TGT is run in a snap the socket is stored in the |
1755 | + # SNAP_DATA path. Define it before calling tgt-admin otherwise the |
1756 | + # standard path is used and the call will fail. |
1757 | + if running_in_snap(): |
1758 | + env['TGT_IPC_SOCKET'] = os.path.join( |
1759 | + get_snap_data_path(), 'tgtd-socket') |
1760 | + call_and_check(sudo([ |
1761 | + get_path('/usr/sbin/tgt-admin'), |
1762 | + '--conf', targets_conf, |
1763 | + '--update', 'ALL', |
1764 | + ]), env=env) |
1765 | + except ExternalProcessError as e: |
1766 | + msg = "Unable to update TGT config: %s" % e |
1767 | + try_send_rack_event(EVENT_TYPES.RACK_IMPORT_WARNING, msg) |
1768 | + maaslog.warning(msg) |
1769 | + |
1770 | + |
1771 | +>>>>>>> src/provisioningserver/import_images/boot_resources.py |
1772 | def read_sources(sources_yaml): |
1773 | """Read boot resources config file. |
1774 | |
1775 | diff --git a/src/provisioningserver/import_images/tests/test_boot_resources.py b/src/provisioningserver/import_images/tests/test_boot_resources.py |
1776 | index 28635e7..31fa955 100644 |
1777 | --- a/src/provisioningserver/import_images/tests/test_boot_resources.py |
1778 | +++ b/src/provisioningserver/import_images/tests/test_boot_resources.py |
1779 | @@ -43,7 +43,18 @@ from provisioningserver.testing.config import ( |
1780 | BootSourcesFixture, |
1781 | ClusterConfigurationFixture, |
1782 | ) |
1783 | +<<<<<<< src/provisioningserver/import_images/tests/test_boot_resources.py |
1784 | from provisioningserver.utils.fs import write_text_file |
1785 | +======= |
1786 | +from provisioningserver.utils.fs import ( |
1787 | + tempdir, |
1788 | + write_text_file, |
1789 | +) |
1790 | +from provisioningserver.utils.service_monitor import ServiceActionError |
1791 | +from provisioningserver.utils.shell import ExternalProcessError |
1792 | +from testtools.content import Content |
1793 | +from testtools.content_type import UTF8_TEXT |
1794 | +>>>>>>> src/provisioningserver/import_images/tests/test_boot_resources.py |
1795 | from testtools.matchers import ( |
1796 | DirExists, |
1797 | FileExists, |
1798 | @@ -477,6 +488,87 @@ class TestMain(MAASTestCase): |
1799 | boot_resources.NoConfigFile, |
1800 | boot_resources.main, self.make_args(sources="", sources_file="")) |
1801 | |
1802 | +<<<<<<< src/provisioningserver/import_images/tests/test_boot_resources.py |
1803 | +======= |
1804 | + def test_update_targets_conf_ensures_tgt_service(self): |
1805 | + mock_ensureService = self.patch( |
1806 | + boot_resources.service_monitor, "ensureService") |
1807 | + self.patch(boot_resources, "call_and_check") |
1808 | + boot_resources.update_targets_conf(factory.make_name("snapshot")) |
1809 | + self.assertThat(mock_ensureService, MockCalledOnceWith("tgt")) |
1810 | + |
1811 | + def test_update_targets_conf_logs_tgt_service_check_error(self): |
1812 | + # Regression test for LP:1735025 |
1813 | + mock_ensureService = self.patch( |
1814 | + boot_resources.service_monitor, "ensureService") |
1815 | + mock_ensureService.side_effect = ServiceActionError() |
1816 | + mock_try_send_rack_event = self.patch( |
1817 | + boot_resources, 'try_send_rack_event') |
1818 | + mock_maaslog = self.patch(boot_resources.maaslog, 'warning') |
1819 | + boot_resources.update_targets_conf(factory.make_name("snapshot")) |
1820 | + self.assertThat(mock_try_send_rack_event, MockCalledOnce()) |
1821 | + self.assertThat(mock_maaslog, MockCalledOnce()) |
1822 | + |
1823 | + def test_update_targets_conf_logs_error(self): |
1824 | + self.patch(boot_resources.service_monitor, "ensureService") |
1825 | + mock_try_send_rack_event = self.patch( |
1826 | + boot_resources, 'try_send_rack_event') |
1827 | + mock_maaslog = self.patch(boot_resources.maaslog, 'warning') |
1828 | + self.patch(boot_resources.os.path, 'exists').return_value = True |
1829 | + self.patch(boot_resources, 'call_and_check').side_effect = ( |
1830 | + ExternalProcessError( |
1831 | + returncode=2, cmd=('tgt-admin',), output='error')) |
1832 | + snapshot = factory.make_name("snapshot") |
1833 | + boot_resources.update_targets_conf(snapshot) |
1834 | + self.assertThat(mock_try_send_rack_event, MockCalledOnce()) |
1835 | + self.assertThat(mock_maaslog, MockCalledOnce()) |
1836 | + self.assertThat( |
1837 | + boot_resources.call_and_check, |
1838 | + MockCalledOnceWith([ |
1839 | + 'sudo', '-n', '/usr/sbin/tgt-admin', |
1840 | + '--conf', os.path.join(snapshot, 'maas.tgt'), |
1841 | + '--update', 'ALL'], env=os.environ)) |
1842 | + |
1843 | + def test_update_targets_only_runs_when_conf_exists(self): |
1844 | + # Regression test for LP:1655721 |
1845 | + temp_dir = self.useFixture(TempDirectory()).path |
1846 | + self.useFixture(ClusterConfigurationFixture(tftp_root=temp_dir)) |
1847 | + mock_ensureService = self.patch( |
1848 | + boot_resources.service_monitor, "ensureService") |
1849 | + mock_call_and_check = self.patch(boot_resources, "call_and_check") |
1850 | + mock_path_exists = self.patch(boot_resources.os.path, 'exists') |
1851 | + mock_path_exists.return_value = False |
1852 | + boot_resources.update_targets_conf(temp_dir) |
1853 | + self.assertThat(mock_ensureService, MockCalledOnceWith("tgt")) |
1854 | + self.assertThat( |
1855 | + mock_path_exists, |
1856 | + MockCalledOnceWith(os.path.join(temp_dir, 'maas.tgt'))) |
1857 | + self.assertThat(mock_call_and_check, MockNotCalled()) |
1858 | + |
1859 | + def test_update_targets_conf_sets_env_var_in_snap(self): |
1860 | + # Regression test for LP:1718706 |
1861 | + self.patch(boot_resources.service_monitor, "ensureService") |
1862 | + self.patch(boot_resources.os.path, 'exists').return_value = True |
1863 | + self.patch(boot_resources, 'call_and_check') |
1864 | + self.patch(boot_resources, 'running_in_snap').return_value = True |
1865 | + snap_data_path = factory.make_name('snap_data_path') |
1866 | + self.patch(boot_resources, 'get_snap_data_path').return_value = ( |
1867 | + snap_data_path) |
1868 | + snapshot = factory.make_name("snapshot") |
1869 | + boot_resources.update_targets_conf(snapshot) |
1870 | + self.assertThat( |
1871 | + boot_resources.call_and_check, |
1872 | + MockCalledOnceWith([ |
1873 | + 'sudo', '-n', '/usr/sbin/tgt-admin', |
1874 | + '--conf', os.path.join(snapshot, 'maas.tgt'), |
1875 | + '--update', 'ALL' |
1876 | + ], env={ |
1877 | + 'TGT_IPC_SOCKET': os.path.join( |
1878 | + snap_data_path, 'tgtd-socket'), |
1879 | + **os.environ, |
1880 | + })) |
1881 | + |
1882 | +>>>>>>> src/provisioningserver/import_images/tests/test_boot_resources.py |
1883 | |
1884 | class TestMetaContains(MAASTestCase): |
1885 | """Tests for the `meta_contains` function.""" |
1886 | diff --git a/src/provisioningserver/import_images/tests/test_download_resources.py b/src/provisioningserver/import_images/tests/test_download_resources.py |
1887 | index 9f40e29..81bc3c1 100644 |
1888 | --- a/src/provisioningserver/import_images/tests/test_download_resources.py |
1889 | +++ b/src/provisioningserver/import_images/tests/test_download_resources.py |
1890 | @@ -359,6 +359,41 @@ class TestRepoWriter(MAASTestCase): |
1891 | label=product['label'], subarches={'ga-16.04', 'generic'}, |
1892 | bootloader_type=None)) |
1893 | |
1894 | +<<<<<<< src/provisioningserver/import_images/tests/test_download_resources.py |
1895 | + def test_inserts_no_generic_link_for_generic_non_ga_kflavor(self): |
1896 | + # Regression test for LP:1749246 |
1897 | +======= |
1898 | + def test_inserts_generic_link_for_generic_ga_kflavor(self): |
1899 | +>>>>>>> src/provisioningserver/import_images/tests/test_download_resources.py |
1900 | + product_mapping = ProductMapping() |
1901 | + product = self.make_product(subarch='hwe-16.04', kflavor='generic') |
1902 | + product_mapping.add(product, 'hwe-16.04') |
1903 | + repo_writer = download_resources.RepoWriter( |
1904 | + None, None, product_mapping) |
1905 | + self.patch( |
1906 | + download_resources, 'products_exdata').return_value = product |
1907 | + # Prevent MAAS from trying to actually write the file. |
1908 | + mock_insert_file = self.patch(download_resources, 'insert_file') |
1909 | + mock_link_resources = self.patch(download_resources, 'link_resources') |
1910 | + # We only need to provide the product as the other fields are only used |
1911 | + # when writing the actual files to disk. |
1912 | + repo_writer.insert_item(product, None, None, None, None) |
1913 | + # None is used for the store and the content source as we're not |
1914 | + # writing anything to disk. |
1915 | + self.assertThat( |
1916 | + mock_insert_file, |
1917 | + MockCalledOnceWith( |
1918 | + None, os.path.basename(product['path']), product['sha256'], |
1919 | + {'sha256': product['sha256']}, product['size'], None)) |
1920 | + # links are mocked out by the mock_insert_file above. |
1921 | + self.assertThat( |
1922 | + mock_link_resources, |
1923 | + MockCalledOnceWith( |
1924 | + snapshot_path=None, links=mock.ANY, osystem=product['os'], |
1925 | + arch=product['arch'], release=product['release'], |
1926 | + label=product['label'], subarches={'hwe-16.04'}, |
1927 | + bootloader_type=None)) |
1928 | + |
1929 | def test_inserts_no_generic_link_for_generic_non_ga_kflavor(self): |
1930 | # Regression test for LP:1749246 |
1931 | product_mapping = ProductMapping() |
1932 | diff --git a/src/provisioningserver/utils/tests/test_network.py b/src/provisioningserver/utils/tests/test_network.py |
1933 | index bda51e1..3b3c6b0 100644 |
1934 | --- a/src/provisioningserver/utils/tests/test_network.py |
1935 | +++ b/src/provisioningserver/utils/tests/test_network.py |
1936 | @@ -83,7 +83,10 @@ from provisioningserver.utils.network import ( |
1937 | resolves_to_loopback_address, |
1938 | reverseResolve, |
1939 | ) |
1940 | +<<<<<<< src/provisioningserver/utils/tests/test_network.py |
1941 | from provisioningserver.utils.shell import get_env_with_locale |
1942 | +======= |
1943 | +>>>>>>> src/provisioningserver/utils/tests/test_network.py |
1944 | from testtools import ExpectedException |
1945 | from testtools.matchers import ( |
1946 | Contains, |
1947 | diff --git a/utilities/release-build b/utilities/release-build |
1948 | index 3c0a5af..49f32cc 100755 |
1949 | --- a/utilities/release-build |
1950 | +++ b/utilities/release-build |
1951 | @@ -15,7 +15,7 @@ usage () { |
1952 | exit 1 |
1953 | } |
1954 | |
1955 | -DEFAULT_DISTROS="$(ubuntu-distro-info --supported | grep -v trusty)" |
1956 | +DEFAULT_DISTROS="$(ubuntu-distro-info --supported | grep -v trusty | grep -v bionic)" |
1957 | PACKAGE_DISTROS="" |
1958 | KEEP_CHANGELOG=0 |
1959 | |
1960 | @@ -61,7 +61,7 @@ dch -a "" --release-heuristic log --nomultimaint |
1961 | PKG=$(head -n1 debian/changelog | awk '{print $1}') |
1962 | MAJOR_VER=$(head -n 1 debian/changelog | sed 's/^.*(//' | sed 's/).*//' | sed 's/-.*//') |
1963 | REV_COUNT=$(git rev-list --count $COMMIT) |
1964 | -REV_SHORT=$(git rev-parse --short $COMMIT) |
1965 | +REV_SHORT=$(git rev-parse --short=7 $COMMIT) |
1966 | FULL_VER="$MAJOR_VER-$REV_COUNT-g$REV_SHORT" |
1967 | TARBALL="maas_$FULL_VER.orig.tar.gz" |
1968 | |
1969 | @@ -89,3 +89,12 @@ if echo "$PACKAGE_DISTROS" | grep -q "bionic"; then |
1970 | sed -i "s/) UNRELEASED;/~18.04.1) bionic;/i" debian/changelog |
1971 | debuild -S -sa |
1972 | fi |
1973 | +<<<<<<< utilities/release-build |
1974 | +======= |
1975 | + |
1976 | +if echo "$PACKAGE_DISTROS" | grep -q "artful"; then |
1977 | + cp $ROOTDIR/debian/changelog debian/changelog |
1978 | + sed -i "s/) UNRELEASED;/~17.10.1) artful;/" debian/changelog |
1979 | + debuild -S |
1980 | +fi |
1981 | +>>>>>>> utilities/release-build |