Retire ironic-webclient repository contents

Per the annoucement[0], the ironic-webclient is being
retired from future development. Please see README for
instructions on obtaining the state of the repository
prior to retirement.

[0]: http://lists.openstack.org/pipermail/openstack-dev/2016-December/109195.html

Change-Id: Ie5f1cc69830fcaacc3db05d6e77a775c9634f6db
This commit is contained in:
Julia Kreger 2016-12-21 19:25:06 +00:00
parent c938dc9817
commit 04e7da6f47
118 changed files with 10 additions and 11344 deletions

View File

@ -1,12 +0,0 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false

View File

@ -1,5 +0,0 @@
cover
www
bower_components
node_modules
dist

View File

@ -1,23 +0,0 @@
# Enable eslint-plugin-angular
plugins:
- angular
extends: openstack
# Set up globals
globals:
angular: false
module: false
env:
browser: true
jasmine: true
rules:
angular/no-private-call: 0
angular/no-services:
- 2
- - $http
- $resource
- Restangular
angular/no-service-method: 0

14
.gitignore vendored
View File

@ -1,14 +0,0 @@
*.iml
.idea
bower_components/
node_modules/
cover/
npm-debug.log
www/
.publish/
*.tar.gz
*.tgz
dist
# Operating system specific files
.DS_Store # OSX Folder Caches

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/ironic-webclient.git

202
LICENSE
View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

10
README Normal file
View File

@ -0,0 +1,10 @@
This project is no longer maintained.
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.

115
README.md
View File

@ -1,115 +0,0 @@
# ironic-webclient
A webclient for OpenStack Ironic.
## Important Things
It is imperative that the design and infrastructure of this project makes it
easy to package for various linux distributions. As such, the following
decisions have been made:
##### The project must be fully functional directly from source.
* ECMAScript 5 only, no transpiled languages such as TypeScript or
CoffeeScript.
* All javascript libraries used at runtime (in the browser) must be committed
to source.
Note that we do not guarantee performance if running in this mode. Certain
things, such as in-browser CSS compilation will severely degrade the
performance of this application.
##### The project must be easy to develop on.
* We use common javascript tooling to assist in development (npm, gulp,
eslint, bower, etc).
* These tools are supportive, but not required, as such they are considered to
be environmental, and thus not committed to source.
##### The project must be compatible with the OpenStack License.
* All runtime dependencies and development tools must use licenses compatible
with the Apache2.0 license.
## Installation
You will need Node.JS and NPM installed on the target system. For Fedora and/or
CentOS systemd you can install these as follows:
```
$ yum install -y nodejs npm #dnf on Fedora
```
and on Debian/Ubuntu with:
```
$ apt-get install nodejs nodejs-legacy npm
```
To install the webclient you have to check out the code and install the
dependencies. This can be done as follows:
```
$ git clone https://github.com/openstack/ironic-webclient.git
$ cd ironic-webclient
$ npm install
```
### Enable CORS
To be able to access the Ironic API, you have to enable CORS. A description for
this can be found in the chapter [Cross-origin resource sharing](http://docs.openstack.org/admin-guide/cross_project_cors.html)
of the OpenStack Administrator's Guide.
### Hosting
After installing the dependencies and enabling CORS, you have two options of
running the web application; using the development server or hosted using
a webserver.
#### Development Server
The webclient offers a built-in webserver option for development purpose. It is
not recommended to use this in production use. You can start this server with:
```
$ npm start
```
Which will start the server on [localhost, port 8000](http://localhost:8000).
If you want to run on a different port or IP, you can specify this from the
command line as follows:
```
IP=0.0.0.0 PORT=8080 npm start
```
#### Webserver
For production use, it is recommended to use a webserver. After running
```
$ npm pack
```
you will have a package which will contain all assets needed for hosting the
application.
## Other options
* Package the site.
```
npm pack
```

View File

@ -1,88 +0,0 @@
@import "./bootstrap_variables";
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap";
@import "./bootstrap_mixins";
.pull-right-bottom {
position: absolute;
right: $grid-gutter-width / 2;
bottom: $padding-base-horizontal;
}
// Navbar form that respects the column-grid
.navbar-form-grid {
margin-left: -$navbar-padding-horizontal;
margin-right: -$navbar-padding-horizontal;
padding: 8px $navbar-padding-horizontal;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
$shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);
@include box-shadow($shadow);
}
/**
* Button default should have button primary active state.
*/
.btn-default {
&.active, &.active:active, &.active:focus, &.active:hover {
background-color: $brand-primary;
color: $btn-primary-color;
border-color: $btn-primary-border;
}
}
/**
* Custom button gradient styles.
*/
.btn-default-gradient {
@include button-gradient-variant($btn-default-color, $btn-default-bg, $btn-default-border);
}
.btn-primary-gradient {
@include button-gradient-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border);
}
// Success appears as green
.btn-success-gradient {
@include button-gradient-variant($btn-success-color, $btn-success-bg, $btn-success-border);
}
// Info appears as blue-green
.btn-info-gradient {
@include button-gradient-variant($btn-info-color, $btn-info-bg, $btn-info-border);
}
// Warning appears as orange
.btn-warning-gradient {
@include button-gradient-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border);
}
// Danger and error appear as red
.btn-danger-gradient {
@include button-gradient-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border);
}
/**
* Additional spacer styles for vertical padding
*/
.row {
&.padding-top {
padding-top: $padding-large-vertical;
}
}
/**
* Additional table styles
*/
table.table {
th {
background-color: $gray-lighter;
white-space: nowrap;
}
}
/**
* Form controls inside of table columns.
*/
table th .form-control {
display: inline-block;
width: auto;
padding: $padding-xs-vertical $padding-xs-horizontal;
height: auto;
}

View File

@ -1,43 +0,0 @@
// Button variants
//
// Easily pump out default styles, as well as :hover, :focus, :active,
// and disabled options for all buttons
@mixin button-gradient-variant($color, $background, $border) {
@include button-variant($color, $background, $border); // Use default styles
//... and override the backgrounds.
@include gradient-vertical($background, darken($background, 10%));
&:focus,
&.focus {
@include gradient-vertical(darken($background, 10%), darken($background, 20%));
}
&:hover {
@include gradient-vertical(darken($background, 10%), darken($background, 20%));
}
&:active,
&.active,
.open > &.dropdown-toggle {
@include gradient-vertical(darken($background, 10%), darken($background, 20%));
&:hover,
&:focus,
&.focus {
@include gradient-vertical(darken($background, 17%), darken($background, 27%));
}
}
&.disabled,
&[disabled],
fieldset[disabled] & {
&:hover,
&:focus,
&.focus {
@include gradient-vertical($background, darken($background, 10%));
}
}
.badge {
@include gradient-vertical($color, darken($color, 10%));
}
}

View File

@ -1,4 +0,0 @@
// Bootstrap variable overrides for this UI.
$navbar-default-bg: #f8f8f8;
$navbar-default-border: $navbar-default-bg;

View File

@ -1 +0,0 @@
@import "../../bower_components/fontawesome/scss/font-awesome";

View File

@ -1,4 +0,0 @@
body {
padding-top: 70px;
padding-bottom: 70px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,38 +0,0 @@
@font-face {
font-family: "<%= fontName %>";
src: url('<%= fontPath %><%= fontName %>.eot');
src: url('<%= fontPath %><%= fontName %>.eot?#iefix') format('eot'),
url('<%= fontPath %><%= fontName %>.woff') format('woff'),
url('<%= fontPath %><%= fontName %>.ttf') format('truetype'),
url('<%= fontPath %><%= fontName %>.svg#<%= fontName %>') format('svg');
font-weight: normal;
font-style: normal;
}
.<%= className %>:before {
display: inline-block;
font-family: "<%= fontName %>";
font-style: normal;
font-weight: normal;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
vertical-align: -15%
}
.<%= className %>-lg {
font-size: 1.3333333333333333em;
line-height: 0.75em;
vertical-align: -15%;
}
.<%= className %>-2x { font-size: 2em; }
.<%= className %>-3x { font-size: 3em; }
.<%= className %>-4x { font-size: 4em; }
.<%= className %>-5x { font-size: 5em; }
.<%= className %>-fw {
width: 1.2857142857142858em;
text-align: center;
}
<% _.each(glyphs, function(glyph) { %>.<%= className %>-<%= glyph.name %>:before { content: "\<%= glyph.unicode[0].charCodeAt(0).toString(16).toUpperCase() %>" }
<% }); %>

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
id="Layer_1" sodipodi:docname="openstack-black.svg" inkscape:version="0.48.4 r9939" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100px" height="100px"
viewBox="0 -20 100 100" enable-background="new 0 -20 100 100" xml:space="preserve">
<g>
<path id="path3038" inkscape:connector-curvature="0" d="M4.522,67.311c-0.748-0.731-1.157-1.744-1.156-2.855l0.035-16.231
l9.387,9.284l-0.029,17.95L4.522,67.311z"/>
<polygon id="polygon3040" points="81.236,20.472 96.543,19.128 96.646,44.657 81.338,45.997 "/>
<polygon id="polygon3042" points="14.87,26.281 30.188,24.941 30.286,50.473 14.971,51.813 "/>
<polygon id="polygon3044" points="3.409,41.106 3.315,16.472 12.692,25.742 12.792,50.384 "/>
<path id="path3046" inkscape:connector-curvature="0" d="M18.894,79.873c-1.091,0-2.096-0.406-2.831-1.144
c-0.732-0.737-1.135-1.748-1.131-2.844l0.029-17.833l15.32-1.344l-0.006,2.842c0,2.102,1.635,3.743,3.725,3.743l0,0
c0.113,0,0.23-0.005,0.348-0.015l43.072-3.774c2.182-0.196,3.895-2.055,3.908-4.231l0.004-3.036l15.307-1.339l-0.029,17.641
c-0.006,2.501-2.061,4.719-4.576,4.941l-72.745,6.375C19.155,79.868,19.024,79.873,18.894,79.873z"/>
<path id="path3048" inkscape:connector-curvature="0" d="M15.006,2.842c0.005-2.5,2.056-4.713,4.572-4.937l72.748-6.338
c0.117-0.01,0.242-0.015,0.363-0.015c1.105,0,2.117,0.409,2.857,1.148c0.734,0.737,1.141,1.75,1.139,2.854l-0.061,17.272
l-15.299,1.328l0.002-2.283c0-2.099-1.635-3.743-3.723-3.743c-0.113,0-0.23,0.005-0.346,0.015l-43.079,3.743
c-2.152,0.191-3.901,2.088-3.901,4.23l-0.002,2.475l-15.318,1.334L15.006,2.842L15.006,2.842z"/>
<path id="path3050" inkscape:connector-curvature="0" d="M3.393,9.228L3.446-8.587c0.005-2.512,2.055-4.728,4.57-4.944l72.75-6.324
c0.131-0.012,0.262-0.018,0.395-0.018c1.084,0,2.08,0.399,2.805,1.124c0,0,6.672,6.59,8.244,8.145
c-0.021,0.001-72.82,6.344-72.82,6.344c-3.609,0.318-6.549,3.502-6.557,7.096l-0.044,15.681L3.393,9.228L3.393,9.228z"/>
<polygon id="polygon3052" points="79.85,48.31 93.551,47.109 95.295,48.836 80.654,50.117 71.115,40.689 72.061,40.605 "/>
<path id="path3054" inkscape:connector-curvature="0" d="M72.832,10.71l4.615-0.402c0.053-0.005,0.105-0.007,0.158-0.007
c0.896,0,1.549,0.659,1.549,1.567l-0.008,4.655l14.252-1.236l1.793,1.775l-14.641,1.286L72.832,10.71z"/>
<polygon id="polygon3056" points="69.785,35.295 69.688,10.984 69.99,10.958 79.059,19.932 79.158,44.568 "/>
<polygon id="polygon3058" points="13.461,22.238 26.997,21.059 28.835,22.876 14.185,24.158 4.652,14.735 5.772,14.638 "/>
<polygon id="polygon3060" points="13.482,54.125 27.201,52.925 28.938,54.644 14.283,55.931 4.752,46.503 5.692,46.419 "/>
<path id="path3062" inkscape:connector-curvature="0" d="M34.008,61.12c-0.073,0-0.146-0.004-0.217-0.013l-0.133-0.036
c-0.114-0.023-0.158-0.029-0.199-0.043l-0.105-0.054c-0.102-0.044-0.15-0.063-0.193-0.09l-0.088-0.071
c-0.068-0.049-0.112-0.076-0.148-0.11c-0.308-0.295-0.477-0.702-0.477-1.15l0.012-4.485l-2.418-2.392l2.424-0.213l-0.005-1.47
l33.401-2.925c2.146-0.186,3.896-2.084,3.904-4.232v-1.425l9.395,9.283l-0.006,3.57c-0.008,1.028-0.891,1.981-1.93,2.074
l-43.066,3.773C34.108,61.118,34.058,61.12,34.008,61.12z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100px" height="100px" viewBox="-282.625 482.5 100 100" enable-background="new -282.625 482.5 100 100"
xml:space="preserve">
<path d="M-232.5,485.549c6.988,0,13.633,1.471,19.643,4.119l-0.006,0.002c0.045-0.068,0.092-0.139,0.139-0.207
c2.832-4.191,7.629-6.949,13.068-6.949c8.704,0,15.76,7.057,15.76,15.76c0,5.01-2.336,9.473-5.979,12.357l0.004-0.002
c3.9,7.008,6.122,14.807,6.122,23.396c-0.001,26.926-21.827,48.479-48.751,48.479h-0.25c-26.924,0-48.75-21.553-48.75-48.477
c0-8.59,2.221-16.525,6.122-23.535l0.004,0.072c-3.643-2.889-5.979-7.316-5.979-12.324c0-8.705,7.056-15.744,15.76-15.744
c5.44,0,10.236,2.684,13.069,6.875c0.045,0.07,0.091,0.133,0.137,0.133h-0.005c6.009-3,12.654-4.037,19.642-4.037 M-215.874,527.504
h-33.504c-4.322,4-6.998,10.109-6.998,16.689c0,13.117,10.634,23.678,23.75,23.678c13.117,0,23.751-10.525,23.751-23.643
C-208.875,537.65-211.55,531.504-215.874,527.504z M-245.542,534.504c0,8,5.782,12.916,12.916,12.916
c7.135,0,12.918-4.916,12.918-12.916H-245.542z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,50 +0,0 @@
<!DOCTYPE html>
<html ng-app="ironic">
<head>
<title>OpenStack Bare Metal</title>
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon">
<link rel="icon" href="./favicon.ico" type="image/x-icon">
<!-- Angular v1.4.0 -->
<script type="application/javascript"
src="js/lib/angular.js"></script>
<!-- State-based routing for AngularJS v0.2.14 -->
<script type="application/javascript"
src="js/lib/angular-ui-router.js"></script>
<!-- provides interaction support with RESTful services -->
<script type="application/javascript"
src="js/lib/angular-resource.js"></script>
<!-- provides a convenient wrapper for reading and writing browser cookies -->
<script type="application/javascript"
src="js/lib/angular-cookies.js"></script>
<!-- Native AngularJS directives for Bootstrap -->
<script type="application/javascript"
src="js/lib/ui-bootstrap-tpls.js"></script>
<!-- Checkboxes-as-list-selection modal -->
<script type="application/javascript"
src="js/lib/checklist-model.js"></script>
<!-- OpenStack specific configuration discovery -->
<script type="application/javascript" src="js/openstack.js"></script>
<!-- The Ironic-Webclient root application -->
<script type="application/javascript" src="js/ironic.js"></script>
<!-- UI Utilities used by the ironic client -->
<script type="application/javascript" src="js/util.js"></script>
<!-- A module of resources that talk with the ironic API -->
<script type="application/javascript" src="js/api.js"></script>
<link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>
<link rel="stylesheet" type="text/css" href="css/font-awesome.css"/>
<link rel="stylesheet" type="text/css" href="css/main.css"/>
<link rel="stylesheet" type="text/css" href="css/openstack-font.css"/>
</head>
<body>
<div ui-view>
<p class="text-center">
<i class="fa fa-refresh fa-spin"></i>
Loading...
</p>
</div>
</body>
</html>

View File

@ -1,84 +0,0 @@
/*
* Copyright (c) 2015 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Logic that needs to be applied to every request against the Ironic API. Includes result
* transformation and other things.
*/
angular.module('ironic.api').service('ironicApiInterceptor',
function($q, $http) {
'use strict';
/**
* Ironic's error message is a doubly-encoded JSON string. This checks the body for that
* field and runs a second decoder.
*
* @param {*} responseBody The decoded JSON response body.
* @param {Function} headers Headers getter.
* @param {int} status The HTTP Status of the response.
* @returns {*} The decoded response body.
*/
function parseErrorResponse (responseBody, headers, status) {
if (400 <= status && responseBody.hasOwnProperty('error_message')) {
responseBody.error_message = angular.fromJson(responseBody.error_message);
}
return responseBody;
}
/**
* In some cases (as with list responses), we need to return the content of a child property
* of the returned body rather than the whole response. This method returns a transformation
* function that applies this change.
*
* @param {String} propertyName The property name to reduce to.
* @returns {Function} A function that reduces the response appropriately.
*/
function parseChildProperty (propertyName) {
/**
* Response transformer for a specific ironic list response. If the property is not found
* (As with error responses), simply returns the original object.
*
* @param {*} responseBody The body received from the server.
* @returns {Array} List of results.
*/
return function(responseBody) {
if (responseBody.hasOwnProperty(propertyName)) {
return responseBody[propertyName];
}
return responseBody;
};
}
return {
/**
* Transform the result of an object query from the Ironic API, accommodating for error
* and list responses.
*
* @param {String} childPropertyName (optional) The name of the child property to reduce to.
* @returns {Array} An array of transformations.
*/
response: function(childPropertyName) {
var transformers = $http.defaults.transformResponse.concat([]);
transformers.push(parseErrorResponse);
if (childPropertyName) {
transformers.push(parseChildProperty(childPropertyName));
}
return transformers;
}
};
});

View File

@ -1,29 +0,0 @@
/*
* Copyright (c) 2015 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* The current supported version of the ironic api. This version is hardcoded until a more
* sophisticated version negotiation algorithm can be provided. The main blocker is that the
* current APi abstraction library - ngResource - does not provide pre-flight modificiation of
* requests via promise resolution. A patch has been provided
* https://github.com/angular/angular.js/pull/13273; if accepted, this parameter will be
* incorproated into the ironic_api_interceptor. If not, alternative libraries will have to be
* evaluated.
*/
angular.module('ironic.api').constant('ironicApiVersion', function() {
'use strict';
return '1.14';
});

View File

@ -1,122 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* IronicChassis is an ngResource abstraction, which switches which Ironic it talks to based on the
* selected cloud configuration provided by the $$configuration service. It may be used as if it
* was an ngResource instance, and supports the methods `query`, `create`, `read`, `update`, and
* `remove`.
*/
angular.module('ironic.api').factory('IronicChassis',
function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource,
ironicApiInterceptor, ironicApiVersion) {
'use strict';
/**
* This method extracts the current active API root URI from $$configuration, ensures that
* the appropriate Ironic resources exists in the $$resourceCache, and returns it.
*
* @returns {{}} The created, and cached, IronicPort resource.
*/
function getResource () {
// Pull the current configuration.
var currentConfig = $$selectedConfiguration.get();
// This should resolve the API root, except in cases where the selected configuration is
// invalid and/or has not yet been resolved.
var ironicConfig = currentConfig.ironic || {};
var ironicApiRoot = ironicConfig.apiRoot || null;
if (!ironicApiRoot) {
$log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
return $$dummyResource;
}
// Build and/or retrieve a cached instance of the requested service.
var chassisUrl = ironicApiRoot + '/chassis/:uuid';
if (!$$resourceCache.has(chassisUrl)) {
$log.debug("Creating new IronicChassis at: " + chassisUrl);
var resource = $resource(chassisUrl, {uuid: '@uuid'}, {
query: {
method: 'GET',
isArray: true,
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response('chassis')
},
create: {
method: 'POST',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
read: {
method: 'GET',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
update: {
method: 'PUT',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
remove: {
method: 'DELETE',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
}
});
$$resourceCache.set(chassisUrl, resource);
}
return $$resourceCache.get(chassisUrl);
}
return {
query: function() {
var r = getResource();
return r.query.apply(r, arguments);
},
create: function() {
var r = getResource();
return r.create.apply(r, arguments);
},
read: function() {
var r = getResource();
return r.read.apply(r, arguments);
},
update: function() {
var r = getResource();
return r.update.apply(r, arguments);
},
remove: function() {
var r = getResource();
return r.remove.apply(r, arguments);
}
};
});

View File

@ -1,120 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* IronicDriver is an ngResource abstraction, which switches which Ironic it talks to based on the
* selected cloud configuration provided by the $$configuration service. It may be used as if it
* was an ngResource instance, and supports the methods `query`, `create`, `read`, `update`, and
* `remove`.
*/
angular.module('ironic.api').factory('IronicDriver',
function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource,
ironicApiInterceptor, ironicApiVersion) {
'use strict';
/**
* This method extracts the current active API root URI from $$configuration, ensures that
* the appropriate Ironic resources exists in the $$resourceCache, and returns it.
*
* @returns {{}} The created, and cached, IronicPort resource.
*/
function getResource () {
// Pull the current configuration.
var currentConfig = $$selectedConfiguration.get();
// This should resolve the API root, except in cases where the selected configuration is
// invalid and/or has not yet been resolved.
var ironicConfig = currentConfig.ironic || {};
var ironicApiRoot = ironicConfig.apiRoot || null;
if (!ironicApiRoot) {
$log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
return $$dummyResource;
}
// Build and/or retrieve a cached instance of the requested service.
var driverUrl = ironicApiRoot + '/drivers/:uuid';
if (!$$resourceCache.has(driverUrl)) {
$log.debug("Creating new IronicDriver at: " + driverUrl);
var resource = $resource(driverUrl, {uuid: '@uuid'}, {
query: {
method: 'GET',
isArray: true,
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response('drivers')
},
create: {
method: 'POST',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
read: {
method: 'GET',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
update: {
method: 'PUT',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
remove: {
method: 'DELETE',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
}
});
$$resourceCache.set(driverUrl, resource);
}
return $$resourceCache.get(driverUrl);
}
return {
query: function() {
var r = getResource();
return r.query.apply(r, arguments);
},
create: function() {
var r = getResource();
return r.create.apply(r, arguments);
},
read: function() {
var r = getResource();
return r.read.apply(r, arguments);
},
update: function() {
var r = getResource();
return r.update.apply(r, arguments);
},
remove: function() {
var r = getResource();
return r.remove.apply(r, arguments);
}
};
});

View File

@ -1,75 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* IronicDriverProperites is an ngResource abstraction, which switches which Ironic it talks to
* based on the selected cloud configuration provided by the $$configuration service. It may be
* used as if it was an ngResource instance, and supports the methods `query`, `create`, `read`,
* `update`, and * `remove`.
*/
angular.module('ironic.api').factory('IronicDriverProperties',
function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource,
ironicApiInterceptor, ironicApiVersion) {
'use strict';
/**
* This method extracts the current active API root URI from $$configuration, ensures that
* the appropriate Ironic resources exists in the $$resourceCache, and returns it.
*
* @returns {{}} The created, and cached, IronicPort resource.
*/
function getResource () {
// Pull the current configuration.
var currentConfig = $$selectedConfiguration.get();
// This should resolve the API root, except in cases where the selected configuration is
// invalid and/or has not yet been resolved.
var ironicConfig = currentConfig.ironic || {};
var ironicApiRoot = ironicConfig.apiRoot || null;
if (!ironicApiRoot) {
$log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
return $$dummyResource;
}
// Build and/or retrieve a cached instance of the requested service.
var driverUrl = ironicApiRoot + '/drivers/properties?driver_name=:driver_name';
if (!$$resourceCache.has(driverUrl)) {
$log.debug("Creating new IronicDriverProperties at: " + driverUrl);
var resource = $resource(driverUrl, {driver_name: 'driver_name'}, {
read: {
method: 'GET',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
}
});
$$resourceCache.set(driverUrl, resource);
}
return $$resourceCache.get(driverUrl);
}
return {
read: function() {
var r = getResource();
return r.read.apply(r, arguments);
}
};
});

View File

@ -1,120 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* IronicNode is an ngResource abstraction, which switches which Ironic it talks to based on the
* selected cloud configuration provided by the $$configuration service. It may be used as if it
* was an ngResource instance, and supports the methods `query`, `create`, `read`, `update`, and
* `remove`.
*/
angular.module('ironic.api').factory('IronicNode',
function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource,
ironicApiInterceptor, ironicApiVersion) {
'use strict';
/**
* This method extracts the current active API root URI from $$configuration, ensures that
* the appropriate Ironic resources exists in the $$resourceCache, and returns it.
*
* @returns {{}} The created, and cached, IronicPort resource.
*/
function getResource () {
// Pull the current configuration.
var currentConfig = $$selectedConfiguration.get();
// This should resolve the API root, except in cases where the selected configuration is
// invalid and/or has not yet been resolved.
var ironicConfig = currentConfig.ironic || {};
var ironicApiRoot = ironicConfig.apiRoot || null;
if (!ironicApiRoot) {
$log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
return $$dummyResource;
}
// Build and/or retrieve a cached instance of the requested service.
var nodeUrl = ironicApiRoot + '/nodes/:uuid';
if (!$$resourceCache.has(nodeUrl)) {
$log.debug("Creating new IronicNode at: " + nodeUrl);
var resource = $resource(nodeUrl, {uuid: '@uuid'}, {
query: {
method: 'GET',
isArray: true,
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response('nodes')
},
create: {
method: 'POST',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
read: {
method: 'GET',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
update: {
method: 'PUT',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
remove: {
method: 'DELETE',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
}
});
$$resourceCache.set(nodeUrl, resource);
}
return $$resourceCache.get(nodeUrl);
}
return {
query: function() {
var r = getResource();
return r.query.apply(r, arguments);
},
create: function() {
var r = getResource();
return r.create.apply(r, arguments);
},
read: function() {
var r = getResource();
return r.read.apply(r, arguments);
},
update: function() {
var r = getResource();
return r.update.apply(r, arguments);
},
remove: function() {
var r = getResource();
return r.remove.apply(r, arguments);
}
};
});

View File

@ -1,93 +0,0 @@
/*
* Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* IronicNodePowerTransition is a mock API that returns the list of valid power
* transitions for nodes.
*/
angular.module('ironic.api').factory('IronicNodePowerTransition',
function($q) {
'use strict';
// Build a dummy resource. The API will return this wrapped in an additional 'transitions'
// field, but we'll just use the raw array.
var transitions = [
{
from_state: "power on",
event: "power off",
target_state: "power off",
actor: "user"
},
{
from_state: "power off",
event: "power on",
target_state: "power on",
actor: "user"
},
{
from_state: "power on",
event: "reboot",
target_state: "rebooting",
actor: "user"
},
{
from_state: "rebooting",
event: "power on",
target_state: "power on",
actor: "conductor"
}
];
return {
query: function(params, successHandler, errorHandler) {
var deferred = $q.defer();
// Build our result array.
var queryResults = [];
queryResults.$promise = deferred.promise;
queryResults.$resolved = false;
deferred.promise.then(function(results) {
angular.forEach(results, function(result) {
queryResults.push(result);
});
});
deferred.promise.finally(function() {
queryResults.$resolved = true;
});
// Check for a filter
if (params) {
var filteredResults = transitions.filter(function(item) {
var approved = true;
angular.forEach(params, function(value, key) {
if (!item.hasOwnProperty(key) || item[key] !== value) {
approved = false;
}
});
return approved;
});
deferred.resolve(filteredResults);
} else {
deferred.resolve(transitions);
}
queryResults.$promise.then(successHandler || null, errorHandler || null);
return queryResults;
}
};
});

View File

@ -1,74 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This resource handles node provision state transitions. It only exposes the $update (PUT) method,
* as that's the only real valid action that can be taken. This service does not validate a
* change which a user/script may want to take.
*/
angular.module('ironic.api').factory('IronicNodeProvision',
function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource,
ironicApiInterceptor, ironicApiVersion) {
'use strict';
/**
* This method extracts the current active API root URI from $$configuration, ensures that
* the appropriate Ironic resources exists in the $$resourceCache, and returns it.
*
* @returns {{}} The created, and cached, IronicPort resource.
*/
function getResource () {
// Pull the current configuration.
var currentConfig = $$selectedConfiguration.get();
// This should resolve the API root, except in cases where the selected configuration is
// invalid and/or has not yet been resolved.
var ironicConfig = currentConfig.ironic || {};
var ironicApiRoot = ironicConfig.apiRoot || null;
if (!ironicApiRoot) {
$log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
return $$dummyResource;
}
// Build and/or retrieve a cached instance of the requested service.
var nodeUrl = ironicApiRoot + '/nodes/:node_ident/states/provision';
if (!$$resourceCache.has(nodeUrl)) {
$log.debug("Creating new IronicNodeProvision at: " + nodeUrl);
var resource = $resource(nodeUrl, {node_ident: '@node_ident'}, {
update: {
method: 'PUT',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
}
});
$$resourceCache.set(nodeUrl, resource);
}
return $$resourceCache.get(nodeUrl);
}
return {
update: function() {
var r = getResource();
return r.update.apply(r, arguments);
}
};
});

View File

@ -1,281 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* IronicNodeProvisionTransition is a mock API that returns the transitions list for
* the ironic state machine. It is intended to be supplanted when the work for
* https://review.openstack.org/#/c/224022/ is completed, though it may continue
* to exist for legacy API microversions.
*/
angular.module('ironic.api').factory('IronicNodeProvisionTransition',
function($q) {
'use strict';
// Build a dummy resource. The API will return this wrapped in an additional 'transitions'
// field, but we'll just use the raw array.
var transitions = [
{
from_state: "active",
event: "rebuild",
target_state: "deploying",
actor: "user"
},
{
from_state: "active",
event: "delete",
target_state: "deleting",
actor: "user"
},
{
from_state: "available",
event: "manage",
target_state: "manageable",
actor: "user"
},
{
from_state: "available",
event: "active",
target_state: "deploying",
actor: "user"
},
{
from_state: "clean failed",
event: "manage",
target_state: "manageable",
actor: "user"
},
{
from_state: "clean wait",
event: "fail",
target_state: "clean failed",
actor: "conductor"
},
{
from_state: "clean wait",
event: "abort",
target_state: "clean failed",
actor: "conductor"
},
{
from_state: "clean wait",
event: "resume",
target_state: "cleaning",
actor: "conductor"
},
{
from_state: "deleting",
event: "clean",
target_state: "cleaning",
actor: "conductor"
},
{
from_state: "deleting",
event: "error",
target_state: "error",
actor: "conductor"
},
{
from_state: "deploy failed",
event: "rebuild",
target_state: "deploying",
actor: "user"
},
{
from_state: "deploy failed",
event: "delete",
target_state: "deleting",
actor: "user"
},
{
from_state: "deploy failed",
event: "active",
target_state: "deploying",
actor: "user"
},
{
from_state: "wait call-back",
event: "delete",
target_state: "deleting",
actor: "conductor"
},
{
from_state: "wait call-back",
event: "resume",
target_state: "deploying",
actor: "conductor"
},
{
from_state: "wait call-back",
event: "fail",
target_state: "deploy failed",
actor: "conductor"
},
{
from_state: "error",
event: "rebuild",
target_state: "deploying",
actor: "user"
},
{
from_state: "error",
event: "delete",
target_state: "deleting",
actor: "user"
},
{
from_state: "enroll",
event: "manage",
target_state: "verifying",
actor: "user"
},
{
from_state: "inspect failed",
event: "inspect",
target_state: "inspecting",
actor: "user"
},
{
from_state: "inspect failed",
event: "manage",
target_state: "manageable",
actor: "user"
},
{
from_state: "manageable",
event: "inspect",
target_state: "inspecting",
actor: "user"
},
{
from_state: "manageable",
event: "provide",
target_state: "cleaning",
actor: "user"
},
{
from_state: "manageable",
event: "clean",
target_state: "cleaning",
actor: "user"
},
{
from_state: "verifying",
event: "fail",
target_state: "enroll",
actor: "conductor"
},
{
from_state: "verifying",
event: "done",
target_state: "manageable",
actor: "conductor"
},
{
from_state: "inspecting",
event: "fail",
target_state: "inspect failed",
actor: "conductor"
},
{
from_state: "inspecting",
event: "done",
target_state: "manageable",
actor: "conductor"
},
{
from_state: "cleaning",
event: "manage",
target_state: "manageable",
actor: "conductor"
},
{
from_state: "cleaning",
event: "wait",
target_state: "clean wait",
actor: "conductor"
},
{
from_state: "cleaning",
event: "fail",
target_state: "clean failed",
actor: "conductor"
},
{
from_state: "cleaning",
event: "done",
target_state: "available",
actor: "conductor"
},
{
from_state: "deploying",
event: "fail",
target_state: "deploy failed",
actor: "conductor"
},
{
from_state: "deploying",
event: "wait",
target_state: "wait call-back",
actor: "conductor"
},
{
from_state: "deploying",
event: "done",
target_state: "active",
actor: "conductor"
}
];
return {
query: function(params, successHandler, errorHandler) {
var deferred = $q.defer();
// Build our result array.
var queryResults = [];
queryResults.$promise = deferred.promise;
queryResults.$resolved = false;
deferred.promise.then(function(results) {
angular.forEach(results, function(result) {
queryResults.push(result);
});
});
deferred.promise.finally(function() {
queryResults.$resolved = true;
});
// Check for a filter
if (params) {
var filteredResults = transitions.filter(function(item) {
var approved = true;
angular.forEach(params, function(value, key) {
if (!item.hasOwnProperty(key) || item[key] !== value) {
approved = false;
}
});
return approved;
});
deferred.resolve(filteredResults);
} else {
deferred.resolve(transitions);
}
queryResults.$promise.then(successHandler || null, errorHandler || null);
return queryResults;
}
};
});

View File

@ -1,120 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* IronicPort is an ngResource abstraction, which switches which Ironic it talks to based on the
* selected cloud configuration provided by the $$configuration service. It may be used as if it
* was an ngResource instance, and supports the methods `query`, `create`, `read`, `update`, and
* `remove`.
*/
angular.module('ironic.api').factory('IronicPort',
function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource,
ironicApiInterceptor, ironicApiVersion) {
'use strict';
/**
* This method extracts the current active API root URI from $$configuration, ensures that
* the appropriate Ironic resources exists in the $$resourceCache, and returns it.
*
* @returns {{}} The created, and cached, IronicPort resource.
*/
function getResource () {
// Pull the current configuration.
var currentConfig = $$selectedConfiguration.get();
// This should resolve the API root, except in cases where the selected configuration is
// invalid and/or has not yet been resolved.
var ironicConfig = currentConfig.ironic || {};
var ironicApiRoot = ironicConfig.apiRoot || null;
if (!ironicApiRoot) {
$log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
return $$dummyResource;
}
// Build and/or retrieve a cached instance of the requested service.
var portUrl = ironicApiRoot + '/ports/:uuid';
if (!$$resourceCache.has(portUrl)) {
$log.debug("Creating new IronicPort at: " + portUrl);
var resource = $resource(portUrl, {uuid: '@uuid'}, {
query: {
method: 'GET',
isArray: true,
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response('ports')
},
create: {
method: 'POST',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
read: {
method: 'GET',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
update: {
method: 'PUT',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
},
remove: {
method: 'DELETE',
headers: {
'X-OpenStack-Ironic-API-Version': ironicApiVersion
},
transformResponse: ironicApiInterceptor.response()
}
});
$$resourceCache.set(portUrl, resource);
}
return $$resourceCache.get(portUrl);
}
return {
query: function() {
var r = getResource();
return r.query.apply(r, arguments);
},
create: function() {
var r = getResource();
return r.create.apply(r, arguments);
},
read: function() {
var r = getResource();
return r.read.apply(r, arguments);
},
update: function() {
var r = getResource();
return r.update.apply(r, arguments);
},
remove: function() {
var r = getResource();
return r.remove.apply(r, arguments);
}
};
});

View File

@ -1,31 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A module of resources that talk with the ironic API.
*/
angular.module('ironic.api', ['openstack', 'ngResource'])
.config(function($$configurationProvider, $windowProvider) {
'use strict';
// Build a default ironic location from the $window provider
var location = $windowProvider.$get().location;
var apiRoot = location.protocol + '//' + location.hostname + ':6385';
// This line registers ironic's default API root, as detected via the current hostname, with
// the $$configurationProvider's default API detection mechanism.
$$configurationProvider.$registerDefault('ironic', apiRoot);
});

View File

@ -1,59 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Form validator that will perform an asynchronous check to see if Ironic is
* available at the provided url. It will expose a variable on the control to
* which it is applied, with an array of detected API versions.
*/
/*eslint-disable angular/no-services */
angular.module('ironic.api').directive('ironicApiUrl',
function ($q, $http) {
/*eslint-enable angular/no-services */
'use strict';
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$ironicVersions = [];
ctrl.$asyncValidators.ironicApiUrl =
function (modelValue) {
var def = $q.defer();
$http.get(modelValue).then(function (result) {
var name = result.data.name;
if (name !== 'OpenStack Ironic API') {
def.reject();
}
var versions = result.data.versions || [];
for (var i = 0; i < versions.length; i++) {
versions[i] = versions[i].id;
}
ctrl.$ironicVersions = versions;
def.resolve();
}, function () {
def.reject();
});
return def.promise;
};
}
};
});

View File

@ -1,200 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A generic controller which permits a user to issue provision state actions against a list of
* nodes.
*/
angular.module('ironic').controller('ProvisionActionModalController',
function($q, $uibModalInstance, nodeIds, actionName, IronicNode, IronicNodeProvisionTransition,
IronicNodeProvision) {
'use strict';
var vm = this;
vm.someUpdated = false;
vm.updating = false;
vm.someNodesExcluded = false;
vm.actionName = actionName;
// Setup our nodes.
vm.nodes = [];
vm.nodes.$resolved = false;
// This is the data preloader, which drives all of our modal data setup. This logic will
// execute in sequence.
vm.nodes.$promise = $q
.all({
nodes: loadNodes(),
states: loadValidStates()
})
.then(filterNodes)
.then(contextWrap)
.then(applyNodesToContext)
.then(toggleExclusionFlag)
.finally(function() {
vm.nodes.$resolved = true;
});
/**
* Resolve all states for which the current action is valid.
*
* @return {Promise} A promise that resolves with all valid states.
*/
function loadValidStates () {
return IronicNodeProvisionTransition.query().$promise
.then(function(transitions) {
return transitions
.filter(function(transition) {
return transition.event === actionName;
})
.map(function(transition) {
return transition.from_state;
});
});
}
/**
* Resolve the node details for all the passed node ID's.
*
* @return {Promise} A promise that resolves with all nodes details.
*/
function loadNodes () {
var promises = [];
angular.forEach(nodeIds, function(nodeId) {
promises.push(IronicNode.read({uuid: nodeId}).$promise);
});
return $q.all(promises);
}
/**
* Filter the nodes into a valid set.
*
* @param {{}} results Results from preloading the valid states and valid nodes.
* @return {[]} A list of nodes that may be manipulated with this action.
*/
function filterNodes (results) {
var nodes = results.nodes;
var states = results.states;
return nodes.filter(function(node) {
return states.indexOf(node.provision_state) > -1;
});
}
/**
* Wrap each node in a context object, used to track state.
*
* @param {[]} nodes A list of nodes.
* @return {[]} A list of context wrapped nodes.
*/
function contextWrap (nodes) {
return nodes.map(function(node) {
return {
node: node,
error: null,
state: 'ready', // ready, busy, error, complete
changed: false
};
});
}
/**
* Apply the nodes to the controller's variables, in a non-destructive fashion.
*
* @param {[]} nodes The list of nodes to apply.
* @return {void}
*/
function applyNodesToContext (nodes) {
return angular.forEach(nodes, function(node) {
vm.nodes.push(node);
return node;
});
}
/**
* Toggle the flag on the controller that indicates that some of the selected nodes are not
* in a valid state for this action.
*
* @param {[]} loadedNodes A list of loaded nodes.
* @return {[]} That same list of loaded nodes, with no modifications.
*/
function toggleExclusionFlag (loadedNodes) {
vm.someNodesExcluded = loadedNodes.length !== nodeIds.length;
return loadedNodes;
}
/**
* Apply the action for which this controller has been configured.
*
* @return {void}
*/
vm.apply = function() {
vm.updating = true;
var promises = [];
// For each context object in our controller...
angular.forEach(vm.nodes, function(ctx) {
// Try to issue the request.
ctx.state = 'updating';
var state = IronicNodeProvision.update({
node_ident: ctx.node.uuid,
target: actionName
}, function() {
vm.someUpdated = true;
ctx.changed = true;
ctx.state = 'complete';
}, function(error) {
ctx.state = 'error';
ctx.error = error.data.error_message;
});
promises.push(state.$promise);
});
// Wait for all the promises...
$q.all(promises).then(
function() {
// If all are successful, just close.
vm.close();
})
.finally(function() {
// Flip the updating bit.
vm.updating = false;
});
};
/**
* Close this modal.
*
* @return {void}
*/
vm.close = function() {
if (vm.someUpdated) {
// Something was changed, collect the changed nodes and return them.
var changedNodeIds = [];
angular.forEach(vm.nodes, function(ctx) {
if (ctx.changed) {
changedNodeIds.push(ctx.node.uuid);
}
});
$uibModalInstance.close(changedNodeIds);
} else {
// Nothing was changed.
$uibModalInstance.dismiss();
}
};
});

View File

@ -1,94 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This controller backs the "Remove Node" modal.
*/
angular.module('ironic').controller('RemoveNodeModalController',
function($uibModalInstance, nodes, $q) {
'use strict';
var vm = this;
// Wrap all the nodes in their own context objects.
vm.someDeleted = false;
vm.deleting = false;
vm.nodes = [];
angular.forEach(nodes, function(node) {
vm.nodes.push({
node: node,
error: null,
state: 'present'
});
});
/**
* Remove all the nodes on this controller.
*
* @returns {void}
*/
vm.remove = function() {
vm.deleting = true;
var promises = [];
// For each context object in our controller...
angular.forEach(vm.nodes, function(ctx) {
// Try to delete it.
ctx.state = 'removing';
var promise = ctx.node.$remove(
function() {
vm.someDeleted = true;
ctx.state = 'removed';
},
function(error) {
ctx.state = 'error';
ctx.error = error.data.error_message;
}
);
promises.push(promise);
});
// Wait for all the promises...
$q.all(promises).then(
function() {
// If all are successful, just close.
vm.deleting = false;
if (vm.someDeleted) {
$uibModalInstance.close();
} else {
$uibModalInstance.dismiss();
}
},
function() {
// Flip the deleting bit.
vm.deleting = false;
});
};
/**
* Close this modal.
*
* @returns {void}
*/
vm.close = function() {
if (vm.someDeleted) {
// Something was changed.
$uibModalInstance.close();
} else {
// Nothing was changed.
$uibModalInstance.dismiss();
}
};
});

View File

@ -1,30 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This controller handles the popup that informs the user that we either do not know, or cannot
* perform, the node action they've requested.
*/
angular.module('ironic').controller('UnknownActionModalController',
function($scope, $uibModalInstance, actionName) {
'use strict';
var vm = this;
vm.actionName = actionName;
vm.close = function() {
$uibModalInstance.dismiss();
};
});

View File

@ -1,47 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This controller allows the creation of a new configuration.
*/
angular.module('ironic').controller('ConfigurationAddController',
function($scope, $state, $location, $$configuration, $uibModalInstance, configuration) {
'use strict';
var vm = this;
vm.configuration = configuration;
vm.newConfiguration = {
name: '',
ironic: {
apiRoot: ''
}
};
vm.location = {
host: $location.host(),
protocol: $location.protocol(),
port: $location.port()
};
vm.save = function() {
vm.newConfiguration.id = vm.newConfiguration.name;
$uibModalInstance.close(vm.newConfiguration);
};
vm.close = function() {
$uibModalInstance.dismiss();
};
});

View File

@ -1,88 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This controller allows the management of all the cloud configuration entries.
*/
angular.module('ironic').controller('ConfigurationController',
function($state, $location, $$configuration, $$selectedConfiguration, $uibModal) {
'use strict';
var vm = this;
vm.configurations = [];
vm.location = {
host: $location.host(),
protocol: $location.protocol(),
port: $location.port()
};
/**
* Reload all configurations in this controller.
*
* @returns {void}
*/
function reloadConfigurations () {
vm.configurations = $$configuration.query({});
}
/**
* Select a single configuration for the current application runtime.
*
* @param {{}} configuration The configuration to select.
* @returns {void}
*/
vm.select = function(configuration) {
$$selectedConfiguration.set(configuration).$promise.then(
function() {
$state.go('root.ironic.nodes');
}
);
};
/**
* Displays the local configuration add modal.
*
* @returns {void}
*/
vm.add = function() {
// var deferred = $q.defer();
$uibModal.open({
templateUrl: 'view/ironic/config_add.html',
controller: 'ConfigurationAddController as ctrl',
backdrop: 'static',
resolve: {
configuration: function() {
return $$configuration.query({}).$promise;
}
}
}).result.then(
function(newConfig) {
$$configuration.create(newConfig).$promise.then(reloadConfigurations);
});
};
/**
* Remove a configuration.
* @param {{}} config The configuration to remove.
* @returns {void}
*/
vm.remove = function(config) {
config.$remove().$promise.then(reloadConfigurations);
};
reloadConfigurations();
});

View File

@ -1,54 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Driver List for the Ironic UI.
*/
angular.module('ironic')
.controller('DriverListController', function($scope, $q, IronicDriver) {
'use strict';
var vm = this;
// Set up controller parameters
vm.errorMessage = null;
vm.drivers = [];
/**
* Initialize the controller.
*
* @returns {void}
*/
vm.init = function() {
vm.loadDrivers();
};
/**
* Load the entire list of drivers.
*
* @returns {void}
*/
vm.loadDrivers = function() {
vm.errorMessage = null;
vm.drivers = IronicDriver.query({}, function() {
// Do nothing.
}, function(error) {
vm.errorMessage = error.data.error_message;
vm.drivers = null;
});
};
vm.init();
});

View File

@ -1,82 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This controller handles the enrollment of a new, single, node
*/
angular.module('ironic').controller('EnrollModalController',
function($scope, $uibModalInstance, IronicDriver, IronicDriverProperties,
IronicNode) {
'use strict';
var vm = this;
vm.drivers = null;
vm.errorMessage = null;
vm.driverProperties = null;
vm.node = null;
vm.loadDrivers = function() {
vm.errorMessage = null;
vm.drivers = IronicDriver.query({}, function() {
// Do nothing on success.
}, function(error) {
vm.errorMessage = error.data.error_message;
vm.drivers = null;
});
};
// Given a driver name, retrieve all the driver's options.
vm.loadDriverProperties = function(driverName) {
vm.driverProperties = null;
vm.errorMessage = null;
vm.node = {
name: vm.node ? vm.node.name : '', // Preserve previously entered names.
driver: driverName,
driver_info: {}
};
var params = {
driver_name: driverName
};
IronicDriverProperties.read(params).$promise.then(
function(properties) {
vm.driverProperties = properties;
},
function(error) {
vm.errorMessage = error.data.error_message;
});
};
vm.enroll = function() {
vm.errorMessage = null;
IronicNode.create(vm.node,
function(node) {
$uibModalInstance.close(node);
},
function(error) {
vm.errorMessage = error.data.error_message;
}
);
};
vm.close = function() {
$uibModalInstance.dismiss();
};
// Initialize the drivers.
vm.loadDrivers();
});

View File

@ -1,34 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Controller for our application header.
*/
angular.module('ironic').controller('HeaderController',
function ($scope, $state, configurations, currentConfiguration, $$selectedConfiguration) {
'use strict';
var vm = this;
vm.configurations = configurations;
vm.currentConfiguration = currentConfiguration;
vm.switchCloud = function (cloudConfig) {
$$selectedConfiguration.set(cloudConfig).$promise.then(
function() {
$state.reload();
});
};
});

View File

@ -1,124 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Controller which handles manipulation of individual nodes.
*/
angular.module('ironic').controller('NodeActionController',
function($uibModal) {
'use strict';
var vm = this;
// Set up controller parameters
vm.errorMessage = null;
/**
* Generic unknown action handler, here as a placeholder until we get actual actions built out.
*
* @param {String} actionName The name of the action to perform.
* @param {[{}]} nodes An array of nodes on which to perform this action.
* @returns {Promise} A promise that resolves when the user performs the action.
*/
function unknownActionHandler (actionName, nodes) {
return $uibModal.open({
templateUrl: 'view/ironic/action/unknown.html',
controller: 'UnknownActionModalController as ctrl',
resolve: {
actionName: function() {
return actionName;
},
nodes: function() {
return nodes;
}
}
}).result;
}
vm.powerAction = unknownActionHandler;
/**
* Provision action handler, delegates the provision action to the modal controller.
*
* @param {String} actionName The name of the action to perform.
* @param {[{}]} nodes An array of nodes on which to perform this action.
* @returns {Promise} A promise that resolves when the user performs the action.
*/
vm.provisionAction = function provisionActionHandler (actionName, nodes) {
var modalParams = {
templateUrl: 'view/ironic/action/provision.html',
controller: 'ProvisionActionModalController as ctrl',
resolve: {
actionName: function() {
return actionName;
},
nodeIds: function() {
return nodes;
}
}
};
switch (actionName) {
case 'manage':
case 'rebuild':
case 'delete':
case 'deploy':
case 'fail':
case 'abort':
case 'clean':
case 'inspect':
case 'provide':
default:
modalParams.templateUrl = 'view/ironic/action/provision.html';
}
return $uibModal.open(modalParams).result;
};
/**
* Delete the node in this controller.
*
* @param {IronicNode} node The node instance to remove.
* @return {Promise} A promise that will resolve true if the modal closed with some deletions.
*/
vm.remove = function(node) {
// Return the result of the modal.
return $uibModal.open({
templateUrl: 'view/ironic/action/remove_node.html',
controller: 'RemoveNodeModalController as ctrl',
backdrop: 'static',
resolve: {
nodes: function() {
return [node];
}
}
}).result;
};
/**
* Enroll a new node.
*
* @return {Promise} A promise that will resolve true if a node has been added.
*/
vm.enroll = function() {
return $uibModal.open({
templateUrl: 'view/ironic/enroll/index.html',
controller: 'EnrollModalController as ctrl',
backdrop: 'static'
}).result;
};
});

View File

@ -1,37 +0,0 @@
/*
* Copyright (c) 2015 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Node handling for the Ironic UI.
*/
angular.module('ironic').controller('NodeDetailController',
function(nodeUuid, IronicNode) {
'use strict';
var vm = this;
// Set up controller parameters
vm.errorMessage = null;
vm.node = null;
// Load the node.
vm.node = IronicNode.read({
uuid: nodeUuid
}, angular.noop, function(error) {
// Set the error message and clear the node promise.
vm.errorMessage = error.data.error_message;
vm.node = null;
});
});

View File

@ -1,45 +0,0 @@
/*
* Copyright (c) 2015 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Node driver handling for the Ironic UI.
*/
angular.module('ironic').controller('NodeDetailDriverController',
function(nodeUuid, IronicNode, IronicDriver) {
'use strict';
var vm = this;
// Set up controller parameters
vm.errorMessage = null;
vm.driver = null;
// Generic, shared error handler.
function errorHandler (error) {
// Set the error message and clear the port promise.
vm.errorMessage = error.data.error_message;
vm.driver = null;
}
// Load the node.
IronicNode.read({
uuid: nodeUuid
}, function(node) {
vm.driver = IronicDriver.read({
uuid: node.driver
}, angular.noop, errorHandler);
}, errorHandler);
});

View File

@ -1,37 +0,0 @@
/*
* Copyright (c) 2015 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Node port handling for the Ironic UI.
*/
angular.module('ironic').controller('NodeDetailPortsController',
function(nodeUuid, IronicPort) {
'use strict';
var vm = this;
// Set up controller parameters
vm.errorMessage = null;
vm.ports = null;
// Load the node.
vm.ports = IronicPort.query({
node: nodeUuid
}, angular.noop, function(error) {
// Set the error message and clear the node promise.
vm.errorMessage = error.data.error_message;
vm.ports = null;
});
});

View File

@ -1,152 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Node handling for the Ironic UI.
*/
angular.module('ironic')
.controller('NodeListController', function($scope, $q, IronicNode, IronicNodeProvisionTransition,
IronicNodePowerTransition) {
'use strict';
var vm = this;
// Set up controller parameters
vm.errorMessage = null;
vm.nodes = [];
vm.selectedNodes = [];
vm.selectAll = false;
vm.provisionTransitions = [];
vm.powerTransitions = [];
/**
* Initialize the controller.
*
* @returns {void}
*/
vm.init = function() {
vm.loadProvisionTransitions();
vm.loadPowerTransitions();
vm.loadNodes();
};
// Load the node list.
vm.loadNodes = function() {
vm.errorMessage = null;
vm.nodes = IronicNode.query({}, function() {
vm.selectedNodes = [];
vm.selectAll = false;
}, function(error) {
vm.errorMessage = error.data.error_message;
vm.selectedNodes = [];
vm.selectAll = false;
vm.nodes = null;
});
};
/**
* Check the selected nodes anytime we suspect that the selectAll property may no longer be
* valid.
*/
// using $watchCollection only works on non-scope items if the property provided is a
// generator. Otherwise this syntax would have to be $scope.$watchCollection('foo'), and
// we do not permit polluting the scope.
var unwatchSelectedNodes = $scope.$watchCollection(function() {
return vm.selectedNodes;
}, function(newValue) {
vm.selectAll = newValue.length === vm.nodes.length;
});
$scope.$on('$destroy', unwatchSelectedNodes);
/**
* Select, or deselect, all nodes based on the value of the checkbox.
*
* @param {Boolean} selectAll Whether to select all.
* @returns {void}
*/
vm.toggleSelectAll = function(selectAll) {
if (selectAll) {
vm.selectedNodes = vm.nodes.map(function(item) {
return angular.copy(item.uuid);
});
} else {
vm.selectedNodes = [];
}
};
/**
* Load all valid user power transition names into the controller.
*
* @returns {void}
*/
vm.loadPowerTransitions = function() {
var transitionPromise = IronicNodePowerTransition.query({actor: 'user'}).$promise;
vm.powerTransitions = [];
vm.powerTransitions.$resolved = false;
vm.powerTransitions.$promise = transitionPromise
.then(mapTransitionNames)
.then(function(names) {
angular.forEach(names, function(name) {
vm.powerTransitions.push(name);
});
vm.powerTransitions.$resolved = true;
});
};
/**
* Load all valid user provisioning transitions names into the controller.
*
* @returns {void}
*/
vm.loadProvisionTransitions = function() {
var transitionPromise = IronicNodeProvisionTransition.query({actor: 'user'}).$promise;
vm.provisionTransitions = [];
vm.provisionTransitions.$resolved = false;
vm.provisionTransitions.$promise = transitionPromise
.then(mapTransitionNames)
.then(function(names) {
angular.forEach(names, function(name) {
vm.provisionTransitions.push(name);
});
vm.provisionTransitions.$resolved = true;
});
};
/**
* This helper method reduces the transition data format down to the actual action name that
* can be performed on a node.
*
* @param {[{}]} transitions The list of resolved transitions.
* @returns {Array} The reduced list of available transitions.
*/
function mapTransitionNames (transitions) {
var reducedTransitions = [];
transitions = transitions.map(function(item) {
return item.event;
});
// deduplicate.
angular.forEach(transitions, function(name) {
if (reducedTransitions.indexOf(name) === -1) {
reducedTransitions.push(name);
}
});
return reducedTransitions;
}
vm.init();
});

View File

@ -1,147 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* The Ironic Root Application.
*
* This module defines dependencies and root routes, but no actual
* functionality.
*/
angular.module('ironic', ['ui.router', 'ui.bootstrap', 'ironic.util', 'ironic.api',
'checklist-model'])
.config(function($urlRouterProvider, $httpProvider, $stateProvider, $$configurationProvider) {
'use strict';
// Default UI route
$urlRouterProvider.otherwise('/ironic/nodes');
// Enable all of our configuration detection methods
$$configurationProvider.$enableDefault(true);
$$configurationProvider.$enableConfigLoad(true);
$$configurationProvider.$enableLocalStorage(true);
// Ironic's root state, used to resolve global resources before
// the application fully initializes.
$stateProvider
.state('root', {
abstract: true,
url: '',
templateUrl: 'view/ironic/root.html'
})
.state('root.ironic', {
abstract: true,
url: '/ironic',
views: {
header: {
templateUrl: 'view/ironic/header.html',
controller: 'HeaderController as headerCtrl'
},
root: {
templateUrl: 'view/ironic/ironic.html'
}
},
resolve: {
configurations: function($$configuration) {
return $$configuration.query({}).$promise;
},
currentConfiguration: function($$selectedConfiguration, $q) {
var deferred = $q.defer();
var resource = $$selectedConfiguration.get();
resource.$promise.then(
function() {
deferred.resolve(resource);
},
function() {
deferred.reject('no_config');
});
return deferred.promise;
}
}
})
.state('root.ironic.nodes', {
url: '/nodes',
views: {
'main@root.ironic': {
templateUrl: 'view/ironic/node_list.html',
controller: 'NodeListController as nodeListCtrl'
}
}
})
.state('root.ironic.nodes.detail', {
abstract: true,
url: '/:uuid',
resolve: {
nodeUuid: function($stateParams) {
return $stateParams.uuid;
}
},
views: {
'main@root.ironic': {
templateUrl: 'view/ironic/detail.html',
controller: 'NodeDetailController as nodeCtrl'
}
}
})
.state('root.ironic.nodes.detail.node', {
url: '/node',
templateUrl: 'view/ironic/detail_node.html'
})
.state('root.ironic.nodes.detail.ports', {
url: '/ports',
templateUrl: 'view/ironic/detail_ports.html',
controller: 'NodeDetailPortsController as portCtrl'
})
.state('root.ironic.nodes.detail.driver', {
url: '/driver',
templateUrl: 'view/ironic/detail_driver.html',
controller: 'NodeDetailDriverController as driverCtrl'
})
.state('root.ironic.drivers', {
url: '/drivers',
views: {
'main@root.ironic': {
controller: 'DriverListController as driverListCtrl',
templateUrl: 'view/ironic/driver_list.html'
}
}
})
.state('root.config', {
url: '/config',
views: {
header: {
templateUrl: 'view/ironic/config_header.html'
},
root: {
templateUrl: 'view/ironic/config.html',
controller: 'ConfigurationController as ctrl'
}
}
});
})
.run(function($rootScope, $state) {
'use strict';
var listener = $rootScope.$on('$stateChangeError',
function(evt, toState, toParams, fromState, fromParams, reason) {
if (reason === 'no_config') {
$state.go('root.config');
} else {
$state.go('root.ironic.nodes');
}
});
$rootScope.$on('$destroy', listener);
});

View File

@ -1,641 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This resource acts as a resource-like service that provides cloud api configurations
* from various optional sources. First, you may hardcode a configuration using
* $$configurationProvider.$addConfig. Secondly, you may use the default configuration
* as registered by peer libraries using $$configurationProvider.$registerDefault.
* Thirdly, it can load a configuration from ./config.json. Lastly, it provides
* a simple CRUD interface by which a user may manipulate configurations that
* are persisted in a browser's persistent storage.
*
* An example configuration file follows:
*
* [{
* "name": "My Little Cloud",
* "ironic": {
* "apiRoot": "https://ironic.example.com:6385/"
* },
* "glance": {
* "apiRoot": "https://glance.example.com:9292/"
* }
* }]
*/
angular.module('openstack').provider('$$configuration',
function() {
'use strict';
var merge = angular.merge;
var forEach = angular.forEach;
/**
* Promise variables, used to keep certain result sets in state.
*/
var deferAll, deferConfig, deferStatic, deferDefault, deferLocal;
/**
* ID key for the persistent storage.
*
* @type {string}
*/
var storageKey = '$$configuration';
/**
* Internal flag: Is the default configuration enabled?
*
* @type {boolean} True if enabled, otherwise false.
*/
var enableDefault = false;
/**
* Internal flag: Is configuration loading enabled?
*
* @type {boolean} True if enabled, otherwise false.
*/
var enableConfigLoad = false;
/**
* Internal flag: Is persisting via localStorage enabled?
*
* @type {boolean} True if enabled, otherwise false.
*/
var enableLocalStorage = false;
/**
* A list of static configurations, added to the provider.
*
* @type {Array}
*/
var staticConfigs = [];
/**
* The default configuration instance.
*
* @type {{}}
*/
var defaultConfig = {
id: 'default',
name: 'Default Cloud',
source: 'default'
};
/**
* Add a new configuration to the provider. Use this method if you are providing your
* own configuration loading mechanism during application initialization, such as a
* hardcoded configuration. For a user-provided configuration, we recommend you use
* $$configuration.create({}); instead.
*
* @param {*} configuration The configuration to add.
* @returns {void}
*/
this.$addConfig = function(configuration) {
configuration = angular.copy(configuration);
configuration.source = 'static';
staticConfigs.push(configuration);
// Reset associated promises
if (deferStatic) {
deferStatic = null;
deferAll = null;
}
};
/**
* Register a service's default API URL. This allows peer libraries, such as glance-apilib
* or ironic-apilib, to set a 'default' location for their API.
*
* @param {String} serviceName The name of the service to register. Example: 'ironic'.
* @param {String} serviceUrl The root url of the API.
* @returns {void}
*/
this.$registerDefault = function(serviceName, serviceUrl) {
defaultConfig[serviceName] = {
id: serviceName,
apiRoot: serviceUrl
};
if (deferDefault) {
deferDefault = null;
deferAll = null;
}
};
/**
* Enable, or disable, the default configuration mechanism. This mechanism assumes that peer
* libraries, such as ironic-jslib or others, register an expected 'default' API root during
* application initialization. These are provided under the 'default' id.
*
* This feature is disabled by default, enable it if you want your UI to permit automatic
* creation of default configurations.
*
* @param {Boolean} enable Whether to enable the default configuration mechanism.
* @returns {void}
*/
this.$enableDefault = function(enable) {
enable = !!enable;
// Only change things if things have changed
if (enable !== enableDefault) {
enableDefault = enable;
// Reset associated promises
if (deferDefault) {
deferDefault = null;
deferAll = null;
}
}
};
/**
* Enable, or disable, the configuration loading mechanism. This mechanism attempts to load
* a list of cloud configurations from a ./config.json file that lives adjacent to your user
* interface. This feature is disabled by default, enable it if you want to configure your
* cloud with a static config.json file.
*
* @param {Boolean} enable Whether to enable configuration loading.
* @returns {void}
*/
this.$enableConfigLoad = function(enable) {
enable = !!enable;
// Only change things if things have changed
if (enable !== enableConfigLoad) {
enableConfigLoad = enable;
// Reset associated promises
if (deferConfig) {
deferConfig = null;
deferAll = null;
}
}
};
/**
* Enable, or disable, loading cloud configuration from a browser's LocalStorage. This may
* be used either to drive a user-provided configuration UI, or to read a configuration that
* is shared between applications on the same domain.
*
* @param {Boolean} enable Whether to enable localStorage configurations.
* @returns {void}
*/
this.$enableLocalStorage = function(enable) {
enable = !!enable;
// Only change things if things have changed
if (enable !== enableLocalStorage) {
enableLocalStorage = enable;
// Reset associated promises
if (deferLocal) {
deferLocal = null;
deferAll = null;
}
}
};
/**
* Create a shallow copy of an object without persisting private methods.
*
* @param {*} src The source object.
* @returns {*|{}} A copy of the object.
*/
function cleanCopy (src) {
var dst = {};
for (var key in src) {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$')) {
dst[key] = src[key];
}
}
return dst;
}
this.$get = function($q, $$persistentStorage, $log, $http) {
/**
* Store a list of data in local persistent storage.
*
* @param {*} list An array of config objects.
* @returns {void}
*/
function saveLocal (list) {
$$persistentStorage.set(storageKey, list);
deferLocal = null;
deferAll = null;
}
/**
* Retrieve all configurations from the browser local storage, if enabled.
*
* @returns {promise} A resolving promise for local configuration objects.
*/
function resolveLocal () {
if (!deferLocal) {
deferLocal = $q.defer();
if (enableLocalStorage) {
var configs = $$persistentStorage.get(storageKey) || [];
forEach(configs, function(config) {
config.source = 'local';
});
deferLocal.resolve(configs);
} else {
deferLocal.resolve([]);
}
}
return deferLocal.promise;
}
/**
* Retrieve all configurations from our various storage mechanisms.
*
* @returns {promise} A resolving promise for all configuration objects.
*/
function resolveAll () {
// Only trigger this if the promise has been cleared.
if (!deferAll) {
deferAll = $q.defer();
$q.all({
local: resolveLocal(),
config: resolveConfig(),
default: resolveDefault(),
static: resolveStatic()
}).then(function(results) {
var list = [];
forEach(results.local, function(config) {
list.push(config);
});
forEach(results.config, function(config) {
list.push(config);
});
forEach(results.default, function(config) {
list.push(config);
});
forEach(results.static, function(config) {
list.push(config);
});
deferAll.resolve(list);
});
}
return deferAll.promise;
}
/**
* Resolve configuration files from the ./config.json file.
*
* @returns {promise} A resolving promise for configFile configuration objects.
*/
function resolveConfig () {
if (!deferConfig) {
deferConfig = $q.defer();
if (!enableConfigLoad) {
deferConfig.resolve([]);
} else {
$log.info('Attempting to load parameters from ./config.json');
$http.get('./config.json').then(
function(response) {
// The result must be an array.
if (!angular.isArray(response.data)) {
$log.warn('Content of ./config.json must be a valid JSON array.');
response.data = [];
}
forEach(response.data, function(config) {
config.source = 'host';
});
deferConfig.resolve(response.data);
},
function() {
$log.warn('Cannot load ./config.json, using defaults.');
deferConfig.resolve([]);
}
);
}
}
return deferConfig.promise;
}
/**
* Resolve the default configuration.
*
* @returns {promise} A resolving promise for default API calls.
*/
function resolveDefault () {
if (!deferDefault) {
deferDefault = $q.defer();
if (enableDefault) {
deferDefault.resolve([defaultConfig]);
} else {
deferDefault.resolve([]);
}
}
return deferDefault.promise;
}
/**
* Resolve a list of configurations added to the services at config time.
*
* @returns {promise} A resolving promise of static configuration objects.
*/
function resolveStatic () {
if (!deferStatic) {
deferStatic = $q.defer();
deferStatic.resolve(staticConfigs);
}
return deferStatic.promise;
}
/**
* Create a new local configuration. This requires that localStorage is enabled.
*
* @param {{}} newConfig The configuration to create.
* @returns {{}} A resource for this new configuration object.
*/
function createConfig (newConfig) {
// Create a result object
var deferred = $q.defer();
// Decorate the result resource with necessary bits.
newConfig = resourceify(newConfig, deferred.promise);
// We must have local storage enabled.
if (!enableLocalStorage) {
deferred.reject('local_storage_disabled');
return newConfig;
}
// The user must provide an id property.
if (!newConfig.id) {
deferred.reject('no_id_provided');
return newConfig;
}
// Resolve both all and local.
$q.all({
all: resolveAll(),
local: resolveLocal()
}).then(function(results) {
// Check for duplicate ID's, reject if one exists.
for (var i = 0; i < results.all.length; i++) {
if (newConfig.id === results.all[i].id) {
deferred.reject('duplicate_id');
return;
}
}
// Add the new config.
results.local.push(cleanCopy(newConfig));
// Stash the results
saveLocal(results.local);
// resolve and exit
deferred.resolve(newConfig);
});
return newConfig;
}
/**
* Update a new locally stored configuration.
*
* @param {{}} config The configuration object to update.
* @returns {{}} The updated configuration.
*/
function updateConfig (config) {
var deferred = $q.defer();
config = resourceify(config, deferred.promise);
// We must have local storage enabled.
if (!enableLocalStorage) {
deferred.reject('local_storage_disabled');
return config;
}
// Load local configurations
resolveLocal().then(
function(results) {
// Try to find the matching id.
for (var i = 0; i < results.length; i++) {
var storedConfig = results[i];
if (config.id === storedConfig.id) {
// Merge the new values onto the stored config.
storedConfig = merge(storedConfig, cleanCopy(config));
// Stash the results
saveLocal(results);
// Resolve the promise
deferred.resolve(config);
return;
}
}
deferred.reject('not_found');
});
return config;
}
/**
* Retrieve a configuration by ID.
*
* @param {{}} config The configuration object to resolve.
* @returns {{}} The configuration resource.
*/
function readConfig (config) {
var deferred = $q.defer();
config = resourceify(config, deferred.promise);
// Force the user to provide an ID.
if (!config.id) {
deferred.reject('no_id_provided');
return config;
}
// Load everything, then...
resolveAll().then(
function(results) {
// Try to find the matching id.
for (var i = 0; i < results.length; i++) {
var storedConfig = results[i];
if (config.id === storedConfig.id) {
// Merge values onto the config.
merge(config, cleanCopy(storedConfig));
deferred.resolve(config);
return;
}
}
deferred.reject('not_found');
});
return config;
}
/**
* Remove a locally stored configuration from the cache.
*
* @param {{}} config The configuration object to remove.
* @returns {{}} The configuration object, with appropriate promises.
*/
function removeConfig (config) {
var deferred = $q.defer();
config = resourceify(config, deferred.promise);
// We must have local storage enabled.
if (!enableLocalStorage) {
deferred.reject('local_storage_disabled');
return config;
}
// Load local configurations
resolveLocal().then(
function(results) {
// Try to find the matching id.
for (var i = 0; i < results.length; i++) {
var storedConfig = results[i];
if (storedConfig.id === config.id) {
// Remove the config from local storage.
results.splice(i, 1);
// Stash the results
saveLocal(results);
// Resolve the promise and exit
deferred.resolve(config);
return;
}
}
deferred.reject('not_found');
});
return config;
}
/**
* This method decorates a raw resource with manipulation methods like $delete, $update,
* etc. These convenience methods permit individual instance manipulation.
*
* @param {{}} instance The instance to decorate.
* @param {Promise} promise The promise to apply.
*
* @returns {{}} A clone of the instance, with $ methods added.
*/
function resourceify (instance, promise) {
instance.$promise = promise;
// Set the initial resolved, and keep it up to date.
instance.$resolved = false;
instance.$promise.then(function() {
instance.$resolved = true;
}, function() {
instance.$resolved = true;
});
instance.$remove = function() {
return removeConfig(instance);
};
instance.$create = function() {
return createConfig(instance);
};
instance.$update = function() {
return updateConfig(instance);
};
instance.$read = function() {
return readConfig(instance);
};
return instance;
}
return {
/**
* Get a list of all configurations.
*
* @returns {{}} A list of configurations.
*/
query: function() {
// Start with the promise
var listDeferred = $q.defer();
// Build a resource
var list = resourceify([], listDeferred.promise);
// Resolve all the resources, then resolve the list promise.
resolveAll().then(
function(results) {
// Load cloned configs into the result array.
forEach(results, function(config) {
var rDeferred = $q.defer();
var resource = resourceify(cleanCopy(config), rDeferred.promise);
listDeferred.promise.then(
function() {
rDeferred.resolve(resource);
});
list.push(resource);
});
listDeferred.resolve(list);
});
return list;
},
/**
* Create a new configuration.
*
* @param {{}} configuration The configuration to add.
* @returns {{}} A resource for this new configuration object.
*/
create: function(configuration) {
return createConfig(configuration);
},
/**
* Retrieve a specific configuration by ID.
*
* @param {String} id The configuration ID/Name to load.
* @returns {{}} The configuration resource.
*/
read: function(id) {
return readConfig({id: id});
},
/**
* Update a configuration in the loaded cache. This will error if the user attempts to
* update a configuration from a static provider - say the config file.
*
* @param {{}} configuration The configuration object to update.
* @returns {{}} The updated configuration.
*/
update: function(configuration) {
return updateConfig(configuration);
},
/**
* Remove a particular configuration from the configuration list.
*
* @param {String} id The ID to remove.
* @returns {{}} The configuration object, with appropriate promises.
*/
remove: function(id) {
return removeConfig({id: id});
}
};
};
}
);

View File

@ -1,61 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* The $$dummyResource component is a placeholder, which simulates the ngResource API. It does
* nothing on its own, and should only be used in situations where a more appropriate resource
* could not be resolved.
*/
angular.module('openstack').factory('$$dummyResource',
function($q) {
'use strict';
/**
* Apply common resource-like behavior, and instantly reject the promise.
*
* @param {*} resource An object to decorate.
* @returns {*} The decorated instance.
*/
function resourceify (resource) {
var deferred = $q.defer();
resource.$promise = deferred.promise;
resource.$resolved = false;
resource.$promise.finally(function() {
resource.$resolved = true;
});
deferred.reject('dummy_resource');
return resource;
}
return {
query: function() {
return resourceify([]);
},
create: function() {
return resourceify({});
},
read: function() {
return resourceify({});
},
update: function() {
return resourceify({});
},
remove: function() {
return resourceify({});
}
};
});

View File

@ -1,22 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* An application-wide static constant that contains error code constants.
* Different modules and components may dynamically add error codes that serve
* their own needs.
*/
angular.module('openstack').constant('$$errorCode', {});

View File

@ -1,20 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* OpenStack specific configuration discovery.
*/
angular.module('openstack', ['ngCookies', 'ngResource']);

View File

@ -1,123 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* The resource cache acts as a centralized location where different services' resource
* instances may be stored. It plays a central role in multi-cloud environments, as a resource
* abstraction - say IronicNode - can create and cache the actual ngResource instances for N>1
* configured ironic endpoints.
*
* In order to maintain flexibility, it does not build resources, it only accepts preconstructed
* instances. It is strongly recommended that you store your resource instances using the root
* URI of the API to which your instance is talking, so that 'https://somecloud.com:6385/v1/nodes'
* will be stored separately from 'https://someothercloud.com:6385/v1/nodes'.
*/
angular.module('openstack').factory('$$resourceCache',
function() {
'use strict';
/**
* Cache of all the resource instances.
*
* @type {{}}
*/
var resourceCache = {};
return {
/**
* Store a resource instance in the cache.
*
* @param {String} uri The root uri of the resource.
* @param {*} resource The resource to store.
* @return {*} The stored resource.
*/
set: function(uri, resource) {
resourceCache[uri] = resource;
return resource;
},
/**
* Retrieve a resource instance from the cache.
*
* @param {String} uri The uri of the resource to retrieve.
* @return {*|undefined} The resource, or undefined.
*/
get: function(uri) {
if (resourceCache[uri]) {
return resourceCache[uri];
}
},
/**
* Check whether or not a resource is in the cache.
*
* @param {String} uri The uri of the resource to check.
* @return {true|false} Whether this resource has already been cached.
*/
has: function(uri) {
return resourceCache.hasOwnProperty(uri);
},
/**
* Remove a specific resource from the cache.
*
* @param {String} uri The uri of the resource to remove.
* @returns {void}
*/
remove: function(uri) {
if (resourceCache.hasOwnProperty(uri)) {
delete resourceCache[uri];
}
},
/**
* Return all the uri keys currently registered.
*
* @returns {Array} An array of all registered uri keys.
*/
keys: function() {
var keys = [];
/*eslint-disable guard-for-in*/
for (var key in resourceCache) {
keys.push(key);
}
/*eslint-enable guard-for-in*/
return keys;
},
/**
* Remove everything from the cache.
*
* @returns {void}
*/
clearAll: function() {
var keys = this.keys();
for (var i = 0; i < keys.length; i++) {
delete resourceCache[keys[i]];
}
},
/**
* Return the number of resources in the cache.
*
* @returns {number} The number of resources cached.
*/
length: function() {
return this.keys().length;
}
};
});

View File

@ -1,101 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* The $$selectedConfiguration service provides a way of easily accessing, and modifying,
* the current active cloud configuration used in an application. It will resolve configurations
* from the $$configuration provider, which provides multiple configuration loading mechanisms.
*/
angular.module('openstack').service('$$selectedConfiguration',
function($$persistentStorage, $$configuration) {
'use strict';
var storageKey = '$$selectedConfiguration';
var currentConfiguration, selectingConfiguration;
return {
/**
* Retrieve the currently active configuration. If there is not configuration selected,
* this resources' $promise will fail.
*
* @returns {*} The configuration.
*/
get: function() {
// If we're in the process of selecting one, return that.
if (selectingConfiguration) {
return selectingConfiguration;
}
// If we have a current one, return that.
if (currentConfiguration) {
return currentConfiguration;
}
// Return the 'selected' configuration by setting it.
return this.set($$persistentStorage.get(storageKey));
},
/**
* Set a particular configuration as active. The selection will only be persisted to
* LocalStorage if the selected configuration is valid and available in the
* $$configuration list.
*
* @param {String} configurationId The configuration to activate.
* @returns {*} The active configuration.
*/
set: function(configurationId) {
// Normalize input
if (angular.isObject(configurationId)) {
configurationId = configurationId.id || null;
}
// If we're in a resolved state and the ID's match...
if (currentConfiguration && currentConfiguration.id === configurationId) {
return currentConfiguration;
}
// If we're in a resolving state and the ID's match...
if (selectingConfiguration && selectingConfiguration.id === configurationId) {
return selectingConfiguration;
}
// If we've reached this point, we need to resolve a new configuration.
selectingConfiguration = $$configuration.read(configurationId);
selectingConfiguration.$promise.then(
function() {
// Only set this if the promise resolves sucessfully.
$$persistentStorage.set(storageKey, configurationId);
currentConfiguration = selectingConfiguration;
},
function() {
// if setting fails, clear the existing storage key if it matches. This case occurs
// if the system initializes with a config ID that has been invalidated since the
// previous run.
if ($$persistentStorage.get(storageKey) === configurationId) {
$$persistentStorage.remove(storageKey);
}
}
).finally(
function() {
// clear the selecting configuration
selectingConfiguration = null;
});
return selectingConfiguration;
}
};
});

View File

@ -1,113 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This file provides an implementation of the storage API, backed by cookies.
* This particular implementation is not intelligent: It will access the
* cookie for this domain, as configured by the $cookieProvider, and will
* grant access to all values stored this way.
*/
angular.module('openstack').factory('$$cookieStorage',
function($cookies) {
'use strict';
return {
/**
* Is this storage type supported?
*
* @returns {boolean} True if it is supported, otherwise false.
*/
isSupported: function() {
return true;
},
/**
* Set a value to the provided key in memory storage. If the
* value already exists it will be overwritten.
*
* @param {String} key The key to store the value at.
* @param {*} value The value to store.
* @return {*} The stored value.
*/
set: function(key, value) {
$cookies.put(key, angular.toJson(value));
return value;
},
/**
* Retrieve a value from this storage provider.
*
* @param {String} key The key to retrieve.
* @return {*|undefined} The value, or undefined if it is not set.
*/
get: function(key) {
var result = angular.fromJson($cookies.get(key));
if (result) {
return result;
}
},
/**
* Remove a specific value from the storage provider.
*
* @param {String} key The key to remove.
* @returns {void}
*/
remove: function(key) {
$cookies.remove(key);
},
/**
* Return all the keys currently registered.
*
* @returns {Array} An array of all registered keys.
*/
keys: function() {
var all = $cookies.getAll();
var keys = [];
/*eslint-disable guard-for-in*/
for (var key in all) {
keys.push(key);
}
/*eslint-enable guard-for-in*/
return keys;
},
/**
* Remove everything from the memory storage mechanism.
*
* @returns {void}
*/
clearAll: function() {
var all = $cookies.getAll();
/*eslint-disable guard-for-in*/
for (var key in all) {
$cookies.remove(key);
}
/*eslint-enable guard-for-in*/
},
/**
* Return the size of the current memory storage.
*
* @returns {number} The number of keys in storage.
*/
length: function() {
return this.keys().length;
}
};
});

View File

@ -1,150 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A $$localStorage service behind a common API. If localStorage is not
* supported, this will log a warning to the console. If you want a provider
* that gracefully degrades, use $$persistentStorage.
*/
angular.module('openstack').factory('$$localStorage',
function($window, $log) {
'use strict';
/**
* Detect whether localStorage is supported, and make sure we can write
* to it.
*/
var isSupported = (function() {
// Does it exist?
if (!$window.localStorage) {
return false;
}
// Can we write to it?
var testKey = '__' + Math.round(Math.random() * 1e7);
try {
$window.localStorage.setItem(testKey, '');
$window.localStorage.removeItem(testKey);
return true;
} catch (e) {
return false;
}
})();
return {
/**
* Is this storage type supported?
*
* @returns {boolean} True if it is supported, otherwise false.
*/
isSupported: function() {
return isSupported;
},
/**
* Set a value of the provided key. If the
* value already exists it will be overwritten.
*
* @param {String} key The key to store the value at.
* @param {*} value The value to store.
* @return {*} The stored value.
*/
set: function(key, value) {
if (isSupported) {
$window.localStorage.setItem(key, angular.toJson(value));
return value;
}
$log.warn('$$localStorage not supported');
},
/**
* Retrieve a value from this storage provider.
*
* @param {String} key The key to retrieve.
* @return {*|undefined} The value, or undefined if it is not set.
*/
get: function(key) {
if (isSupported) {
var result = angular.fromJson($window.localStorage.getItem(key));
if (result) {
return result;
}
} else {
$log.warn('$$localStorage not supported');
}
},
/**
* Remove a specific value from the storage provider.
*
* @param {String} key The key to remove.
* @returns {void}
*/
remove: function(key) {
if (isSupported) {
return $window.localStorage.removeItem(key);
}
$log.warn('$$localStorage not supported');
},
/**
* Return all the keys currently registered.
*
* @returns {Array} An array of all registered keys.
*/
keys: function() {
if (isSupported) {
var keys = [];
for (var i = 0; i < $window.localStorage.length; i++) {
keys.push($window.localStorage.key(i));
}
return keys;
}
$log.warn('$$localStorage not supported');
return [];
},
/**
* Remove everything from the memory storage mechanism.
*
* @returns {void}
*/
clearAll: function() {
if (isSupported) {
var keys = this.keys();
for (var i = 0; i < keys.length; i++) {
this.remove(keys[i]);
}
}
$log.warn('$$localStorage not supported');
},
/**
* Return the size of the current memory storage.
*
* @returns {number} The number of keys in storage.
*/
length: function() {
if (isSupported) {
return $window.localStorage.length;
}
$log.warn('$$localStorage not supported');
return 0;
}
};
});

View File

@ -1,117 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This provides a memory-based key/value storage mechanism. It's provided as
* a fallback option for all other storage mechanisms, to prevent unexpected
* runtime failures.
*/
angular.module('openstack').factory('$$memoryStorage',
function() {
'use strict';
var memoryStorage = {};
return {
/**
* Is this storage type supported?
*
* @returns {boolean} True if it is supported, otherwise false.
*/
isSupported: function() {
return true;
},
/**
* Set a value to the provided key in memory storage. If the
* value already exists it will be overwritten.
*
* @param {String} key The key to store the value at.
* @param {*} value The value to store.
* @return {*} The stored value.
*/
set: function(key, value) {
memoryStorage[key] = value;
return value;
},
/**
* Retrieve a value from this storage provider.
*
* @param {String} key The key to retrieve.
* @return {*|undefined} The value, or undefined if it is not set.
*/
get: function(key) {
if (memoryStorage.hasOwnProperty(key)) {
return memoryStorage[key];
}
},
/**
* Remove a specific value from the storage provider.
*
* @param {String} key The key to remove.
* @returns {void}
*/
remove: function(key) {
delete memoryStorage[key];
},
/**
* Return all the keys currently registered.
*
* @returns {Array} An array of all registered keys.
*/
keys: function() {
var keys = [];
/*eslint-disable guard-for-in*/
for (var key in memoryStorage) {
keys.push(key);
}
/*eslint-enable guard-for-in*/
return keys;
},
/**
* Remove everything from the memory storage mechanism.
*
* @returns {void}
*/
clearAll: function() {
var keys = [];
/*eslint-disable guard-for-in*/
for (var key in memoryStorage) {
keys.push(key);
}
/*eslint-enable guard-for-in*/
for (var i = 0; i < keys.length; i++) {
delete memoryStorage[keys[i]];
}
},
/**
* Return the size of the current memory storage.
*
* @returns {number} The number of keys in storage.
*/
length: function() {
return this.keys().length;
}
};
});

View File

@ -1,38 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A convenience component that automatically selects the most secure, and most
* persistent, storage mechanism available in the current runtime. This does
* not include sessionStorage, which must be used independently.
*/
angular.module('openstack').factory('$$persistentStorage',
function($log, $$cookieStorage, $$memoryStorage, $$localStorage) {
'use strict';
// Check for local storage.
if ($$localStorage.isSupported()) {
return $$localStorage;
}
// Check for cookie storage.
if ($$cookieStorage.isSupported()) {
return $$cookieStorage;
}
$log.warn('Warning: No persistent storage mechanism supported, all storage will be transient.');
return $$memoryStorage;
});

View File

@ -1,151 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A $$sessionStorage service behind a common API. If sessionStorage is not
* supported, this will log a warning to the console. If you want a provider
* that gracefully degrades, use $$persistentStorage.
*/
angular.module('openstack').factory('$$sessionStorage',
function($window, $log) {
'use strict';
/**
* Detect whether sessionStorage is supported, and make sure we can
* write to it.
*/
var isSupported = (function() {
// Does it exist?
if (!$window.sessionStorage) {
return false;
}
// Can we write to it?
var testKey = '__' + Math.round(Math.random() * 1e7);
try {
$window.sessionStorage.setItem(testKey, '');
$window.sessionStorage.removeItem(testKey);
return true;
} catch (e) {
return false;
}
})();
return {
/**
* Is this storage type supported?
*
* @returns {boolean} True if it is supported, otherwise false.
*/
isSupported: function() {
return isSupported;
},
/**
* Set a value of the provided key. If the
* value already exists it will be overwritten.
*
* @param {String} key The key to store the value at.
* @param {*} value The value to store.
* @return {*} The stored value.
*/
set: function(key, value) {
if (isSupported) {
$window.sessionStorage.setItem(key, angular.toJson(value));
return value;
}
$log.warn('$$sessionStorage not supported');
},
/**
* Retrieve a value from this storage provider.
*
* @param {String} key The key to retrieve.
* @return {*|undefined} The value, or undefined if it is not set.
*/
get: function(key) {
if (isSupported) {
var result = angular.fromJson($window.sessionStorage.getItem(key));
if (result) {
return result;
}
} else {
$log.warn('$$sessionStorage not supported');
}
},
/**
* Remove a specific value from the storage provider.
*
* @param {String} key The key to remove.
* @returns {void}
*/
remove: function(key) {
if (isSupported) {
return $window.sessionStorage.removeItem(key);
}
$log.warn('$$sessionStorage not supported');
},
/**
* Return all the keys currently registered.
*
* @returns {Array} An array of all registered keys.
*/
keys: function() {
if (isSupported) {
var keys = [];
for (var i = 0; i < $window.sessionStorage.length; i++) {
keys.push($window.sessionStorage.key(i));
}
return keys;
}
$log.warn('$$sessionStorage not supported');
return [];
},
/**
* Remove everything from the memory storage mechanism.
*
* @returns {void}
*/
clearAll: function() {
if (isSupported) {
var keys = this.keys();
for (var i = 0; i < keys.length; i++) {
this.remove(keys[i]);
}
return;
}
$log.warn('$$sessionStorage not supported');
},
/**
* Return the size of the current memory storage.
*
* @returns {number} The number of keys in storage.
*/
length: function() {
if (isSupported) {
return $window.sessionStorage.length;
}
$log.warn('$$sessionStorage not supported');
return 0;
}
};
});

View File

@ -1,48 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This directive requires ui-router, as it listens for events dispatched as
* a user navigates through the application, and adds the 'active' class to
* the bound element if the user's selected path matches the one configured.
*/
angular.module('ironic.util').directive('activePath',
function ($location, $rootScope) {
'use strict';
return {
link: function ($scope, element, attrs) {
var activePath = attrs.activePath;
function setActivePath () {
var path = $location.path();
var isMatchedPath = path.match(activePath) !== null;
element.toggleClass('active', isMatchedPath);
}
// This is angularjs magic, the return method from any $on
// binding will return a function that will disconnect
// that binding.
var disconnectBinding =
$rootScope.$on('$stateChangeSuccess', setActivePath);
$scope.$on('$destroy', disconnectBinding);
// INIT
setActivePath();
}
};
});

View File

@ -1,26 +0,0 @@
/*
* Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
angular.module('ironic.util').filter('capitalize',
function() {
'use strict';
return function(input) {
input = input || '';
return input.replace(/([^\W_]+[^\s-]*) */g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
};
});

View File

@ -1,20 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* UI Utilities used by the ironic client.
*/
angular.module('ironic.util', []);

View File

@ -1,58 +0,0 @@
<div class="modal-header">
<button type="button"
class="close"
ng-disabled="!ctrl.nodes.$resolved || ctrl.updating"
ng-click="ctrl.close()">&times;</button>
<h3 class="panel-title">{{ctrl.actionName | capitalize }} Node</h3>
</div>
<div class="modal-body" ng-if="!ctrl.nodes.$resolved">
<div class="row">
<div class="col-xs-12">
<p class="text-center text-muted">
<i class="fa fa-spin fa-refresh"></i> Loading Details...
</p>
</div>
</div>
</div>
<div class="modal-body" ng-if="ctrl.nodes.$resolved && ctrl.nodes.length > 0">
<div class="row">
<div class="col-xs-12">
<p>Ironic will {{ctrl.actionName}} the following nodes.</p>
<table class="table table-condensed table-striped">
<tr>
<th>Node ID</th>
<th>Node Name</th>
<th>Node State</th>
</tr>
<tr ng-repeat="ctx in ctrl.nodes">
<td>{{ctx.node.uuid}}</td>
<td>{{ctx.node.name || 'None'}}</td>
<td ng-switch="ctx.state">
<div ng-switch-when="updating" class="text-warning">
<i class="fa fa-spin fa-refresh"></i> Updating...
</div>
<div ng-switch-when="complete" class="text-muted">
Complete
</div>
<div ng-switch-when="error" class="text-danger">
<i class="fa fa-exclamation-triangle"></i> Error
</div>
<div ng-switch-default>
{{ctx.node.provision_state | capitalize}}
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="modal-footer ng-scope">
<button class="btn btn-danger" ng-click="ctrl.apply()"
ng-disabled="!ctrl.nodes.$resolved || ctrl.updating">
{{ ctrl.actionName | capitalize }}
</button>
<button class="btn btn-default" ng-click="ctrl.close()"
ng-disabled="!ctrl.nodes.$resolved || ctrl.updating">
Cancel
</button>
</div>

View File

@ -1,52 +0,0 @@
<div class="modal-header">
<button type="button"
class="close"
ng-disabled="ctrl.deleting"
ng-click="ctrl.close()">&times;</button>
<h3 class="panel-title">Remove Nodes</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-12">
<div class="alert alert-danger">
<span class="fa fa-exclamation-circle"></span>
You are about to remove the following nodes.
</div>
<dl class="dl-horizontal">
<dt ng-repeat-start="e in ctrl.nodes"
ng-switch="e.state">
<div ng-switch-when="removing" class="text-warning">
<i class="fa fa-spin fa-refresh"></i> Removing...
</div>
<div ng-switch-when="removed" class="text-muted">
Removed:
</div>
<div ng-switch-when="error" class="text-danger">
<i class="fa fa-exclamation-triangle"></i> Error:
</div>
<div ng-switch-default>
Node:
</div>
</dt>
<dd ng-repeat-end>
{{e.node.uuid}}
<span ng-if="e.node.name">({{e.node.name}})</span>
<span class="text-danger" ng-if="e.error"><br/>
{{e.error.faultcode}}: {{e.error.faultstring}}
</span>
</dd>
</dl>
This will remove all knowledge of the nodes from the system. It will not modify the nodes,
however you will lose the ability to manage them via this application.
</div>
</div>
</div>
<div class="modal-footer ng-scope">
<p class="text-danger" ng-if="!!ctrl.error_message">{{ctrl.error_message}}</p>
<button class="btn btn-danger" ng-click="ctrl.remove()"
ng-disabled="ctrl.deleting">Remove
</button>
<button class="btn btn-default" ng-click="ctrl.close()"
ng-disabled="ctrl.deleting">Cancel
</button>
</div>

View File

@ -1,16 +0,0 @@
<div class="modal-header">
<button type="button"
class="close"
ng-click="ctrl.close()">&times;</button>
<h3 class="panel-title">Unsupported Action</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-12">
<p>The action "{{ctrl.actionName | capitalize}}" is not yet supported.</p>
</div>
</div>
</div>
<div class="modal-footer ng-scope">
<button class="btn btn-default" ng-click="ctrl.close()">Cancel</button>
</div>

View File

@ -1,81 +0,0 @@
<div class="row">
<div class="col-xs-12">
<h3>Manage Clouds</h3>
<p>Below you see a collection of all the Ironic clouds available
to you. You may add additional ones here, or remove ones that
you've previously added.</p>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Name</th>
<th>API Endpoint</th>
<th>Source</th>
<th class="text-right">
<button class="btn btn-default btn-sm"
ng-click="ctrl.add()">
<i class="fa fa-plus"></i>
<span class="hidden-xs">Add Cloud</span>
</button>
</th>
</tr>
</thead>
<tbody ng-if="ctrl.configurations.$resolved && ctrl.configurations.length == 0">
<tr>
<td colspan="4" class="text-center text-muted">
<em>
<small>We were unable to detect a valid ironic service. Would you like
to <a href="" ng-click="ctrl.add()">add one</a>?
</small>
</em>
</td>
</tr>
</tbody>
<tbody>
<tr ng-repeat="config in ctrl.configurations">
<td>
<a href="" ng-click="ctrl.select(config)">
{{config.name}}
<i class="fa fa-play-circle"></i>
</a>
</td>
<td>
{{config.ironic.apiRoot}}
</td>
<td ng-switch="config.source">
<div ng-switch-when="local">
<small class="text-info">Local</small>
<i class="fa fa-question-circle"
tooltip="This configuration was added by you, and is stored in your browser."></i>
</div>
<div ng-switch-when="host">
<small class="text-success">Host</small>
<i class="fa fa-question-circle"
tooltip="This configuration was loaded from the server."></i>
</div>
<div ng-switch-when="static">
<small class="text-success">Static</small>
<i class="fa fa-question-circle"
tooltip="This configuratoin was set by your application administrator."></i>
</div>
<div ng-switch-when="default">
<small class="text-success">Default</small>
<i class="fa fa-question-circle"
tooltip="This configuration was automatically detected."></i>
</div>
</td>
<td>
<button type="button" class="close"
ng-if="config.source === 'local'"
ng-click="ctrl.remove(config)">
&times;</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -1,85 +0,0 @@
<div class="modal-header">
<button type="button"
class="close"
ng-click="ctrl.close()">&times;</button>
<h3 class="panel-title">Add Cloud</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-12">
<form name="configForm">
<div class="form-group has-feedback"
ng-class="{'has-error': configForm.name.$invalid && configForm.name.$dirty,
'has-success': configForm.name.$valid && configForm.name.$dirty}">
<label for="name" class="control-label">Name</label>
<input id="name"
name="name"
type="text"
class="form-control"
ng-model="ctrl.newConfiguration.name"
required
ng-minlength="5"
placeholder="Please provide a distinct name for this cloud.">
<span class="glyphicon glyphicon-remove form-control-feedback"
ng-if="!configForm.name.$valid && configForm.name.$dirty">
</span>
<span class="glyphicon glyphicon-ok form-control-feedback"
ng-if="configForm.name.$valid">
</span>
<small class="help-block">This will be used to
distinguish this group of API's from others. Please
use a name longer than 5 characters.
</small>
</div>
<div class="form-group has-feedback"
ng-class="{'has-error': configForm.ironic.$invalid && configForm.ironic.$dirty,
'has-success': configForm.ironic.$valid && configForm.ironic.$dirty}">
<label class="control-label" for="ironic">
Ironic API URL
<small class="text-muted"
ng-if="configForm.ironic.$pending">
&emsp;
<i class="fa fa-spin fa-refresh"></i>
Checking...
</small>
</label>
<input id="ironic"
name="ironic"
type="url"
required
ironic-api-url
class="form-control"
ng-model="ctrl.newConfiguration.ironic.apiRoot"
ng-model-options="{debounce: 250}"
placeholder="https://ironic.example.com:6385">
<span class="glyphicon glyphicon-remove form-control-feedback"
ng-if="!configForm.ironic.$valid && configForm.ironic.$dirty">
</span>
<span class="glyphicon glyphicon-ok form-control-feedback"
ng-if="configForm.ironic.$valid">
</span>
<small class="help-block"
ng-if="!configForm.ironic.$valid">Your API must
support CORS requests from
{{ctrl.location.protocol}}://{{ctrl.location.host}}:{{ctrl.location.port}}
</small>
<small class="help-block"
ng-if="configForm.ironic.$valid">Versions
{{configForm.ironic.$ironicVersions.join(', ')}}
detected.
</small>
</div>
</form>
</div>
</div>
</div>
<div class="modal-footer ng-scope">
<button type="submit"
ng-disabled="!configForm.$valid"
ng-click="ctrl.save()"
class="btn btn-primary">
Save
</button>
<button class="btn btn-default" ng-click="ctrl.close()">Cancel</button>
</div>

View File

@ -1,11 +0,0 @@
<div class="row">
<div class="navbar-header col-lg-3 col-xs-2">
<a class="navbar-brand text-nowrap"
href="#/ironic"
active-path="^\/ironic$">
<i class="of of-openstack"></i>
<span class="hidden-sm hidden-md hidden-xs">OpenStack</span>
Bare Metal
</a>
</div>
</div>

View File

@ -1,37 +0,0 @@
<div class="container-fluid"
ng-if="!nodeCtrl.errorMessage">
<div class="row">
<div class="col-xs-12">
<h1>Node: {{nodeCtrl.node.name || nodeCtrl.node.uuid}}</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<ul class="nav nav-tabs">
<li active-path="^\/ironic\/nodes\/.*\/node$">
<a href="#/ironic/nodes/{{nodeCtrl.node.uuid}}/node">Node</a>
</li>
<li active-path="^\/ironic\/nodes\/.*\/ports$">
<a href="#/ironic/nodes/{{nodeCtrl.node.uuid}}/ports">Ports</a>
</li>
<li active-path="^\/ironic\/nodes\/.*\/driver$">
<a href="#/ironic/nodes/{{nodeCtrl.node.uuid}}/driver">Driver</a>
</li>
</ul>
</div>
</div>
<div class="row" ui-view>
</div>
</div>
<div class="container-fluid"
ng-if="nodeCtrl.errorMessage">
<div class="row">
<div class="col-xs-13">
<h2 class="text-center text-danger">Error:
({{nodeCtrl.errorMessage.faultcode}})
{{nodeCtrl.errorMessage.faultstring}}
</h2>
</div>
</div>
</div>

View File

@ -1,28 +0,0 @@
<div class="col-xs-12">
<table class="table-condensed table table-bordered col-xs-12">
<tbody ng-if="driverCtrl.driver && driverCtrl.driver.$resolved">
<tr ng-repeat="(key, value) in driverCtrl.driver">
<th>{{key}}</th>
<td>{{value}}</td>
</tr>
</tbody>
<tbody ng-if="driverCtrl.driver && !driverCtrl.driver.$resolved">
<tr>
<td class="text-center">
<i class="fa fa-spin fa-refresh"></i>
</td>
</tr>
</tbody>
<tbody ng-if="driverCtrl.errorMessage">
<tr>
<td class="text-center text-danger">
<strong>Error:</strong>
({{driverCtrl.errorMessage.faultcode}})
{{driverCtrl.errorMessage.faultstring}}
</td>
</tr>
</tbody>
</table>
</div>

View File

@ -1,28 +0,0 @@
<div class="col-xs-12">
<table class="table-condensed table table-bordered col-xs-12">
<tbody ng-if="nodeCtrl.node && nodeCtrl.node.$resolved">
<tr ng-repeat="(key, value) in nodeCtrl.node">
<th>{{key}}</th>
<td>{{value}}</td>
</tr>
</tbody>
<tbody ng-if="nodeCtrl.node && !nodeCtrl.node.$resolved">
<tr>
<td class="text-center">
<i class="fa fa-spin fa-refresh"></i>
</td>
</tr>
</tbody>
<tbody ng-if="nodeCtrl.errorMessage">
<tr>
<td class="text-center text-danger">
<strong>Error:</strong>
({{nodeCtrl.errorMessage.faultcode}})
{{nodeCtrl.errorMessage.faultstring}}
</td>
</tr>
</tbody>
</table>
</div>

View File

@ -1,34 +0,0 @@
<div class="col-xs-12">
<br/>
<div ng-repeat="port in portCtrl.ports"
ng-if="portCtrl.ports && portCtrl.ports.$resolved && portCtrl.ports.length > 0">
<h3>Port: {{port.uuid}}</h3>
<table class="table-condensed table table-bordered">
<tr ng-repeat="(key, value) in port">
<th>{{key}}</th>
<td>{{value}}</td>
</tr>
</table>
</div>
<div ng-if="portCtrl.ports && portCtrl.ports.$resolved && portCtrl.ports.length == 0">
<p class="text-center">
This node has no ports.
</p>
</div>
<div ng-if="portCtrl.ports && !portCtrl.ports.$resolved">
<p class="text-center">
<i class="fa fa-spin fa-refresh"></i>
</p>
</div>
<div ng-if="portCtrl.errorMessage">
<p class="text-center text-danger">
<strong>Error:</strong>
({{portCtrl.errorMessage.faultcode}})
{{portCtrl.errorMessage.faultstring}}
</p>
</div>
</div>

View File

@ -1,50 +0,0 @@
<div class="row">
<div class="col-xs-12">
<h1>Driver List</h1>
</div>
</div>
<div class="row padding-top">
<div class="col-xs-12">
<button type="button" class="btn btn-default"
ng-disabled="!driverListCtrl.drivers.$resolved"
ng-click="driverListCtrl.init()">
Refresh
</button>
</div>
</div>
<div class="row padding-top">
<div class="col-xs-12">
<table class="table table-condensed table-striped table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Related Nodes</th>
</tr>
</thead>
<tbody ng-if="driverListCtrl.drivers.$resolved && driverListCtrl.drivers.length > 0">
<tr ng-repeat="driver in driverListCtrl.drivers"
ng-class="{'info': driverListCtrl.selectedDrivers.indexOf(driver.uuid) > -1}">
<td>{{driver.name || 'Unnamed'}}</td>
<td>
<a href="">View</a>
</td>
</tr>
</tbody>
<tbody ng-if="driverListCtrl.drivers.$resolved && driverListCtrl.drivers.length == 0">
<tr>
<td colspan="7" class="text-center text-muted">
<small><em>No drivers were found.</em></small>
</td>
</tr>
</tbody>
<tbody ng-if="!driverListCtrl.drivers.$resolved">
<tr>
<td colspan="7" class="text-center text-muted">
<i class="fa fa-refresh fa-spin"></i>
Loading drivers...
</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -1,64 +0,0 @@
<div class="modal-header">
<button type="button"
class="close"
ng-click="ctrl.close()">&times;</button>
<h3 class="panel-title">Enroll Node</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-12">
<form class="form-horizontal">
<div class="form-group">
<label for="name" class="col-xs-3 control-label">Node Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" ng-model="ctrl.node.name"
id="name" name="name"
placeholder="A descriptive node name (optional)"/>
</div>
</div>
<div class="form-group">
<label for="driver" class="col-xs-3 control-label">Node Driver</label>
<div class="col-sm-9">
<select id="driver"
class="form-control"
ng-options="driver as driver.name for driver in ctrl.drivers"
ng-model="ctrl.selectedDriver"
ng-change="ctrl.loadDriverProperties(ctrl.selectedDriver.name)">
<option value="" disabled selected>Choose a Driver</option>
</select>
</div>
</div>
</form>
<hr ng-if="ctrl.driverProperties.$resolved"/>
<form class="form-horizontal" name="configForm" id="configForm">
<p class="text-center"
ng-if="ctrl.driverProperties && !ctrl.driverProperties.$resolved">
<small><em><i class="fa fa-spin fa-refresh"></i></em></small>
</p>
<div class="form-group" ng-repeat="(name, desc) in ctrl.driverProperties">
<label for="{{name}}" class="col-xs-3 control-label">{{name}}</label>
<div class="col-sm-9">
<input type="text"
class="form-control"
id="{{name}}"
name="{{name}}"
ng-model="ctrl.node.driver_info[name]"
placeholder="{{desc}}"/>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="modal-footer ng-scope">
<p class="text-danger" ng-if="!!ctrl.error_message">{{ctrl.error_message}}</p>
<button type="submit"
ng-disabled="!ctrl.driverProperties.$resolved || !configForm.$valid"
ng-click="ctrl.enroll()"
class="btn btn-primary">
Enroll
</button>
<button class="btn btn-default" ng-click="ctrl.close()">Cancel</button>
</div>

View File

@ -1,50 +0,0 @@
<div class="row">
<div class="navbar-header col-lg-3 col-xs-2">
<a class="navbar-brand text-nowrap"
href="#"
active-path="^\/ironic$">
<i class="of of-openstack"></i>
<span class="hidden-sm hidden-md hidden-xs">OpenStack</span>
Bare Metal
</a>
</div>
<ul class="nav navbar-nav col-xs-2">
<li uib-dropdown is-open="status.isopen">
<a href="#"
uib-dropdown-toggle
data-toggle="dropdown"
class="text-nowrap">
{{headerCtrl.currentConfiguration.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li class="dropdown-header">Switch to...</li>
<li ng-repeat="cloud in headerCtrl.configurations">
<a href="#"
ng-click="headerCtrl.switchCloud(cloud)">
<i class="fa fa-cloud"></i>&nbsp;
{{cloud.name}}
</a>
</li>
<li class="divider"></li>
<li>
<a href="#/config" class="text-muted">
<i class="fa fa-gear"></i>
Manage
</a>
</li>
</ul>
</li>
</ul>
<div class="nav navbar-form-grid col-lg-7 col-xs-8">
<div class="col-xs-9 col-sm-10">
<input type="text"
class="form-control"
placeholder="Search"
disabled/>
</div>
<button type="submit" class="btn btn-default-gradient col-xs-3 col-sm-2" disabled>
Submit
</button>
</div>
</div>

View File

@ -1,22 +0,0 @@
<div class="row" ng-controller="NodeActionController as ctrl">
<div class="col-xs-6">
<div class="btn-group btn-group-lg">
<button type="button"
active-path="\/ironic\/nodes\/?.*"
ui-sref="root.ironic.nodes"
class="btn btn-default">Nodes</button>
<button type="button"
ui-sref="root.ironic.drivers"
active-path="\/ironic\/drivers\/?.*"
class="btn btn-default">Drivers</button>
</div>
</div>
<div class="col-xs-6">
<button ng-click="ctrl.enroll()"
class="btn btn-primary btn-lg pull-right">
<i class="fa fa-plus"></i> Node
</button>
</div>
</div>
<div ui-view="main">
</div>

View File

@ -1,112 +0,0 @@
<div class="row padding-top">
<div class="col-xs-12"
ng-controller="NodeActionController as actionCtrl">
<div class="btn-group" uib-dropdown>
<button type="button"
class="btn btn-default"
ng-disabled="nodeListCtrl.selectedNodes.length == 0"
ng-click="actionCtrl.powerAction(nodeListCtrl.powerTransitions[0], nodeListCtrl.selectedNodes)">
{{nodeListCtrl.powerTransitions[0] | capitalize}}
</button>
<button type="button"
class="btn btn-default"
uib-dropdown-toggle
ng-disabled="nodeListCtrl.selectedNodes.length == 0">
<i class="caret"></i>
</button>
<ul uib-dropdown-menu>
<li ng-repeat="event in nodeListCtrl.powerTransitions">
<a ng-disabled="nodeListCtrl.selectedNodes.length == 0"
ng-click="actionCtrl.powerAction(event, nodeListCtrl.selectedNodes)"
href="">
{{event | capitalize}}
</a>
</li>
<li role="separator" class="divider"></li>
<li ng-repeat="event in nodeListCtrl.provisionTransitions">
<a ng-disabled="nodeListCtrl.selectedNodes.length == 0"
ng-click="actionCtrl.provisionAction(event, nodeListCtrl.selectedNodes)"
href="">
{{event | capitalize}}
</a>
</li>
</ul>
</div>
<button type="button" class="btn btn-default"
ng-disabled="nodeListCtrl.selectedNodes.length == 0"
ng-click="actionCtrl.powerAction('reboot', nodeListCtrl.selectedNodes)">
Reboot
</button>
<button type="button" class="btn btn-default"
ng-disabled="!nodeListCtrl.nodes.$resolved"
ng-click="nodeListCtrl.init()">
Refresh
</button>
</div>
</div>
<div class="row padding-top">
<div class="col-xs-12">
<table class="table table-condensed table-striped table-bordered table-hover">
<thead>
<tr>
<th><input type="checkbox"
ng-model="nodeListCtrl.selectAll"
ng-change="nodeListCtrl.toggleSelectAll(nodeListCtrl.selectAll)"
ng-if="nodeListCtrl.nodes.length > 0"
/></th>
<th>UUID</th>
<th>Name</th>
<th>Instance</th>
<th>Provision State</th>
<th>
<select class="form-control input-sm pull-right" disabled>
<option value="-1">All</option>
<option value="true">True</option>
<option value="false">False</option>
</select>
Maintenance
</th>
<th>
<select class="form-control input-sm pull-right" disabled>
<option value="-1">All</option>
<option value="on">On</option>
<option value="off">Off</option>
</select>
Power
</th>
</tr>
</thead>
<tbody ng-if="nodeListCtrl.nodes.$resolved && nodeListCtrl.nodes.length > 0">
<tr ng-repeat="node in nodeListCtrl.nodes"
ng-class="{'info': nodeListCtrl.selectedNodes.indexOf(node.uuid) > -1}">
<td>
<input type="checkbox"
checklist-model="nodeListCtrl.selectedNodes"
checklist-value="node.uuid"/>
</td>
<td><a href="#/ironic/nodes/{{node.uuid}}/node">{{node.uuid}}</a></td>
<td>{{node.name || 'Unnamed'}}</td>
<td>{{node.instance_uuid || 'None'}}</td>
<td>{{node.provision_state | capitalize}}</td>
<td>{{node.maintenance ? 'True' : 'False'}}</td>
<td>{{node.power_state || 'Unknown'}}</td>
</tr>
</tbody>
<tbody ng-if="nodeListCtrl.nodes.$resolved && nodeListCtrl.nodes.length == 0">
<tr>
<td colspan="7" class="text-center text-muted">
<small><em>No nodes were found.</em></small>
</td>
</tr>
</tbody>
<tbody ng-if="!nodeListCtrl.nodes.$resolved">
<tr>
<td colspan="7" class="text-center text-muted">
<i class="fa fa-refresh fa-spin"></i>
Loading nodes...
</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -1,9 +0,0 @@
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid" ui-view="header"></div>
</nav>
<div class="container-fluid" ui-view="root"></div>
<nav class="navbar navbar-default navbar-fixed-bottom">
<div class="container-fluid" ui-view="footer"></div>
</nav>

View File

@ -1,35 +0,0 @@
{
"name": "openstack-ironic-webclient",
"version": "1.0.0",
"description": "A webclient for OpenStack Ironic.",
"license": "Apache-2.0",
"dependencies": {
"angular": "1.5.1",
"angular-bootstrap": "1.2.4",
"angular-cookies": "1.5.1",
"angular-resource": "1.5.1",
"angular-ui-router": "0.2.18",
"bootstrap-sass": "3.3.6",
"fontawesome": "4.5.0",
"checklist-model": "0.9.0"
},
"devDependencies": {},
"overrides": {
"bootstrap-sass": {
"dependencies": false,
"main": [
"assets/stylesheets/_bootstrap.scss",
"assets/fonts/bootstrap/glyphicons-halflings-regular.eot",
"assets/fonts/bootstrap/glyphicons-halflings-regular.svg",
"assets/fonts/bootstrap/glyphicons-halflings-regular.ttf",
"assets/fonts/bootstrap/glyphicons-halflings-regular.woff",
"assets/fonts/bootstrap/glyphicons-halflings-regular.woff2"
]
},
"fontawesome": {
"main": [
"fonts/*"
]
}
}
}

View File

@ -1,197 +0,0 @@
(function initializeGulp () {
'use strict';
var SERVER_PORT = process.env.PORT || 8000;
var SERVER_HOST = process.env.IPADDR || process.env.IP || 'localhost';
var gulp = require('gulp');
var sass = require('gulp-sass');
var concat = require('gulp-concat');
var mainBowerFiles = require('main-bower-files');
var filter = require('gulp-filter');
var webserver = require('gulp-webserver');
var del = require('del');
var fs = require('fs');
var path = require('path');
var iconfont = require('gulp-iconfont');
var consolidate = require('gulp-consolidate');
var vinylPaths = require('vinyl-paths');
var merge = require('merge-stream');
var dir = {
app: './app',
dist: './www',
cover: './cover',
bower: './bower_components',
npm: './node_modules'
};
/**
* Cleans all imported, and generated, files from the project.
*
* @param {Function} cb callback.
* @return {*} A gulp stream that performs this action.
*/
gulp.task('clean', function () {
return gulp.src([
dir.npm,
dir.bower,
dir.dist,
dir.cover
]).pipe(vinylPaths(del));
});
/**
* Build our font from the icon svg.
*
* @return {*} A gulp stream that performs this action.
*/
gulp.task('iconfont', function() {
gulp.src([dir.app + '/fonts/openstack/*.svg'])
.pipe(iconfont({
fontName: 'openstack',
appendCodepoints: true,
appendUnicode: true,
formats: ['ttf', 'eot', 'woff', 'woff2', 'svg']
}))
.on('glyphs', function(glyphs) {
var options = {
glyphs: glyphs,
fontName: 'openstack',
fontPath: '../fonts/',
className: 'of'
};
return gulp.src(dir.app + '/fonts/openstack/openstack-font.css')
.pipe(consolidate('lodash', options))
.pipe(gulp.dest(dir.dist + '/css'));
})
.pipe(gulp.dest(dir.dist + '/fonts'));
});
/**
* Start a local server and serve the packaged application code.
* This also watches the source tree and will update the application
* whenever changes are detected.
*
* @return {*} A gulp stream that performs this action.
*/
gulp.task('serve', ['package'], function () {
// Watch changes to the fonts directory.
gulp.watch([dir.app + '/fonts/**/*.*'], ['iconfont']);
// Watch changes to the css directory.
gulp.watch([dir.app + '/css/*.scss'], ['package:styles']);
// Watch changes to the bower.json file.
gulp.watch(['./bower.json'], ['package:libs']);
gulp.watch(
[dir.app + '/**/*.+(html)'],
['package:static']);
gulp.watch(
[dir.app + '/**/*.+(js)'],
['package:app']);
return gulp.src(dir.dist)
.pipe(webserver({
host: SERVER_HOST,
port: SERVER_PORT,
livereload: true,
open: true
}));
});
/**
* Copy all the fonts into the dist directory, and generate any custom
* fonts necessary for the application.
*
* @return {*} A gulp stream that performs this action.
*/
gulp.task('package:fonts', ['iconfont'], function () {
var files = mainBowerFiles();
return gulp.src(files)
.pipe(filter(['*.eot', '*.svg', '*.ttf', '*.woff', '*.woff2']))
.pipe(gulp.dest(dir.dist + '/fonts'));
});
/**
* Compile all styling files for the project. We're using SCSS includes here,
* to explicitly choose which bits we need included.
*
* @return {*} A gulp stream that performs this action.
*/
gulp.task('package:styles', function () {
return gulp.src([dir.app + '/css/*.scss'])
.pipe(sass())
.pipe(gulp.dest(dir.dist + '/css'));
});
/**
* Copy all external javascript files, as independent documents (since
* they may carry their own license) into the output directory.
*
* @return {*} A gulp stream that performs this action.
*/
gulp.task('package:libs', function () {
// NOTE: This is the extension point for package maintainers. If, rather
// than using bower, you would like to link in dependencies from a different
// source (ex. debian package), this is the method that would need to be
// extended to look for those.
var files = mainBowerFiles();
return gulp.src(files)
.pipe(filter('*.js'))
.pipe(gulp.dest(dir.dist + '/js/lib'));
});
/**
* Iterate through the modules directory and build a concatenated js file for each directory.
*
* @return {*} A gulp stream that performs this action.
*/
gulp.task('package:app', function () {
var streams = [];
var modulePath = path.join(dir.app, 'js', 'modules');
/*eslint-disable no-sync */
fs.readdirSync(modulePath)
.filter(function(file) {
// Only pull directories
return fs.statSync(path.join(modulePath, file)).isDirectory();
}).forEach(function(libName) {
// Build a file glob pattern.
var libPath = path.join(modulePath, libName);
var files = [
path.join(libPath, 'module.js'),
path.join(libPath, '**/*.js')
];
// Create a concat stream for this library.
var stream = gulp.src(files)
.pipe(concat(libName + '.js'))
.pipe(gulp.dest(path.join(dir.dist, 'js')));
streams.push(stream);
});
/*eslint-enable no-sync */
// Merge all the streams and return.
return merge.apply(merge, streams);
});
/**
* Copy static assets into our package.
*
* @return {*} A gulp stream that performs this action.
*/
gulp.task('package:static', function () {
return gulp.src([
dir.app + '/**/*.+(html|ico)'
]).pipe(gulp.dest(dir.dist));
});
/**
* Package the app
*/
gulp.task('package', ['package:static', 'package:app', 'package:fonts',
'package:styles', 'package:libs']);
})();

View File

@ -1,84 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function () {
'use strict';
module.exports = function (config) {
config.set({
port: 9876,
basePath: './',
frameworks: ['jasmine'],
browsers: ['Chrome', 'Firefox'],
reporters: ['progress', 'coverage', 'threshold'],
browserNoActivityTimeout: 30000,
plugins: [
'karma-jasmine',
'karma-coverage',
'karma-threshold-reporter',
'karma-chrome-launcher',
'karma-firefox-launcher'
],
preprocessors: {
'www/js/{!lib/**/*.js,*.js}': ['coverage']
},
files: [
// Library files, with some ordering.
'www/js/lib/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'www/js/lib/*.js',
// Application files
'www/js/**/*.js',
// Mocks
'test/mock/**/*.js',
// Tests
'test/spec/**/*.js'
],
coverageReporter: {
type: 'html',
dir: 'cover',
instrumenterOptions: {
istanbul: {noCompact: true}
}
},
// Coverage threshold values.
thresholdReporter: {
statements: 94, // target 100
branches: 96, // target 100
functions: 92, // target 100
lines: 94 // target 100
},
exclude: [],
singleRun: true
});
};
})();

View File

@ -1,48 +0,0 @@
{
"name": "ironic-webclient",
"version": "1.0.0",
"description": "A webclient for OpenStack Ironic.",
"main": "www/index.html",
"repository": "https://git.openstack.org/openstack/ironic-webclient",
"scripts": {
"postinstall": "bower install --config.interactive=false",
"start": "gulp serve",
"prepublish": "gulp package",
"lint": "eslint ./",
"pretest": "gulp package",
"test": "karma start ./karma.conf.js",
"clean": "gulp clean"
},
"files": [
"www"
],
"author": "OpenStack <openstack-dev@lists.openstack.org>",
"license": "Apache-2.0",
"devDependencies": {
"angular-mocks": "1.4.6",
"bower": "1.5.2",
"del": "1.2.1",
"eslint": "1.10.3",
"eslint-config-openstack": "1.2.4",
"eslint-plugin-angular": "1.0.0",
"gulp": "3.9.0",
"gulp-concat": "2.6.0",
"gulp-consolidate": "0.1.2",
"gulp-filter": "3.0.0",
"gulp-iconfont": "5.0.1",
"gulp-sass": "2.0.4",
"gulp-webserver": "0.9.1",
"jasmine": "2.3.2",
"karma": "0.13.9",
"karma-chrome-launcher": "0.2.0",
"karma-cli": "0.1.0",
"karma-coverage": "0.5.0",
"karma-firefox-launcher": "0.1.6",
"karma-jasmine": "0.3.6",
"karma-threshold-reporter": "0.1.15",
"lodash": "3.10.1",
"main-bower-files": "2.9.0",
"merge-stream": "1.0.0",
"vinyl-paths": "1.0.0"
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This module provides some basic IronicDriver API responses.
*
* Usage: beforeEach(module('ironic.mock.IronicDriver'));
*/
angular.module('ironic.api.mock.IronicDriver',
['ironic.api', 'openstack.mock.$$selectedConfiguration'])
.run(function($httpBackend) {
'use strict';
$httpBackend
.whenGET('http://ironic.example.com:1000/drivers')
.respond(200, {
drivers: [
{name: 'test_driver_1'},
{name: 'test_driver_2'}
]
});
$httpBackend
.whenGET('http://ironic.example.com:1000/drivers/test_driver_1')
.respond(200, {
name: "test_driver_1"
});
$httpBackend
.whenGET('http://ironic.example.com:1000/drivers/test_driver_2')
.respond(200, {
name: "test_driver_2"
});
});

View File

@ -1,44 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This module provides some basic IronicDriverProperties API responses.
*
* Usage: beforeEach(module('ironic.mock.IronicDriverProperties'));
*/
angular.module('ironic.api.mock.IronicDriverProperties',
['ironic.api', 'openstack.mock.$$selectedConfiguration'])
.run(function($httpBackend) {
'use strict';
$httpBackend
.whenGET('http://ironic.example.com:1000/drivers/properties?driver_name=test_driver_1')
.respond(200, {name: 'test_driver_1'});
$httpBackend
.whenGET('http://ironic.example.com:1000/drivers/properties?driver_name=test_driver_2')
.respond(200, {name: 'test_driver_2'});
$httpBackend
.whenGET('http://ironic.example.com:1000/drivers/properties?driver_name=invalid')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
});

View File

@ -1,62 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This module provides some basic IronicNode API responses.
*
* Usage: beforeEach(module('ironic.mock.IronicNode'));
*/
angular.module('ironic.api.mock.IronicNode',
['ironic.api', 'openstack.mock.$$selectedConfiguration'])
.run(function($httpBackend) {
'use strict';
var nodes = [
{
uuid: 'test_node_1',
driver: 'test_driver_1',
provision_state: 'enroll'
},
{
uuid: 'test_node_2',
driver: 'test_driver_1',
provision_state: 'enroll'
},
{
uuid: 'test_node_3',
driver: 'test_driver_2',
provision_state: 'manageable'
}
];
$httpBackend
.whenGET('http://ironic.example.com:1000/nodes')
.respond(200, {
nodes: angular.copy(nodes)
});
$httpBackend
.whenGET('http://ironic.example.com:1000/nodes/test_node_1')
.respond(200, angular.copy(nodes[0]));
$httpBackend
.whenGET('http://ironic.example.com:1000/nodes/test_node_2')
.respond(200, angular.copy(nodes[1]));
$httpBackend
.whenGET('http://ironic.example.com:1000/nodes/test_node_3')
.respond(200, angular.copy(nodes[3]));
});

View File

@ -1,44 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This module provides some basic IronicPort API responses.
*
* Usage: beforeEach(module('ironic.mock.IronicPort'));
*/
angular.module('ironic.api.mock.IronicPort',
['ironic.api', 'openstack.mock.$$selectedConfiguration'])
.run(function($httpBackend) {
'use strict';
$httpBackend
.whenGET('http://ironic.example.com:1000/ports?node=test_node_1')
.respond(200, {
ports: []
});
$httpBackend
.whenGET('http://ironic.example.com:1000/ports?node=test_node_2')
.respond(200, {
ports: []
});
$httpBackend
.whenGET('http://ironic.example.com:1000/ports?node=test_node_3')
.respond(200, {
ports: []
});
});

View File

@ -1,45 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This module loads various mock configurations into the test scope.
*
* Usage: beforeEach(module('openstack.mock.$$configuration'));
*/
angular.module('openstack.mock.$$configuration', ['openstack'])
.config(function($$configurationProvider) {
'use strict';
// Disable unused configuration options for the test harness.
$$configurationProvider.$enableLocalStorage(false);
$$configurationProvider.$enableDefault(false);
$$configurationProvider.$enableConfigLoad(false);
// Wrap a spy around our other feature flags so they can't accidentally be triggered in a
// test harness.
spyOn($$configurationProvider, '$enableLocalStorage');
spyOn($$configurationProvider, '$enableDefault');
spyOn($$configurationProvider, '$enableConfigLoad');
$$configurationProvider.$addConfig({
id: 'test_config_1',
ironic: {apiRoot: 'http://ironic.example.com:1000'}
});
$$configurationProvider.$addConfig({
id: 'test_config_2',
ironic: {apiRoot: 'http://ironic.example.com:2000'}
});
});

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This module automatically selects a default configuration for the test harness.
*
* Usage: beforeEach(module('openstack.mock.$$selectedConfiguration'));
*/
angular.module('openstack.mock.$$selectedConfiguration', ['openstack.mock.$$configuration'])
.run(function($$selectedConfiguration, $$persistentStorage, $rootScope) {
'use strict';
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
afterEach(function() {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
});
});

View File

@ -1,28 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Mock service override for the ui-router template factory, so that we can unit test components
* without having to mock out the entire template loading mechanism.
*/
angular.module('template.mock', ['ui.router'])
.run(function($templateFactory) {
'use strict';
// Spy on the fromUrl method, so we intercept all $http calls
// to templates and don't fail on creating those.
spyOn($templateFactory, 'fromUrl');
});

View File

@ -1,103 +0,0 @@
/**
* Unit tests for ironic's HTTP interceptors.
*/
describe('Unit: OpenStack Ironic API Transformation Methods',
function() {
'use strict';
var ironicApiInterceptor;
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
ironicApiInterceptor = $injector.get('ironicApiInterceptor');
}));
/**
* A dummy response body for a valid object.
*/
var objectResponse = {
uuid: 'foo'
};
/**
* A dummy response object for a valid list of objects.
*/
var listResponse = {
resourceName: [
angular.copy(objectResponse)
]
};
/**
* A dummy error response.
*/
var errorResponse = {
error_message: {
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
}
};
var encodedErrorResponse = angular.copy(errorResponse);
encodedErrorResponse.error_message = angular.toJson(encodedErrorResponse.error_message);
/**
* Run all changes through the filter chain.
*
* @param {String} resourceName The name of the resource to resolve.
* @param {*} body The response data body to modify.
* @param {int} status The HTTP status code to return.
* @return {*} The modified body.
*/
function applyResponseTransformationChain (resourceName, body, status) {
status = status || 200;
var transformers = ironicApiInterceptor.response(resourceName);
for (var i = 0; i < transformers.length; i++) {
var transformer = transformers[i];
body = transformer(body, function() {
}, status);
}
return body;
}
it('should transform a regular object response',
function() {
var result = applyResponseTransformationChain(
null,
angular.toJson(objectResponse),
200
);
expect(result).toEqual(objectResponse);
});
it('should transform an object error response (twice)',
function() {
var result = applyResponseTransformationChain(
null,
angular.toJson(encodedErrorResponse),
400
);
expect(result).toEqual(errorResponse);
});
it('should transform a correct list response',
function() {
var result = applyResponseTransformationChain(
'resourceName',
angular.toJson(listResponse),
200
);
expect(result).toEqual(listResponse.resourceName);
});
it('should transform an error list response (twice)',
function() {
var result = applyResponseTransformationChain(
'resourceName',
angular.toJson(encodedErrorResponse),
400
);
expect(result).toEqual(errorResponse);
});
});

View File

@ -1,80 +0,0 @@
/**
* Unit tests for ironic's API Version Header.
*/
describe('Unit: OpenStack Ironic Version Header',
function() {
'use strict';
var $httpBackend;
// Load common configuration mocks.
beforeEach(module('openstack.mock.$$selectedConfiguration'));
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$httpBackend = $injector.get('$httpBackend');
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
it('should exist',
inject(function(ironicApiVersion) {
expect(ironicApiVersion).toBeDefined();
}));
it('should return 1.14',
inject(function(ironicApiVersion) {
expect(ironicApiVersion()).toBe('1.14');
}));
it('should be attached to all ironic API requests',
inject(function(IronicNode, IronicPort, IronicChassis, IronicDriver,
IronicDriverProperties) {
var expectedHeaders = {
'X-OpenStack-Ironic-API-Version': "1.14",
Accept: "application/json, text/plain, */*"
};
// Everything's a 404 response for simplicity's sake. We only care about the header.
$httpBackend
.expectGET('http://ironic.example.com:1000/nodes/fake',
expectedHeaders)
.respond(404, '');
$httpBackend
.expectGET('http://ironic.example.com:1000/ports/fake',
expectedHeaders)
.respond(404, '');
$httpBackend
.expectGET('http://ironic.example.com:1000/chassis/fake',
expectedHeaders)
.respond(404, '');
$httpBackend
.expectGET('http://ironic.example.com:1000/drivers/fake',
expectedHeaders)
.respond(404, '');
$httpBackend
.expectGET('http://ironic.example.com:1000/drivers/properties?driver_name=fake',
expectedHeaders)
.respond(404, '');
IronicNode.read({uuid: 'fake'});
IronicPort.read({uuid: 'fake'});
IronicChassis.read({uuid: 'fake'});
IronicDriver.read({uuid: 'fake'});
IronicDriverProperties.read({driver_name: 'fake'});
$httpBackend.flush();
}));
});

View File

@ -1,217 +0,0 @@
/**
* Unit tests for ironic's ngResource IronicChassis implementation.
*/
describe('Unit: OpenStack Ironic Chassis Resource',
function() {
'use strict';
var $rootScope, $httpBackend;
// Load common configuration mocks.
beforeEach(module('openstack.mock.$$configuration'));
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$rootScope = $injector.get('$rootScope');
$httpBackend = $injector.get('$httpBackend');
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
it('should implement a basic CRUD interface',
inject(function(IronicChassis) {
expect(IronicChassis.query).toBeDefined();
expect(IronicChassis.create).toBeDefined();
expect(IronicChassis.read).toBeDefined();
expect(IronicChassis.update).toBeDefined();
expect(IronicChassis.remove).toBeDefined();
}));
it('should switch API requests if the configuration changes.',
inject(function(IronicChassis, $$selectedConfiguration) {
// Select #1
var config1 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config1.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/chassis')
.respond(200, {chassis: [{}]});
var result1 = IronicChassis.query({});
expect(result1.$promise).toBeDefined();
expect(result1.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result1.$resolved).toBeTruthy();
expect(result1.length).toBe(1);
expect(result1.$promise.$$state.status).toBe(1);
// Switch configs.
var config2 = $$selectedConfiguration.set('test_config_2');
$rootScope.$apply();
expect(config2.ironic.apiRoot).toBe('http://ironic.example.com:2000');
// Try a request
$httpBackend.expect('GET', 'http://ironic.example.com:2000/chassis')
.respond(200, {chassis: [{}, {}]});
var result2 = IronicChassis.query({});
expect(result2.$promise).toBeDefined();
expect(result2.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result2.$resolved).toBeTruthy();
expect(result2.length).toBe(2);
expect(result2.$promise.$$state.status).toBe(1);
// Switch it back.
var config3 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config3.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expect('GET', 'http://ironic.example.com:1000/chassis')
.respond(200, {chassis: [{}]});
var result3 = IronicChassis.query({});
expect(result3.$promise).toBeDefined();
expect(result3.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result3.$resolved).toBeTruthy();
expect(result3.length).toBe(1);
expect(result3.$promise.$$state.status).toBe(1);
}));
it('should return a failed resource if an invalid config has been selected',
inject(function(IronicChassis) {
var queryResult = IronicChassis.query({id: 'meaningless'});
expect(angular.isArray(queryResult)).toBeTruthy();
expect(queryResult.$promise).toBeDefined();
expect(queryResult.$resolved).toBeFalsy();
var createResult = IronicChassis.create({id: 'meaningless'});
expect(angular.isObject(createResult)).toBeTruthy();
expect(createResult.$promise).toBeDefined();
expect(createResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(createResult.$resolved).toBeTruthy();
expect(createResult.$promise.$$state.status).toBe(2);
var updateResult = IronicChassis.update({id: 'meaningless'});
expect(angular.isObject(updateResult)).toBeTruthy();
expect(updateResult.$promise).toBeDefined();
expect(updateResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(updateResult.$resolved).toBeTruthy();
expect(updateResult.$promise.$$state.status).toBe(2);
var readResult = IronicChassis.read({id: 'meaningless'});
expect(angular.isObject(readResult)).toBeTruthy();
expect(readResult.$promise).toBeDefined();
expect(readResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(readResult.$resolved).toBeTruthy();
expect(readResult.$promise.$$state.status).toBe(2);
var removeResult = IronicChassis.remove({id: 'meaningless'});
expect(angular.isObject(removeResult)).toBeTruthy();
expect(removeResult.$promise).toBeDefined();
expect(removeResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(removeResult.$resolved).toBeTruthy();
expect(removeResult.$promise.$$state.status).toBe(2);
}));
it('should correctly parse query error responses',
inject(function(IronicChassis, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/chassis')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicChassis.query({});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
it('should correctly parse CRUD regular responses',
inject(function(IronicChassis, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/chassis/1')
.respond(200, {uuid: 1});
// Issue a request and attach a listener for the error response.
var result = IronicChassis.read({uuid: 1});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(1);
expect(result.uuid).toBe(1);
}));
it('should correctly parse CRUD error responses',
inject(function(IronicChassis, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/chassis/1')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicChassis.read({uuid: 1});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
});

View File

@ -1,217 +0,0 @@
/**
* Unit tests for ironic's ngResource IronicDriver implementation.
*/
describe('Unit: OpenStack Ironic Driver Resource',
function() {
'use strict';
var $rootScope, $httpBackend;
// Load common configuration mocks.
beforeEach(module('openstack.mock.$$configuration'));
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$rootScope = $injector.get('$rootScope');
$httpBackend = $injector.get('$httpBackend');
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
it('should implement a basic CRUD interface',
inject(function(IronicDriver) {
expect(IronicDriver.query).toBeDefined();
expect(IronicDriver.create).toBeDefined();
expect(IronicDriver.read).toBeDefined();
expect(IronicDriver.update).toBeDefined();
expect(IronicDriver.remove).toBeDefined();
}));
it('should switch API requests if the configuration changes.',
inject(function(IronicDriver, $$selectedConfiguration) {
// Select #1
var config1 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config1.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/drivers')
.respond(200, {drivers: [{}]});
var result1 = IronicDriver.query({});
expect(result1.$promise).toBeDefined();
expect(result1.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result1.$resolved).toBeTruthy();
expect(result1.length).toBe(1);
expect(result1.$promise.$$state.status).toBe(1);
// Switch configs.
var config2 = $$selectedConfiguration.set('test_config_2');
$rootScope.$apply();
expect(config2.ironic.apiRoot).toBe('http://ironic.example.com:2000');
// Try a request
$httpBackend.expect('GET', 'http://ironic.example.com:2000/drivers')
.respond(200, {drivers: [{}, {}]});
var result2 = IronicDriver.query({});
expect(result2.$promise).toBeDefined();
expect(result2.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result2.$resolved).toBeTruthy();
expect(result2.length).toBe(2);
expect(result2.$promise.$$state.status).toBe(1);
// Switch it back.
var config3 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config3.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expect('GET', 'http://ironic.example.com:1000/drivers')
.respond(200, {drivers: [{}]});
var result3 = IronicDriver.query({});
expect(result3.$promise).toBeDefined();
expect(result3.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result3.$resolved).toBeTruthy();
expect(result3.length).toBe(1);
expect(result3.$promise.$$state.status).toBe(1);
}));
it('should return a failed resource if an invalid config has been selected',
inject(function(IronicDriver) {
var queryResult = IronicDriver.query({id: 'meaningless'});
expect(angular.isArray(queryResult)).toBeTruthy();
expect(queryResult.$promise).toBeDefined();
expect(queryResult.$resolved).toBeFalsy();
var createResult = IronicDriver.create({id: 'meaningless'});
expect(angular.isObject(createResult)).toBeTruthy();
expect(createResult.$promise).toBeDefined();
expect(createResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(createResult.$resolved).toBeTruthy();
expect(createResult.$promise.$$state.status).toBe(2);
var updateResult = IronicDriver.update({id: 'meaningless'});
expect(angular.isObject(updateResult)).toBeTruthy();
expect(updateResult.$promise).toBeDefined();
expect(updateResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(updateResult.$resolved).toBeTruthy();
expect(updateResult.$promise.$$state.status).toBe(2);
var readResult = IronicDriver.read({id: 'meaningless'});
expect(angular.isObject(readResult)).toBeTruthy();
expect(readResult.$promise).toBeDefined();
expect(readResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(readResult.$resolved).toBeTruthy();
expect(readResult.$promise.$$state.status).toBe(2);
var removeResult = IronicDriver.remove({id: 'meaningless'});
expect(angular.isObject(removeResult)).toBeTruthy();
expect(removeResult.$promise).toBeDefined();
expect(removeResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(removeResult.$resolved).toBeTruthy();
expect(removeResult.$promise.$$state.status).toBe(2);
}));
it('should correctly parse query error responses',
inject(function(IronicDriver, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/drivers')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicDriver.query({});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
it('should correctly parse CRUD regular responses',
inject(function(IronicDriver, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/drivers/1')
.respond(200, {uuid: 1});
// Issue a request and attach a listener for the error response.
var result = IronicDriver.read({uuid: 1});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(1);
expect(result.uuid).toBe(1);
}));
it('should correctly parse CRUD error responses',
inject(function(IronicDriver, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/drivers/1')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicDriver.read({uuid: 1});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
});

View File

@ -1,183 +0,0 @@
/**
* Unit tests for ironic's ngResource Ironic Driver Properties implementation.
*/
describe('Unit: OpenStack Ironic Driver Properties Resource',
function() {
'use strict';
var $rootScope, $httpBackend;
// Create some test data
var testReturnData = {
foo: 'bar'
};
// Load common configuration mocks.
beforeEach(module('openstack.mock.$$configuration'));
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$rootScope = $injector.get('$rootScope');
$httpBackend = $injector.get('$httpBackend');
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
it('should implement a basic CRUD interface',
inject(function(IronicDriverProperties) {
expect(IronicDriverProperties.read).toBeDefined("must have read() method");
}));
it('should switch API requests if the configuration changes.',
inject(function(IronicDriverProperties, $$selectedConfiguration) {
// Select #1
var config1 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config1.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend
.expectGET('http://ironic.example.com:1000/drivers/properties?driver_name=foo')
.respond(200, testReturnData);
var result1 = IronicDriverProperties.read({driver_name: 'foo'});
expect(result1.$promise).toBeDefined();
expect(result1.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result1.$resolved).toBeTruthy();
expect(result1.$promise.$$state.status).toBe(1);
// Switch configs.
var config2 = $$selectedConfiguration.set('test_config_2');
$rootScope.$apply();
expect(config2.ironic.apiRoot).toBe('http://ironic.example.com:2000');
// Try a request
$httpBackend
.expect('GET', 'http://ironic.example.com:2000/drivers/properties?driver_name=foo')
.respond(200, testReturnData);
var result2 = IronicDriverProperties.read({driver_name: 'foo'});
expect(result2.$promise).toBeDefined();
expect(result2.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result2.$resolved).toBeTruthy();
expect(result2.$promise.$$state.status).toBe(1);
// Switch it back.
var config3 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config3.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend
.expect('GET', 'http://ironic.example.com:1000/drivers/properties?driver_name=foo')
.respond(200, testReturnData);
var result3 = IronicDriverProperties.read({driver_name: 'foo'});
expect(result3.$promise).toBeDefined();
expect(result3.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result3.$resolved).toBeTruthy();
expect(result3.$promise.$$state.status).toBe(1);
}));
it('should return a failed resource if an invalid config has been selected',
inject(function(IronicDriverProperties) {
var queryResult = IronicDriverProperties.read({driver_name: 'foo'});
expect(queryResult.$promise).toBeDefined();
expect(queryResult.$resolved).toBeFalsy();
}));
it('should correctly parse query error responses',
inject(function(IronicDriverProperties, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/drivers/properties?driver_name=foo')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicDriverProperties.read({driver_name: 'foo'});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
it('should correctly parse CRUD regular responses',
inject(function(IronicDriverProperties, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend
.expectGET('http://ironic.example.com:1000/drivers/properties?driver_name=foo')
.respond(200, testReturnData);
// Issue a request and attach a listener for the error response.
var result = IronicDriverProperties.read({driver_name: 'foo'});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(1);
expect(result.foo).toBe('bar');
}));
it('should correctly parse CRUD error responses',
inject(function(IronicDriverProperties, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend
.expectGET('http://ironic.example.com:1000/drivers/properties?driver_name=foo')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicDriverProperties.read({driver_name: 'foo'});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
});

View File

@ -1,216 +0,0 @@
/**
* Unit tests for ironic's ngResource IronicNode implementation.
*/
describe('Unit: OpenStack Ironic Node Resource',
function() {
'use strict';
var $rootScope, $httpBackend;
// Load common configuration mocks.
beforeEach(module('openstack.mock.$$configuration'));
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$rootScope = $injector.get('$rootScope');
$httpBackend = $injector.get('$httpBackend');
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
it('should implement a basic CRUD interface',
inject(function(IronicNode) {
expect(IronicNode.query).toBeDefined();
expect(IronicNode.create).toBeDefined();
expect(IronicNode.read).toBeDefined();
expect(IronicNode.update).toBeDefined();
expect(IronicNode.remove).toBeDefined();
}));
it('should switch API requests if the configuration changes.',
inject(function(IronicNode, $$selectedConfiguration) {
// Select #1
var config1 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config1.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/nodes')
.respond(200, {nodes: [{}]});
var result1 = IronicNode.query({});
expect(result1.$promise).toBeDefined();
expect(result1.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result1.$resolved).toBeTruthy();
expect(result1.length).toBe(1);
expect(result1.$promise.$$state.status).toBe(1);
// Switch configs.
var config2 = $$selectedConfiguration.set('test_config_2');
$rootScope.$apply();
expect(config2.ironic.apiRoot).toBe('http://ironic.example.com:2000');
// Try a request
$httpBackend.expect('GET', 'http://ironic.example.com:2000/nodes')
.respond(200, {nodes: [{}, {}]});
var result2 = IronicNode.query({});
expect(result2.$promise).toBeDefined();
expect(result2.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result2.$resolved).toBeTruthy();
expect(result2.length).toBe(2);
expect(result2.$promise.$$state.status).toBe(1);
// Switch it back.
var config3 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config3.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expect('GET', 'http://ironic.example.com:1000/nodes')
.respond(200, {nodes: [{}]});
var result3 = IronicNode.query({});
expect(result3.$promise).toBeDefined();
expect(result3.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result3.$resolved).toBeTruthy();
expect(result3.length).toBe(1);
expect(result3.$promise.$$state.status).toBe(1);
}));
it('should return a failed resource if an invalid config has been selected',
inject(function(IronicNode) {
var queryResult = IronicNode.query({id: 'meaningless'});
expect(angular.isArray(queryResult)).toBeTruthy();
expect(queryResult.$promise).toBeDefined();
expect(queryResult.$resolved).toBeFalsy();
var createResult = IronicNode.create({id: 'meaningless'});
expect(angular.isObject(createResult)).toBeTruthy();
expect(createResult.$promise).toBeDefined();
expect(createResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(createResult.$resolved).toBeTruthy();
expect(createResult.$promise.$$state.status).toBe(2);
var updateResult = IronicNode.update({id: 'meaningless'});
expect(angular.isObject(updateResult)).toBeTruthy();
expect(updateResult.$promise).toBeDefined();
expect(updateResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(updateResult.$resolved).toBeTruthy();
expect(updateResult.$promise.$$state.status).toBe(2);
var readResult = IronicNode.read({id: 'meaningless'});
expect(angular.isObject(readResult)).toBeTruthy();
expect(readResult.$promise).toBeDefined();
expect(readResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(readResult.$resolved).toBeTruthy();
expect(readResult.$promise.$$state.status).toBe(2);
var removeResult = IronicNode.remove({id: 'meaningless'});
expect(angular.isObject(removeResult)).toBeTruthy();
expect(removeResult.$promise).toBeDefined();
expect(removeResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(removeResult.$resolved).toBeTruthy();
expect(removeResult.$promise.$$state.status).toBe(2);
}));
it('should correctly parse query error responses',
inject(function(IronicNode, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/nodes')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicNode.query({});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
it('should correctly parse CRUD regular responses',
inject(function(IronicNode, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/nodes/1')
.respond(200, {uuid: 1});
// Issue a request and attach a listener for the error response.
var result = IronicNode.read({uuid: 1});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(1);
expect(result.uuid).toBe(1);
}));
it('should correctly parse CRUD error responses',
inject(function(IronicNode, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/nodes/1')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicNode.read({uuid: 1});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
});

View File

@ -1,117 +0,0 @@
/*
* Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Unit tests for ironic's ngResource IronicNode implementation.
*/
describe('Unit: OpenStack Ironic Node Resource',
function() {
'use strict';
var $rootScope;
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$rootScope = $injector.get('$rootScope');
}));
/**
* Assertion helper, make sure a list of transitions contains one we're looking for.
*
* @param {Array} transitions The transitions array to check.
* @param {String} from The name of the originating state.
* @param {String} actor The name of the actor.
* @param {String} action The name of the transition action name.
* @param {String} target The name of the target state.
* @return {void}
*/
function assertHasTransition (transitions, from, actor, action, target) {
expect(transitions).toContain({
from_state: from,
event: action,
target_state: target,
actor: actor
});
}
it('should implement the query method',
inject(function(IronicNodePowerTransition) {
expect(IronicNodePowerTransition.query).toBeDefined();
// CRUD methods not supported on this resource.
expect(IronicNodePowerTransition.create).not.toBeDefined();
expect(IronicNodePowerTransition.read).not.toBeDefined();
expect(IronicNodePowerTransition.update).not.toBeDefined();
expect(IronicNodePowerTransition.remove).not.toBeDefined();
}));
it('should return a "resource-like" array"',
inject(function(IronicNodePowerTransition) {
var transitions = IronicNodePowerTransition.query();
$rootScope.$apply();
expect(transitions.$resolved).toBeTruthy();
expect(transitions.$promise.$$state.status).toBe(1);
}));
it('should permit filtering.',
inject(function(IronicNodePowerTransition) {
var transitions = IronicNodePowerTransition.query({
from_state: 'power on'
});
$rootScope.$apply();
expect(transitions.length).toEqual(2);
var conductorTransitions = IronicNodePowerTransition.query({
from_state: 'rebooting',
actor: 'conductor'
});
$rootScope.$apply();
expect(conductorTransitions.length).toEqual(1);
}));
it('should permit query promise handlers.',
inject(function(IronicNodePowerTransition) {
var success = false;
IronicNodePowerTransition.query({
from_state: 'power on'
}, function() {
success = true;
});
$rootScope.$apply();
expect(success).toBeTruthy();
}));
it('should return the expected transition graph.',
inject(function(IronicNodePowerTransition) {
var transitions = IronicNodePowerTransition.query();
$rootScope.$apply();
var USER = "user";
var CONDUCTOR = "conductor";
// Power states
assertHasTransition(transitions, "power off", USER, "power on", "power on");
assertHasTransition(transitions, "power on", USER, "power off", "power off");
assertHasTransition(transitions, "power on", USER, "reboot", "rebooting");
assertHasTransition(transitions, "rebooting", CONDUCTOR, "power on", "power on");
}));
});

View File

@ -1,185 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Unit tests for Ironic's IronicNodeProvision resource.
*/
describe('IronicNodeProvision',
function() {
'use strict';
var $rootScope, $httpBackend;
// Load common configuration mocks.
beforeEach(module('openstack.mock.$$configuration'));
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$rootScope = $injector.get('$rootScope');
$httpBackend = $injector.get('$httpBackend');
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
describe('Interface', function() {
it('should implement the update method',
inject(function(IronicNodeProvision) {
expect(IronicNodeProvision.update).toBeDefined();
})
);
it('should not implement the query, create, read, or remove method',
inject(function(IronicNodeProvision) {
expect(IronicNodeProvision.query).not.toBeDefined();
expect(IronicNodeProvision.create).not.toBeDefined();
expect(IronicNodeProvision.read).not.toBeDefined();
expect(IronicNodeProvision.remove).not.toBeDefined();
})
);
});
describe('basic behavior', function() {
var testManageBody = {
node_ident: 'test_id',
target: 'manage'
};
it('should switch API requests if the configuration changes.',
inject(function(IronicNodeProvision, $$selectedConfiguration) {
// Select #1
var config1 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config1.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expectPUT('http://ironic.example.com:1000/nodes/test_id/states/provision')
.respond(202);
var result1 = IronicNodeProvision.update(testManageBody);
expect(result1.$promise).toBeDefined();
expect(result1.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result1.$resolved).toBeTruthy();
expect(result1.node_ident).toBe(testManageBody.node_ident);
expect(result1.$promise.$$state.status).toBe(1);
// Switch configs.
var config2 = $$selectedConfiguration.set('test_config_2');
$rootScope.$apply();
expect(config2.ironic.apiRoot).toBe('http://ironic.example.com:2000');
// Try a request
$httpBackend.expectPUT('http://ironic.example.com:2000/nodes/test_id/states/provision')
.respond(202);
var result2 = IronicNodeProvision.update(testManageBody);
expect(result2.$promise).toBeDefined();
expect(result2.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result2.$resolved).toBeTruthy();
expect(result2.node_ident).toBe(testManageBody.node_ident);
expect(result2.$promise.$$state.status).toBe(1);
// Switch it back.
var config3 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config3.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expectPUT('http://ironic.example.com:1000/nodes/test_id/states/provision')
.respond(202);
var result3 = IronicNodeProvision.update(testManageBody);
expect(result3.$promise).toBeDefined();
expect(result3.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result3.$resolved).toBeTruthy();
expect(result3.node_ident).toBe(testManageBody.node_ident);
expect(result3.$promise.$$state.status).toBe(1);
}));
it('should return a \'resource-like\' object',
inject(function(IronicNodeProvision, $$selectedConfiguration) {
// Select #1
var config1 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config1.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expectPUT('http://ironic.example.com:1000/nodes/test_id/states/provision')
.respond(202);
var result1 = IronicNodeProvision.update(testManageBody);
expect(result1.$promise).toBeDefined();
expect(result1.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result1.$resolved).toBeTruthy();
expect(result1.node_ident).toBe(testManageBody.node_ident);
expect(result1.$promise.$$state.status).toBe(1);
}));
it('should return a failed resource if an invalid config has been selected',
inject(function(IronicNodeProvision) {
var updateResult = IronicNodeProvision.update(testManageBody);
expect(angular.isObject(updateResult)).toBeTruthy();
expect(updateResult.$promise).toBeDefined();
expect(updateResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(updateResult.$resolved).toBeTruthy();
expect(updateResult.$promise.$$state.status).toBe(2);
}));
it('should correctly parse error responses',
inject(function(IronicNodeProvision, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectPUT('http://ironic.example.com:1000/nodes/test_id/states/provision')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicNodeProvision.update(testManageBody);
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
});
});

View File

@ -1,159 +0,0 @@
/**
* Unit tests for ironic's ngResource IronicNode implementation.
*/
describe('Unit: OpenStack Ironic Node Resource',
function() {
'use strict';
var $rootScope;
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$rootScope = $injector.get('$rootScope');
}));
/**
* Assertion helper, make sure a list of transitions contains one we're looking for.
*
* @param {Array} transitions The transitions array to check.
* @param {String} from The name of the originating state.
* @param {String} actor The name of the actor.
* @param {String} action The name of the transition action name.
* @param {String} target The name of the target state.
* @return {void}
*/
function assertHasTransition (transitions, from, actor, action, target) {
expect(transitions).toContain({
from_state: from,
event: action,
target_state: target,
actor: actor
});
}
it('should implement the query method',
inject(function(IronicNodeProvisionTransition) {
expect(IronicNodeProvisionTransition.query).toBeDefined();
// CRUD methods not supported on this resource.
expect(IronicNodeProvisionTransition.create).not.toBeDefined();
expect(IronicNodeProvisionTransition.read).not.toBeDefined();
expect(IronicNodeProvisionTransition.update).not.toBeDefined();
expect(IronicNodeProvisionTransition.remove).not.toBeDefined();
}));
it('should return a "resource-like" array"',
inject(function(IronicNodeProvisionTransition) {
var transitions = IronicNodeProvisionTransition.query();
$rootScope.$apply();
expect(transitions.$resolved).toBeTruthy();
expect(transitions.$promise.$$state.status).toBe(1);
}));
it('should permit filtering.',
inject(function(IronicNodeProvisionTransition) {
var transitions = IronicNodeProvisionTransition.query({
from_state: 'manageable'
});
$rootScope.$apply();
expect(transitions.length).toEqual(3);
var conductorTransitions = IronicNodeProvisionTransition.query({
from_state: 'manageable',
actor: 'conductor'
});
$rootScope.$apply();
expect(conductorTransitions.length).toEqual(0);
}));
it('should permit query promise handlers.',
inject(function(IronicNodeProvisionTransition) {
var success = false;
IronicNodeProvisionTransition.query({
from_state: 'manageable'
}, function() {
success = true;
});
$rootScope.$apply();
expect(success).toBeTruthy();
}));
it('should return the expected transition graph.',
inject(function(IronicNodeProvisionTransition) {
var transitions = IronicNodeProvisionTransition.query();
$rootScope.$apply();
var USER = "user";
var CONDUCTOR = "conductor";
// ENROLL state
assertHasTransition(transitions, "enroll", USER, "manage", "verifying");
// VERIFYING state
assertHasTransition(transitions, "verifying", CONDUCTOR, "done", "manageable");
assertHasTransition(transitions, "verifying", CONDUCTOR, "fail", "enroll");
// MANAGEABLE state
assertHasTransition(transitions, "manageable", USER, "provide", "cleaning");
assertHasTransition(transitions, "manageable", USER, "clean", "cleaning");
assertHasTransition(transitions, "manageable", USER, "inspect", "inspecting");
// INSPECTING state
assertHasTransition(transitions, "inspecting", CONDUCTOR, "done", "manageable");
assertHasTransition(transitions, "inspecting", CONDUCTOR, "fail", "inspect failed");
// INSPECT FAILED state
assertHasTransition(transitions, "inspect failed", USER, "inspect", "inspecting");
assertHasTransition(transitions, "inspect failed", USER, "manage", "manageable");
// CLEANING state
assertHasTransition(transitions, "cleaning", CONDUCTOR, "manage", "manageable");
assertHasTransition(transitions, "cleaning", CONDUCTOR, "wait", "clean wait");
assertHasTransition(transitions, "cleaning", CONDUCTOR, "fail", "clean failed");
assertHasTransition(transitions, "cleaning", CONDUCTOR, "done", "available");
// CLEAN WAIT state
assertHasTransition(transitions, "clean wait", CONDUCTOR, "resume", "cleaning");
assertHasTransition(transitions, "clean wait", CONDUCTOR, "abort", "clean failed");
assertHasTransition(transitions, "clean wait", CONDUCTOR, "fail", "clean failed");
// CLEAN FAILED state
assertHasTransition(transitions, "clean failed", USER, "manage", "manageable");
// AVAILABLE state
assertHasTransition(transitions, "available", USER, "manage", "manageable");
assertHasTransition(transitions, "available", USER, "active", "deploying");
// DEPLOYING state
assertHasTransition(transitions, "deploying", CONDUCTOR, "wait", "wait call-back");
assertHasTransition(transitions, "deploying", CONDUCTOR, "done", "active");
assertHasTransition(transitions, "deploying", CONDUCTOR, "fail", "deploy failed");
// ACTIVE state
assertHasTransition(transitions, "active", USER, "rebuild", "deploying");
assertHasTransition(transitions, "active", USER, "delete", "deleting");
// DEPLOY FAILED state
assertHasTransition(transitions, "deploy failed", USER, "rebuild", "deploying");
assertHasTransition(transitions, "deploy failed", USER, "active", "deploying");
assertHasTransition(transitions, "deploy failed", USER, "delete", "deleting");
// WAIT CALL_BACK state
assertHasTransition(transitions, "wait call-back", CONDUCTOR, "resume", "deploying");
assertHasTransition(transitions, "wait call-back", CONDUCTOR, "delete", "deleting");
// DELETING state
assertHasTransition(transitions, "deleting", CONDUCTOR, "error", "error");
assertHasTransition(transitions, "deleting", CONDUCTOR, "clean", "cleaning");
// ERROR state
assertHasTransition(transitions, "error", USER, "delete", "deleting");
assertHasTransition(transitions, "error", USER, "rebuild", "deploying");
}));
});

View File

@ -1,217 +0,0 @@
/**
* Unit tests for ironic's ngResource IronicPort implementation.
*/
describe('Unit: OpenStack Ironic Port Resource',
function() {
'use strict';
var $rootScope, $httpBackend;
// Load common configuration mocks.
beforeEach(module('openstack.mock.$$configuration'));
// We are testing the ironic.api module.
beforeEach(module('ironic.api'));
beforeEach(inject(function($injector) {
// Set up the mock http service
$rootScope = $injector.get('$rootScope');
$httpBackend = $injector.get('$httpBackend');
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
it('should implement a basic CRUD interface',
inject(function(IronicPort) {
expect(IronicPort.query).toBeDefined();
expect(IronicPort.create).toBeDefined();
expect(IronicPort.read).toBeDefined();
expect(IronicPort.update).toBeDefined();
expect(IronicPort.remove).toBeDefined();
}));
it('should switch API requests if the configuration changes.',
inject(function(IronicPort, $$selectedConfiguration) {
// Select #1
var config1 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config1.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/ports')
.respond(200, {ports: [{}]});
var result1 = IronicPort.query({});
expect(result1.$promise).toBeDefined();
expect(result1.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result1.$resolved).toBeTruthy();
expect(result1.length).toBe(1);
expect(result1.$promise.$$state.status).toBe(1);
// Switch configs.
var config2 = $$selectedConfiguration.set('test_config_2');
$rootScope.$apply();
expect(config2.ironic.apiRoot).toBe('http://ironic.example.com:2000');
// Try a request
$httpBackend.expect('GET', 'http://ironic.example.com:2000/ports')
.respond(200, {ports: [{}, {}]});
var result2 = IronicPort.query({});
expect(result2.$promise).toBeDefined();
expect(result2.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result2.$resolved).toBeTruthy();
expect(result2.length).toBe(2);
expect(result2.$promise.$$state.status).toBe(1);
// Switch it back.
var config3 = $$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
expect(config3.ironic.apiRoot).toBe('http://ironic.example.com:1000');
// Try a request
$httpBackend.expect('GET', 'http://ironic.example.com:1000/ports')
.respond(200, {ports: [{}]});
var result3 = IronicPort.query({});
expect(result3.$promise).toBeDefined();
expect(result3.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result3.$resolved).toBeTruthy();
expect(result3.length).toBe(1);
expect(result3.$promise.$$state.status).toBe(1);
}));
it('should return a failed resource if an invalid config has been selected',
inject(function(IronicPort) {
var queryResult = IronicPort.query({id: 'meaningless'});
expect(angular.isArray(queryResult)).toBeTruthy();
expect(queryResult.$promise).toBeDefined();
expect(queryResult.$resolved).toBeFalsy();
var createResult = IronicPort.create({id: 'meaningless'});
expect(angular.isObject(createResult)).toBeTruthy();
expect(createResult.$promise).toBeDefined();
expect(createResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(createResult.$resolved).toBeTruthy();
expect(createResult.$promise.$$state.status).toBe(2);
var updateResult = IronicPort.update({id: 'meaningless'});
expect(angular.isObject(updateResult)).toBeTruthy();
expect(updateResult.$promise).toBeDefined();
expect(updateResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(updateResult.$resolved).toBeTruthy();
expect(updateResult.$promise.$$state.status).toBe(2);
var readResult = IronicPort.read({id: 'meaningless'});
expect(angular.isObject(readResult)).toBeTruthy();
expect(readResult.$promise).toBeDefined();
expect(readResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(readResult.$resolved).toBeTruthy();
expect(readResult.$promise.$$state.status).toBe(2);
var removeResult = IronicPort.remove({id: 'meaningless'});
expect(angular.isObject(removeResult)).toBeTruthy();
expect(removeResult.$promise).toBeDefined();
expect(removeResult.$resolved).toBeFalsy();
$rootScope.$apply();
expect(removeResult.$resolved).toBeTruthy();
expect(removeResult.$promise.$$state.status).toBe(2);
}));
it('should correctly parse query error responses',
inject(function(IronicPort, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/ports')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicPort.query({});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
it('should correctly parse CRUD regular responses',
inject(function(IronicPort, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/ports/1')
.respond(200, {uuid: 1});
// Issue a request and attach a listener for the error response.
var result = IronicPort.read({uuid: 1});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(1);
expect(result.uuid).toBe(1);
}));
it('should correctly parse CRUD error responses',
inject(function(IronicPort, $$selectedConfiguration) {
$$selectedConfiguration.set('test_config_1');
$rootScope.$apply();
// Try a request
$httpBackend.expectGET('http://ironic.example.com:1000/ports/1')
.respond(400, {
error_message: angular.toJson({
debuginfo: null,
faultcode: 'Client',
faultstring: 'Test fault string'
})
});
// Issue a request and attach a listener for the error response.
var result = IronicPort.read({uuid: 1});
result.$promise.then(function() {
expect(true).toBeFalsy(); // This must fail, we're checking for an error.
}, function(error) {
expect(error.data.error_message.faultcode).toBe('Client');
});
// Check and resolve the promise.
expect(result.$promise).toBeDefined();
expect(result.$resolved).toBeFalsy();
$httpBackend.flush();
expect(result.$resolved).toBeTruthy();
expect(result.$promise.$$state.status).toBe(2);
}));
});

View File

@ -1,35 +0,0 @@
/**
* Unit tests for the ironic api module.
*/
describe('Unit: Ironic.API Module',
function() {
'use strict';
var $rootScope;
beforeEach(module('ironic.api'));
beforeEach(module(function($$configurationProvider) {
$$configurationProvider.$enableDefault(true);
}));
beforeEach(inject(function($injector) {
$rootScope = $injector.get('$rootScope');
}));
it('should exist', function() {
expect(angular.module('ironic.api')).toBeTruthy();
});
it('should automatically register a root API endpoint with $$configuration',
inject(function($$configuration) {
var config = $$configuration.read('default');
$rootScope.$apply();
expect(config.id).toBe('default');
expect(config.ironic).toBeDefined();
expect(config.ironic.apiRoot).toBeDefined();
// Localhost is where we're running these tests.
expect(config.ironic.apiRoot).toBe('http://localhost:6385');
})
);
});

View File

@ -1,364 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Unit tests for the provision action modal controller.
*/
describe('ProvisionActionModalController',
function() {
'use strict';
var $controller, $httpBackend, mockInjectionProperties, $rootScope;
beforeEach(function() {
module('ironic.api.mock.IronicNode');
module('template.mock');
module('ironic');
});
beforeEach(inject(function(_$controller_, $injector) {
$httpBackend = $injector.get('$httpBackend');
$rootScope = $injector.get('$rootScope');
$controller = _$controller_;
mockInjectionProperties = {
$scope: {},
$uibModalInstance: {
close: function() {
},
dismiss: function() {
}
},
actionName: 'manage',
nodeIds: ['test_node_1', 'test_node_2']
};
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
describe('Controller Properties', function() {
afterEach(function() {
$httpBackend.flush();
});
it('does not pollute the $scope',
function() {
$controller('ProvisionActionModalController', mockInjectionProperties);
expect(mockInjectionProperties.$scope).toEqual({});
});
it('starts with the updating flag disabled',
function() {
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
expect(controller.updating).toBeFalsy();
});
it('starts with the someUpdated flag disabled',
function() {
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
expect(controller.someUpdated).toBeFalsy();
});
it('starts with the someNodesExcluded flag disabled',
function() {
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
expect(controller.someNodesExcluded).toBeFalsy();
});
it('starts with an empty, unresolved list of nodes',
function() {
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
expect(controller.nodes).toBeDefined();
expect(controller.nodes.$resolved).toBeFalsy();
});
it('Applies the action name to the controller scope',
function() {
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
expect(controller.actionName).toBe('manage');
});
});
describe('Controller Initialization', function() {
it('Initializes the controller with a resolving list of nodes', function() {
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
expect(controller.nodes).toBeDefined();
expect(controller.nodes.length).toBe(0);
expect(controller.nodes.$resolved).toBeFalsy();
expect(controller.nodes.$promise).toBeDefined();
$httpBackend.flush();
expect(controller.nodes.length).toBe(2);
expect(controller.nodes.$resolved).toBeTruthy();
expect(controller.someNodesExcluded).toBeFalsy();
});
it('Creates a context object for each passed node',
function() {
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
expect(controller.nodes.length).toBe(2);
angular.forEach(controller.nodes, function(ctx) {
expect(ctx.node).toBeDefined();
expect(ctx.error).toBeNull();
expect(ctx.changed).toBe(false);
expect(ctx.state).toBe('ready');
});
});
it('Should not activate the someExcluded flag if all nodes are valid',
function() {
mockInjectionProperties.nodeIds = ['test_node_1', 'test_node_2'];
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
expect(controller.nodes.length).toBe(2);
expect(controller.someNodesExcluded).toBeFalsy();
});
it('Should activate the someExcluded flag if invalid node states are detected',
function() {
mockInjectionProperties.nodeIds = ['test_node_1', 'test_node_3'];
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
expect(controller.nodes.length).toBe(1);
expect(controller.someNodesExcluded).toBeTruthy();
});
});
describe('apply()', function() {
var mockError = {
error_message: {
faultstring: "faultstring",
faultcode: "faultcode"
}
};
it('modifies nothing if invoked with no nodes.',
function() {
mockInjectionProperties.nodeIds = [];
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeFalsy();
controller.apply();
$rootScope.$apply(); // Resolve promises
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeFalsy();
});
it('dismisses the modal if invoked with no nodes.',
function() {
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
mockInjectionProperties.nodeIds = [];
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeFalsy();
controller.apply();
expect(controller.updating).toBeTruthy();
$rootScope.$apply();
expect(controller.updating).toBeFalsy();
expect(spyDismiss.calls.count()).toEqual(1);
});
it('correctly flips the updating flag',
function() {
// Preload the controller
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_1/states/provision')
.respond(202);
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_2/states/provision')
.respond(202);
expect(controller.updating).toBeFalsy();
controller.apply();
expect(controller.updating).toBeTruthy();
$httpBackend.flush();
expect(controller.updating).toBeFalsy();
});
it('flips the someUpdated flag if some nodes are updated and others are not',
function() {
// Preload the controller
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var spyClose = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_1/states/provision')
.respond(400, {});
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_2/states/provision')
.respond(202);
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeFalsy();
controller.apply();
expect(controller.nodes[0].state).toBe('updating');
expect(controller.nodes[1].state).toBe('updating');
expect(controller.updating).toBeTruthy();
$httpBackend.flush();
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeTruthy();
expect(controller.nodes[0].state).toBe('error');
expect(controller.nodes[1].state).toBe('complete');
expect(spyDismiss.calls.count()).toEqual(0);
expect(spyClose.calls.count()).toEqual(0);
});
it('changes a node\'s context state to "updating" and "complete".',
function() {
// Preload the controller
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_1/states/provision')
.respond(202);
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_2/states/provision')
.respond(202);
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeFalsy();
controller.apply();
expect(controller.nodes[0].state).toBe('updating');
expect(controller.nodes[1].state).toBe('updating');
expect(controller.updating).toBeTruthy();
$httpBackend.flush();
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeTruthy();
expect(controller.nodes[0].state).toBe('complete');
expect(controller.nodes[1].state).toBe('complete');
});
it('Correctly reports a returned error if a request fails.',
function() {
// Preload the controller
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_1/states/provision')
.respond(400, mockError);
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_2/states/provision')
.respond(400, mockError);
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeFalsy();
controller.apply();
expect(controller.nodes[0].state).toBe('updating');
expect(controller.nodes[1].state).toBe('updating');
expect(controller.updating).toBeTruthy();
$httpBackend.flush();
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeFalsy();
expect(controller.nodes[0].state).toBe('error');
expect(controller.nodes[0].error).toBeDefined();
expect(controller.nodes[1].state).toBe('error');
expect(controller.nodes[1].error).toBeDefined();
});
it('invokes $uibModalInstance.close() if all nodes have been updated.',
function() {
// Preload the controller
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var spyClose = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_1/states/provision')
.respond(202);
$httpBackend
.expectPUT('http://ironic.example.com:1000/nodes/test_node_2/states/provision')
.respond(202);
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeFalsy();
controller.apply();
expect(controller.nodes[0].state).toBe('updating');
expect(controller.nodes[1].state).toBe('updating');
expect(controller.updating).toBeTruthy();
$httpBackend.flush();
expect(controller.updating).toBeFalsy();
expect(controller.someUpdated).toBeTruthy();
expect(controller.nodes[0].state).toBe('complete');
expect(controller.nodes[1].state).toBe('complete');
expect(spyDismiss.calls.count()).toEqual(0);
expect(spyClose.calls.count()).toEqual(1);
expect(spyClose.calls.mostRecent().args[0][0]).toEqual(controller.nodes[0].node.uuid);
expect(spyClose.calls.mostRecent().args[0][1]).toEqual(controller.nodes[1].node.uuid);
});
});
describe('close()', function() {
it('invokes dismiss() if nothing updated',
function() {
// Preload the controller
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var spyClose = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
controller.someUpdated = false;
controller.close();
expect(spyDismiss.calls.count()).toEqual(1);
expect(spyClose.calls.count()).toEqual(0);
});
it('invokes close() with updated nodes if something updated',
function() {
// Preload the controller
var controller = $controller('ProvisionActionModalController', mockInjectionProperties);
$httpBackend.flush();
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var spyClose = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
controller.someUpdated = true;
controller.nodes[0].state = 'complete';
controller.nodes[0].changed = true;
controller.close();
expect(spyDismiss.calls.count()).toEqual(0);
expect(spyClose.calls.count()).toEqual(1);
expect(spyClose.calls.mostRecent().args[0]).toEqual([controller.nodes[0].node.uuid]);
});
});
});

View File

@ -1,262 +0,0 @@
/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Unit tests for the RemoveNodeModalController.
*/
describe('RemoveNodeModalController',
function() {
'use strict';
var $controller, $httpBackend, mockInjectionProperties, $rootScope, $q;
beforeEach(function() {
module('ironic.api.mock.IronicNode');
module('template.mock');
module('ironic');
mockInjectionProperties = {
$scope: {},
$uibModalInstance: {
close: function() {
},
dismiss: function() {
}
},
nodes: []
};
});
beforeEach(inject(function(_$controller_, $injector) {
$httpBackend = $injector.get('$httpBackend');
$rootScope = $injector.get('$rootScope');
$q = $injector.get('$q');
$controller = _$controller_;
}));
afterEach(inject(function($$persistentStorage) {
// Clear any config selections we've made.
$$persistentStorage.remove('$$selectedConfiguration');
// Assert no outstanding requests.
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));
describe('Controller Properties', function() {
it('does not pollute the $scope',
function() {
$controller('RemoveNodeModalController', mockInjectionProperties);
expect(mockInjectionProperties.$scope).toEqual({});
});
it('starts not deleting anything',
function() {
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.deleting).toBeFalsy();
});
it('starts not having deleted anything',
function() {
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.someDeleted).toBeFalsy();
});
it('Creates a scope object for each passed node',
function() {
mockInjectionProperties.nodes = [{node: 'node1'}, {node: 'node2'}];
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.nodes.length).toBe(2);
angular.forEach(controller.nodes, function(ctx) {
expect(ctx.node).toBeDefined();
expect(ctx.error).toBeNull();
expect(ctx.state).toBe('present');
});
});
});
describe('close()', function() {
it('invokes dismiss() if nothing deleted',
function() {
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var spyClose = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
controller.someDeleted = false;
controller.close();
expect(spyDismiss.calls.count()).toEqual(1);
expect(spyClose.calls.count()).toEqual(0);
});
it('invokes close() if something deleted',
function() {
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var spyClose = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
controller.someDeleted = true;
controller.close();
expect(spyDismiss.calls.count()).toEqual(0);
expect(spyClose.calls.count()).toEqual(1);
});
});
describe('remove()', function() {
var mockError = {
data: {
error_message: {
faultstring: "faultstring",
faultcode: "faultcode"
}
}
};
function removeMock (success) {
return function(successHandler, failureHandler) {
var promise = success ? $q.resolve() : $q.reject(mockError);
promise.then(successHandler, failureHandler);
return promise;
};
}
it('deletes nothing if invoked with no nodes.',
function() {
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeFalsy();
controller.remove();
$rootScope.$apply(); // Resolve promises
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeFalsy();
});
it('dismisses the modal if invoked with no nodes..',
function() {
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeFalsy();
controller.remove();
expect(controller.deleting).toBeTruthy();
$rootScope.$apply(); // Resolve promises
expect(controller.deleting).toBeFalsy();
expect(spyDismiss.calls.count()).toEqual(1);
});
it('correctly flips the deleting flag',
function() {
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.deleting).toBeFalsy();
controller.remove();
expect(controller.deleting).toBeTruthy();
$rootScope.$apply(); // Resolve promises
expect(controller.deleting).toBeFalsy();
});
it('flips the someDeleted flag if some nodes are deleted and others are not',
function() {
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var spyClose = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
mockInjectionProperties.nodes = [
{$remove: removeMock(true)},
{$remove: removeMock(false)}
];
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeFalsy();
controller.remove();
expect(controller.nodes[0].state).toBe('removing');
expect(controller.nodes[1].state).toBe('removing');
expect(controller.deleting).toBeTruthy();
$rootScope.$apply();
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeTruthy();
expect(controller.nodes[0].state).toBe('removed');
expect(controller.nodes[1].state).toBe('error');
expect(spyDismiss.calls.count()).toEqual(0);
expect(spyClose.calls.count()).toEqual(0);
});
it('changes a node\'s context state to "removing" and "removed".',
function() {
mockInjectionProperties.nodes = [
{$remove: removeMock(true)}
];
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeFalsy();
controller.remove();
expect(controller.nodes[0].state).toBe('removing');
expect(controller.deleting).toBeTruthy();
$rootScope.$apply();
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeTruthy();
expect(controller.nodes[0].state).toBe('removed');
});
it('Correctly reports a returned error if a node is not deleted.',
function() {
mockInjectionProperties.nodes = [
{$remove: removeMock(false)}
];
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeFalsy();
controller.remove();
expect(controller.nodes[0].state).toBe('removing');
expect(controller.deleting).toBeTruthy();
$rootScope.$apply();
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeFalsy();
expect(controller.nodes[0].state).toBe('error');
expect(controller.nodes[0].error).toBeDefined();
});
it('invokes $uibModalInstance.close() if all nodes have been deleted.',
function() {
var spyDismiss = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var spyClose = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
mockInjectionProperties.nodes = [
{$remove: removeMock(true)},
{$remove: removeMock(true)}
];
var controller = $controller('RemoveNodeModalController', mockInjectionProperties);
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeFalsy();
controller.remove();
expect(controller.nodes[0].state).toBe('removing');
expect(controller.nodes[1].state).toBe('removing');
expect(controller.deleting).toBeTruthy();
$rootScope.$apply();
expect(controller.deleting).toBeFalsy();
expect(controller.someDeleted).toBeTruthy();
expect(controller.nodes[0].state).toBe('removed');
expect(controller.nodes[1].state).toBe('removed');
expect(spyDismiss.calls.count()).toEqual(0);
expect(spyClose.calls.count()).toEqual(1);
});
});
});

View File

@ -1,73 +0,0 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Unit tests for the unknown action modal controller.
*/
describe('Unit: Ironic-webclient UnknownActionModalController',
function() {
'use strict';
var $controller;
var mockInjectionProperties = {
$scope: {},
$uibModalInstance: {
close: function() {
},
dismiss: function() {
}
},
actionName: 'test'
};
beforeEach(function() {
module('template.mock');
module('ironic');
});
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
}));
describe('Controller Properties', function() {
it('does not pollute the $scope',
function() {
$controller('UnknownActionModalController', mockInjectionProperties);
expect(mockInjectionProperties.$scope).toEqual({});
});
});
describe('Controller Initialization', function() {
it('passes the actionName to the controller scope',
function() {
var controller = $controller('UnknownActionModalController', mockInjectionProperties);
expect(controller.actionName).toEqual('test');
});
});
describe('$scope.close', function() {
it('calls dismiss when close() is called.',
function() {
var spy = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var controller = $controller('UnknownActionModalController', mockInjectionProperties);
controller.close();
expect(spy).toHaveBeenCalled();
expect(spy.calls.count()).toEqual(1);
});
});
});

View File

@ -1,98 +0,0 @@
/**
* Unit tests for the application root module.
*/
describe('Unit: Ironic-webclient Add-Configuration Controller',
function() {
'use strict';
var $controller;
var mockInjectionProperties = {
$scope: {},
$uibModalInstance: {
close: function() {
},
dismiss: function() {
}
},
configuration: [{id: 'test'}]
};
beforeEach(function() {
// Load the ironic module.
module('ironic');
// Load the controller provider.
inject(function(_$controller_) {
$controller = _$controller_;
});
});
describe('Controller Properties', function() {
it('does not pollute the $scope', function() {
var $scope = {};
$controller('ConfigurationAddController', mockInjectionProperties);
expect($scope).toEqual({});
});
it('sets the configuration property to the injected configuration',
function() {
var controller = $controller('ConfigurationAddController', mockInjectionProperties);
expect(controller.configuration).toBe(mockInjectionProperties.configuration);
});
it('sets the newConfiguration property to a valid cloud configuration with an ironic apiBase',
function() {
var controller = $controller('ConfigurationAddController', mockInjectionProperties);
expect(controller.newConfiguration.ironic).toEqual({apiRoot: ''});
expect(controller.newConfiguration.name).toBeDefined();
});
it('sets the $scope.location to a hash of the current URL',
inject(function($location) {
spyOn($location, 'host').and.returnValue('example.com');
spyOn($location, 'protocol').and.returnValue('https');
spyOn($location, 'port').and.returnValue(1234);
var controller = $controller('ConfigurationAddController', mockInjectionProperties);
expect(controller.location).toEqual({
host: 'example.com',
protocol: 'https',
port: 1234
});
}));
});
describe('$scope.close', function() {
it('calls dismiss when close() is called.',
function() {
var spy = spyOn(mockInjectionProperties.$uibModalInstance, 'dismiss');
var controller = $controller('ConfigurationAddController', mockInjectionProperties);
controller.close();
expect(spy).toHaveBeenCalled();
expect(spy.calls.count()).toEqual(1);
});
});
describe('$scope.save', function() {
it('resolves the modal promise with a valid configuration when close() is called',
function() {
var spy = spyOn(mockInjectionProperties.$uibModalInstance, 'close');
var controller = $controller('ConfigurationAddController', mockInjectionProperties);
// Simulate form input
controller.newConfiguration.name = 'Test Name';
controller.save();
expect(spy).toHaveBeenCalled();
// The ID should have been automatically populated from the name, in order to ensure
// the existence of a key.
expect(spy.calls.mostRecent().args[0]).toEqual({
id: 'Test Name',
name: 'Test Name',
ironic: {
apiRoot: ''
}
});
});
});
});

Some files were not shown because too many files have changed in this diff Show More