Compare commits
139 Commits
3.3.1
...
xo-web/v3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
261c9e2240 | ||
|
|
c03973867c | ||
|
|
ca6984f4a5 | ||
|
|
4fbc2680ba | ||
|
|
9a5b0a8104 | ||
|
|
ad998f7a63 | ||
|
|
91043e4b52 | ||
|
|
8f3bd0145b | ||
|
|
ace0051147 | ||
|
|
83720c6673 | ||
|
|
f1ce4de1eb | ||
|
|
013b23c918 | ||
|
|
d03debf59c | ||
|
|
00b55d893a | ||
|
|
085b92df9c | ||
|
|
8516ff7cb3 | ||
|
|
56d64d8adc | ||
|
|
5d68e48312 | ||
|
|
8b0be0a8c5 | ||
|
|
d6e4d6d927 | ||
|
|
bbfe60ffd2 | ||
|
|
c9cc0b5ce3 | ||
|
|
f0bf19f286 | ||
|
|
6492e55954 | ||
|
|
c9fd1d5833 | ||
|
|
e8f73cae6c | ||
|
|
7d436b40d0 | ||
|
|
55cd6d2a74 | ||
|
|
01a2585edf | ||
|
|
bb329b73ef | ||
|
|
9b4008f39c | ||
|
|
ef52f20993 | ||
|
|
f2a207e790 | ||
|
|
40f502492b | ||
|
|
c5f1cf94ac | ||
|
|
2e286d4a50 | ||
|
|
94a27fa79e | ||
|
|
0adec1dbe2 | ||
|
|
c38c53957d | ||
|
|
498eb0134e | ||
|
|
d34f9182c2 | ||
|
|
324f21cf3d | ||
|
|
200c0110fd | ||
|
|
5694b0054b | ||
|
|
8825fdcdaf | ||
|
|
7805bc172e | ||
|
|
aa321b4b22 | ||
|
|
c74df7abc5 | ||
|
|
c6ffe8978d | ||
|
|
6761e0df15 | ||
|
|
cc47225f24 | ||
|
|
58c47a325f | ||
|
|
706f7f3b7b | ||
|
|
4d67f8bd0b | ||
|
|
d5227e00ff | ||
|
|
62fa1d570c | ||
|
|
899b4ab29a | ||
|
|
a2801f0b7b | ||
|
|
84694fedef | ||
|
|
c0005741bd | ||
|
|
150a341510 | ||
|
|
e486713026 | ||
|
|
0201dfa8e3 | ||
|
|
78269a1b7b | ||
|
|
d1bf60ab17 | ||
|
|
6c368dc8cb | ||
|
|
10ac67c1e0 | ||
|
|
ba3b38d941 | ||
|
|
47044d93d3 | ||
|
|
423ac4ea04 | ||
|
|
b114b40f88 | ||
|
|
030ce18d65 | ||
|
|
44116b3559 | ||
|
|
78332c7bfd | ||
|
|
dd8ac28240 | ||
|
|
81818222e5 | ||
|
|
4a25af03f4 | ||
|
|
6515d73534 | ||
|
|
006fc9acd3 | ||
|
|
1aa878b3a6 | ||
|
|
23dc475905 | ||
|
|
a827ab6ffd | ||
|
|
f55784ce6a | ||
|
|
f7110ac472 | ||
|
|
52a94e0f47 | ||
|
|
c25eb678a8 | ||
|
|
5128efbbf4 | ||
|
|
c73ee3c531 | ||
|
|
54ba67f882 | ||
|
|
395bfe6eb5 | ||
|
|
e0ecf65aac | ||
|
|
a7c651c98c | ||
|
|
ed473f457a | ||
|
|
a572c1132d | ||
|
|
ba314c7135 | ||
|
|
a0788ab050 | ||
|
|
8885f0be10 | ||
|
|
9d433ed1d0 | ||
|
|
0125c9445e | ||
|
|
a3454d4143 | ||
|
|
ad3c224b10 | ||
|
|
43d186d464 | ||
|
|
a3671e776d | ||
|
|
862ce07f8d | ||
|
|
a78bc7d195 | ||
|
|
b0ce4404ea | ||
|
|
cd5641d2f3 | ||
|
|
51dd9f0419 | ||
|
|
6d0a76756d | ||
|
|
6e8342fbf4 | ||
|
|
8d227724d7 | ||
|
|
eab91ffa83 | ||
|
|
6488198a06 | ||
|
|
07b4c67b5c | ||
|
|
70077a1eac | ||
|
|
03763a6556 | ||
|
|
3cb8d77977 | ||
|
|
c8d7940451 | ||
|
|
2253aa1229 | ||
|
|
d5a16855fb | ||
|
|
1aec909c10 | ||
|
|
7b60980959 | ||
|
|
097424e2ee | ||
|
|
a99b17d240 | ||
|
|
7526230b69 | ||
|
|
6b8f9a6319 | ||
|
|
8e74556613 | ||
|
|
26a3086b81 | ||
|
|
6dd70137db | ||
|
|
94ab475a31 | ||
|
|
e77b6f3b85 | ||
|
|
4000b44d90 | ||
|
|
ad09d98d81 | ||
|
|
567d5e7676 | ||
|
|
123b0a331e | ||
|
|
11f1f3d132 | ||
|
|
a44ce0c538 | ||
|
|
23348c5d8c | ||
|
|
176c6524a2 |
138
.jshintrc
@@ -1,24 +1,118 @@
|
||||
{
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"esnext": true,
|
||||
"bitwise": true,
|
||||
"camelcase": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"immed": true,
|
||||
"indent": 2,
|
||||
"latedef": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"quotmark": "single",
|
||||
"regexp": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"strict": true,
|
||||
"trailing": true,
|
||||
"smarttabs": true,
|
||||
"globals": {
|
||||
"angular": false
|
||||
}
|
||||
// --------------------------------------------------------------------
|
||||
// JSHint Configuration, Node.js Edition
|
||||
// --------------------------------------------------------------------
|
||||
//
|
||||
// This is an options template for [JSHint][1], forked from
|
||||
// haschek's [JSHint template][2]:
|
||||
//
|
||||
// * the environment has been changed to `node`;
|
||||
// * recent options were added;
|
||||
// * coding style has been adapted to node (e.g. 2 spaces
|
||||
// indenting, global use strict).
|
||||
//
|
||||
// [1]: http://www.jshint.com/
|
||||
// [2]: https://gist.github.com/haschek/2595796
|
||||
//
|
||||
// @author Julien Fontanet <julien.fontanet@isonoe.net>
|
||||
// @license http://unlicense.org/
|
||||
|
||||
// == Enforcing Options ===============================================
|
||||
//
|
||||
// These options tell JSHint to be more strict towards your code. Use
|
||||
// them if you want to allow only a safe subset of JavaScript, very
|
||||
// useful when your codebase is shared with a big number of developers
|
||||
// with different skill levels.
|
||||
|
||||
"bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.).
|
||||
"camelcase" : true, // Require variable names to use either camelCase or UPPER_CASE styles.
|
||||
"curly" : true, // Require {} for every new block or scope.
|
||||
"eqeqeq" : true, // Require triple equals i.e. `===`.
|
||||
"forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`.
|
||||
"freeze" : true, // Prohibit modification of native objects' prototypes.
|
||||
"immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
|
||||
"indent" : 2, // Specify indentation spacing
|
||||
"latedef" : true, // Prohibit variable use before definition.
|
||||
"newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
|
||||
"noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
|
||||
"noempty" : true, // Prohibit use of empty blocks.
|
||||
"nonew" : true, // Prohibit use of constructors for side-effects.
|
||||
"plusplus" : false, // Prohibit the use of `++` & `--`.
|
||||
"quotmark" : "'", // Require single quotes.
|
||||
"undef" : true, // Require all non-global variables be declared before they are used.
|
||||
"unused" : true, // Prohibit unused variables.
|
||||
"strict" : true, // Require `use strict` pragma in every function.
|
||||
"trailing" : true, // Prohibit trailing whitespaces.
|
||||
"maxparams" : 4, // Prohibit more than 4 parameters per function definition.
|
||||
"maxdepth" : 3, // Prohibit nesting more than 3 control blocks.
|
||||
"maxstatements" : 20, // Prohibit more than 20 statements per function.
|
||||
"maxcomplexity" : 7, // Prohibit having to much branches in your code.
|
||||
"maxlen" : 80, // Prohibit line with more than 80 characters.
|
||||
|
||||
// == Relaxing Options ================================================
|
||||
//
|
||||
// These options allow you to suppress certain types of warnings. Use
|
||||
// them only if you are absolutely positive that you know what you are
|
||||
// doing.
|
||||
|
||||
"asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons).
|
||||
"boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
|
||||
"debug" : false, // Allow debugger statements e.g. browser breakpoints.
|
||||
"eqnull" : false, // Tolerate use of `== null`.
|
||||
"esnext" : false, // Allow ES.next specific features such as `const` and `let`.
|
||||
"evil" : false, // Tolerate use of `eval`.
|
||||
"expr" : true, // Tolerate `ExpressionStatement` as Programs. (Allowed for Mocha.)
|
||||
"funcscope" : false, // Tolerate declarations of variables inside of control structures while accessing them later from the outside.
|
||||
"gcl" : false, // Makes JSHint compatible with Google Closure Compiler.
|
||||
"globalstrict" : true, // Allow global "use strict" (also enables 'strict').
|
||||
"iterator" : false, // Allow usage of __iterator__ property.
|
||||
"lastsemic" : false, // Tolerate missing semicolons when the it is omitted for the last statement in a one-line block.
|
||||
"laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
|
||||
"laxcomma" : false, // Suppress warnings about comma-first coding style.
|
||||
"loopfunc" : false, // Allow functions to be defined within loops.
|
||||
"maxerr" : 50, // Maximum errors before stopping.
|
||||
"moz" : false, // Tolerate Mozilla JavaScript extensions.
|
||||
"notypeof" : false, // Tolerate invalid typeof values.
|
||||
"multistr" : false, // Tolerate multi-line strings.
|
||||
"proto" : false, // Tolerate __proto__ property. This property is deprecated.
|
||||
"scripturl" : false, // Tolerate script-targeted URLs.
|
||||
"smarttabs" : false, // Tolerate mixed tabs and spaces when the latter are used for alignment only.
|
||||
"shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
|
||||
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
|
||||
"supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
|
||||
"validthis" : false, // Tolerate strict violations when the code is running in strict mode and you use this in a non-constructor function.
|
||||
|
||||
// == Environments ====================================================
|
||||
//
|
||||
// These options pre-define global variables that are exposed by
|
||||
// popular JavaScript libraries and runtime environments—such as
|
||||
// browser or node.js.
|
||||
|
||||
"browser" : false, // Standard browser globals e.g. `window`, `document`.
|
||||
"couch" : false, // Enable globals exposed by CouchDB.
|
||||
"devel" : false, // Allow development statements e.g. `console.log();`.
|
||||
"dojo" : false, // Enable globals exposed by Dojo Toolkit.
|
||||
"jquery" : false, // Enable globals exposed by jQuery JavaScript library.
|
||||
"mocha" : true, // Enable globals exposed by the mocha test runner.
|
||||
"mootools" : false, // Enable globals exposed by MooTools JavaScript framework.
|
||||
"node" : true, // Enable globals available when code is running inside of the NodeJS runtime environment.
|
||||
"nonstandard" : false, // Define non-standard but widely adopted globals such as escape and unescape.
|
||||
"phantom" : false, // Enable globals exposed by PhantomJS.
|
||||
"prototypejs" : false, // Enable globals exposed by Prototype JavaScript framework.
|
||||
"rhino" : false, // Enable globals available when your code is running inside of the Rhino runtime environment.
|
||||
"worker" : false, // Enable globals exposed when running inside a Web Worker.
|
||||
"wsh" : false, // Enable globals available when your code is running as a script for the Windows Script Host.
|
||||
"yui" : false, // Enable globals exposed by YUI.
|
||||
|
||||
// == JSLint Legacy ===================================================
|
||||
//
|
||||
// These options are legacy from JSLint. Aside from bug fixes they will
|
||||
// not be improved in any way and might be removed at any point.
|
||||
|
||||
"nomen" : false, // Prohibit use of initial or trailing underbars in names.
|
||||
"onevar" : false, // Allow only one `var` statement per function.
|
||||
"passfail" : false, // Stop on first error.
|
||||
"white" : false, // Check against strict whitespace and indentation rules.
|
||||
|
||||
"globals": {}
|
||||
}
|
||||
|
||||
24
CHANGELOG.md
@@ -1,26 +1,42 @@
|
||||
# ChangeLog
|
||||
|
||||
## **3.4.0** (2014-05-22)
|
||||
|
||||
*Highlight in this release is the new events system between XO-Web
|
||||
and XO-Server which results in less bandwidth consumption as well as
|
||||
better performance and reactivity.*
|
||||
|
||||
### Enhancements
|
||||
|
||||
- events system between XO-Web and XO-Server ([#52](https://github.com/vatesfr/xo-web/issues/52))
|
||||
- ability to clone/copy a VM ([#116](https://github.com/vatesfr/xo-web/issues/116))
|
||||
- mandatory log in page ([#120](https://github.com/vatesfr/xo-web/issues/120))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- failure in VM creation ([#111](https://github.com/vatesfr/xo-web/issues/111))
|
||||
|
||||
## **3.3.1** (2014-03-28)
|
||||
|
||||
## Enhancements
|
||||
### Enhancements
|
||||
|
||||
- console view is now prettier ([#92](https://github.com/vatesfr/xo-web/issues/92))
|
||||
|
||||
## Bug fixes
|
||||
### Bug fixes
|
||||
|
||||
- VM creation fails to incorrect dependencies ([xo-server/#24](https://github.com/vatesfr/xo-server/issues/24))
|
||||
- VDIs list in SR view is blinking ([#109](https://github.com/vatesfr/xo-web/issues/109))
|
||||
|
||||
## **3.3.0** (2014-03-07)
|
||||
|
||||
## Enhancements
|
||||
### Enhancements
|
||||
|
||||
- [Grunt](http://gruntjs.com/) has been replaced by [gulp.js](http://gulpjs.com/) ([#91](https://github.com/vatesfr/xo-web/issues/91))
|
||||
- a host can be detached from a pool ([#98](https://github.com/vatesfr/xo-web/issues/98))
|
||||
- snapshots management in VM view ([#99](https://github.com/vatesfr/xo-web/issues/99))
|
||||
- log deletion in VM view ([#100](https://github.com/vatesfr/xo-web/issues/100))
|
||||
|
||||
## Bug fixes
|
||||
### Bug fixes
|
||||
|
||||
- *Snapshot* not working in VM view ([#95](https://github.com/vatesfr/xo-web/issues/95))
|
||||
- Host *Reboot*/*Restart toolstack*/*Shutdown* not working in main view ([#97](https://github.com/vatesfr/xo-web/issues/97))
|
||||
|
||||
26
README.md
@@ -18,3 +18,29 @@ Manual install procedure is [available here](https://github.com/vatesfr/xo/blob/
|
||||
If you are certain the bug is exclusively related to XO-Web, you may use the [bugtracker of this repository](https://github.com/vatesfr/xo-web/issues).
|
||||
|
||||
Otherwise, please consider using the [bugtracker of the general repository](https://github.com/vatesfr/xo/issues).
|
||||
|
||||
## Process for new release
|
||||
|
||||
```bash
|
||||
# Switch to the master branch.
|
||||
git checkout master
|
||||
|
||||
# Merge changes of the next-release branch.
|
||||
git merge next-release
|
||||
|
||||
# Increment the version (patch, minor or major).
|
||||
npm version minor
|
||||
|
||||
# Go back to the next-release branch.
|
||||
git checkout next-release
|
||||
|
||||
# Fetches the last changes (the merge and version bump) from master to
|
||||
# next-release.
|
||||
git pull --fast-forward master
|
||||
|
||||
# Push the changes on git.
|
||||
git push origin master:master next-release:next-release
|
||||
|
||||
# Publish this release to npm.
|
||||
npm publish
|
||||
```
|
||||
|
||||
85
app/app.coffee
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
window.jQuery = window.$ = require 'jquery'
|
||||
|
||||
require 'angular'
|
||||
require 'angular-animate'
|
||||
|
||||
require 'angular-bootstrap'
|
||||
require 'angular-ui-router'
|
||||
require 'angular-ui-utils'
|
||||
require 'angular-xeditable'
|
||||
require 'select2'
|
||||
require 'angular-ui-select2'
|
||||
require 'angularjs-naturalsort'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
angular.module 'xoWebApp', [
|
||||
|
||||
'ui.bootstrap'
|
||||
'ui.indeterminate'
|
||||
'ui.router'
|
||||
'ui.select2'
|
||||
|
||||
'naturalSort'
|
||||
'xeditable'
|
||||
|
||||
(require './directives').name
|
||||
(require './filters').name
|
||||
(require './services').name
|
||||
|
||||
(require './modules/about').name
|
||||
(require './modules/console').name
|
||||
(require './modules/delete-vms').name
|
||||
(require './modules/generic-modal').name
|
||||
(require './modules/home').name
|
||||
(require './modules/host').name
|
||||
(require './modules/list').name
|
||||
(require './modules/login').name
|
||||
(require './modules/navbar').name
|
||||
(require './modules/new-sr').name
|
||||
(require './modules/new-vm').name
|
||||
(require './modules/pool').name
|
||||
(require './modules/settings').name
|
||||
(require './modules/sr').name
|
||||
(require './modules/vm').name
|
||||
]
|
||||
.config ($urlRouterProvider, $tooltipProvider) ->
|
||||
# Redirects unmatched URLs to `/`.
|
||||
$urlRouterProvider.otherwise '/'
|
||||
|
||||
# Changes the default settings for the tooltips.
|
||||
$tooltipProvider.options
|
||||
appendToBody: true
|
||||
placement: 'bottom'
|
||||
.run ($rootScope, $state, xoApi, editableOptions, editableThemes, notify, $templateCache) ->
|
||||
$rootScope.$on '$stateChangeStart', (event, state, stateParams) ->
|
||||
{user} = xoApi
|
||||
loggedIn = user?
|
||||
|
||||
if state.name is 'login'
|
||||
if loggedIn
|
||||
event.preventDefault()
|
||||
$state.go 'home'
|
||||
return
|
||||
|
||||
unless loggedIn
|
||||
event.preventDefault()
|
||||
|
||||
# FIXME: find a better way to pass info to the login controller.
|
||||
$rootScope._login = { state, stateParams }
|
||||
|
||||
$state.go 'login'
|
||||
return
|
||||
|
||||
# The user must have the `admin` permission to access the
|
||||
# settings page.
|
||||
if state.name is 'settings' and user.permission isnt 'admin'
|
||||
event.preventDefault()
|
||||
notify.error
|
||||
title: 'Restricted area'
|
||||
message: 'You do not have the permission to view this page'
|
||||
|
||||
editableThemes.bs3.inputClass = 'input-sm'
|
||||
editableThemes.bs3.buttonsClass = 'btn-sm'
|
||||
editableOptions.theme = 'bs3'
|
||||
@@ -1,4 +1,5 @@
|
||||
angular.module('xoWebApp')
|
||||
# TODO: split into multiple modules.
|
||||
module.exports = angular.module 'xoWebApp.directives', []
|
||||
|
||||
# This attribute stops the ascendant propagation of a given event.
|
||||
#
|
||||
@@ -6,7 +7,9 @@ angular.module('xoWebApp')
|
||||
# stop.
|
||||
.directive 'stopEvent', ->
|
||||
(_, $element, attrs) ->
|
||||
$element.on attrs.stopEvent, ($event) -> $event.stopPropagation()
|
||||
$element.on attrs.stopEvent, ($event) ->
|
||||
console.log $event
|
||||
$event.stopPropagation()
|
||||
|
||||
# This attribute works similarly to `ng-click` but do not handle the
|
||||
# event if the clicked element:
|
||||
@@ -98,104 +101,15 @@ angular.module('xoWebApp')
|
||||
true
|
||||
)
|
||||
|
||||
.directive 'xoVnc', ($window) ->
|
||||
# This helper function parses a URL and returns its components:
|
||||
# protocol, hostname, port, path and query.
|
||||
parseUrl = (url) ->
|
||||
a = $window.document.createElement 'a'
|
||||
a.href = url
|
||||
{protocol, hostname, port, host, pathname, search, hash} = a
|
||||
|
||||
{
|
||||
# Protocol lowercased postfixed with ':'.
|
||||
protocol
|
||||
|
||||
hostname
|
||||
port
|
||||
|
||||
# Same has hostname[:port].
|
||||
host
|
||||
|
||||
# The path excluding the query string.
|
||||
pathname
|
||||
|
||||
# Query string (including '?').
|
||||
search
|
||||
|
||||
# Same has `pathname + search`.
|
||||
path: "#{pathname}#{search}"
|
||||
|
||||
# Fragment (including '#').
|
||||
hash
|
||||
}
|
||||
|
||||
# The directive definition.
|
||||
{
|
||||
restrict: 'E'
|
||||
|
||||
scope: {
|
||||
height: '@?'
|
||||
width: '@?'
|
||||
url: '@'
|
||||
remoteControl: '='
|
||||
}
|
||||
|
||||
replace: true
|
||||
template: '''
|
||||
<canvas height="{{height}}" width="{{width}}">
|
||||
Sorry, your browser does not support the canvas element.
|
||||
</canvas>
|
||||
'''
|
||||
|
||||
link: ($scope, $element, attrs) ->
|
||||
# Default options.
|
||||
$scope.$watch 'height', -> $scope.height ?= 480
|
||||
$scope.$watch 'width', -> $scope.width ?= 640
|
||||
|
||||
rfb = null
|
||||
|
||||
$scope.remoteControl = {
|
||||
sendCtrlAltDel: ->
|
||||
rfb.sendCtrlAltDel() if rfb?
|
||||
}
|
||||
|
||||
# Connects to the specified URL.
|
||||
$scope.$watch 'url', (url) ->
|
||||
# Properly disconnects first if necessary.
|
||||
if rfb?
|
||||
rfb.disconnect()
|
||||
rfb = null
|
||||
|
||||
# If the URL is empty, nothing to do.
|
||||
return unless url
|
||||
|
||||
# Creates the new RFB object.
|
||||
rfb = new $window.RFB {
|
||||
# Options.
|
||||
encrypt: false
|
||||
target: $element[0]
|
||||
wsProtocols: ['chat']
|
||||
|
||||
# Callbacks.
|
||||
onPasswordRequired: (rfb) ->
|
||||
rfb.sendPassword $window.prompt 'Password required:'
|
||||
onUpdateState: (args...) -> console.log args
|
||||
}
|
||||
|
||||
# Parse the URL.
|
||||
url = parseUrl url
|
||||
|
||||
# Connects.
|
||||
rfb.connect(
|
||||
url.hostname
|
||||
80 # Ignores the specified port and always use 80.
|
||||
'' # TODO: comment.
|
||||
url.path.substr 1 # Leading '/' is added by noVNC.
|
||||
)
|
||||
|
||||
# Properly disconnect if the console is closed.
|
||||
$scope.$on '$destroy', ->
|
||||
if rfb?
|
||||
rfb.disconnect()
|
||||
rfb = null
|
||||
}
|
||||
.directive 'fixAutofill', ($timeout) ->
|
||||
restrict: 'A'
|
||||
require: 'ngModel'
|
||||
link: ($scope, $elem, attrs, ngModel) ->
|
||||
previous = $elem.val()
|
||||
$timeout(
|
||||
->
|
||||
current = $elem.val()
|
||||
if ngModel.$pristine and current isnt previous
|
||||
ngModel.$setViewValue current
|
||||
5e2
|
||||
)
|
||||
@@ -1,4 +1,7 @@
|
||||
angular.module('xoWebApp')
|
||||
# TODO: split into multiple modules.
|
||||
module.exports = angular.module 'xoWebApp.filters', [
|
||||
(require './services').name
|
||||
]
|
||||
|
||||
# The bytes filters takes a number and formats it using adapted
|
||||
# units (KB, MB, etc.).
|
||||
@@ -91,7 +94,7 @@ angular.module('xoWebApp')
|
||||
return 'N/A' if value[1] is 0
|
||||
|
||||
result = 100 * value[0] / value[1]
|
||||
if Number.isNaN result
|
||||
if isNaN result
|
||||
return 'N/A'
|
||||
|
||||
value = result
|
||||
BIN
app/images/logo.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
app/images/logo_small.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
140
app/index.html
@@ -1,140 +0,0 @@
|
||||
<!-- HTML 5 Doctype -->
|
||||
<!doctype html>
|
||||
|
||||
<!-- The “no-js” class will be automatically removed if JavaScript is
|
||||
- available.
|
||||
-->
|
||||
<html lang="en" dir="ltr" class="no-js">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!--
|
||||
- This file is a part of Xen Orchestra Web.
|
||||
-
|
||||
- Xen Orchestra Web is free software: you can redistribute it and/or
|
||||
- modify it under the terms of the GNU Affero General Public License
|
||||
- as published by the Free Software Foundation, either version 3 of
|
||||
- the License, or (at your option) any later version.
|
||||
-
|
||||
- Xen Orchestra Web is distributed in the hope that it will be
|
||||
- useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
- Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with Xen Orchestra Web. If not, see
|
||||
- <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
- @author Olivier Lambert <olivier.lambert@vates.fr>
|
||||
- @license http://www.gnu.org/licenses/agpl-3.0-standalone.html GNU AGPLv3
|
||||
-
|
||||
- @package Xen Orchestra Web
|
||||
-->
|
||||
|
||||
<!-- Makes sure IE is using the last engine available. -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<!-- Replaces the “no-js” class by the “js” class if JavaScript
|
||||
- is available.
|
||||
-->
|
||||
<script>!function(d){d.className=d.className.replace(/\bno-js\b/,'js')}(document.documentElement)</script>
|
||||
|
||||
<!-- (To confirm.) For smartphones and tablets: sets the page
|
||||
- width to the device width and prevents the page from being
|
||||
- zoomed in when going to landscape mode.
|
||||
-->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Xen Orchestra</title>
|
||||
|
||||
<meta name="description" content="Web interface for XenServer/XAPI Hosts" />
|
||||
<meta name="author" content="Vates SAS" />
|
||||
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
|
||||
|
||||
<!-- build:css(.tmp) styles/main.css -->
|
||||
<link rel="stylesheet" href="styles/main.css">
|
||||
<!-- endbuild -->
|
||||
<!-- Angular-xeditable CSS -->
|
||||
<link rel="stylesheet" href="bower_components/angular-xeditable/dist/css/xeditable.css">
|
||||
<!-- Angular UI Select -->
|
||||
<link rel="stylesheet" href="bower_components/select2/select2.css">
|
||||
<link rel="stylesheet" href="bower_components/angular-notify-toaster/toaster.css">
|
||||
</head>
|
||||
<body ng-app="xoWebApp">
|
||||
<!--[if lt IE 7]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="bower_components/es5-shim/es5-shim.js"></script>
|
||||
<script src="bower_components/json3/lib/json3.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<toaster-container></toaster-container>
|
||||
|
||||
<!-- Navigation bar. -->
|
||||
<div ng-controller="NavBarCtrl">
|
||||
<ng-include src="'views/nav-bar.html'"></ng-include>
|
||||
</div>
|
||||
|
||||
<!-- Main content (managed by the router). -->
|
||||
<div class="view-main" ui-view></div>
|
||||
|
||||
<script src="bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="bower_components/angular/angular.js"></script>
|
||||
<script src="bower_components/angular-animate/angular-animate.js"></script>
|
||||
<script src="bower_components/angular-cookies/angular-cookies.js"></script>
|
||||
|
||||
<!-- build:js scripts/plugins.js -->
|
||||
<script src="bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.js"></script>
|
||||
<script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
|
||||
<script src="bower_components/angular-ui-utils/ui-utils.js"></script>
|
||||
<script src="bower_components/angular-notify-toaster/toaster.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
<!-- build:js scripts/modules.js -->
|
||||
<script src="bower_components/angular-xeditable/dist/js/xeditable.js"></script>
|
||||
<script src="bower_components/select2/select2.js"></script>
|
||||
<script src="bower_components/angular-ui-select2/src/select2.js"></script>
|
||||
<script src="bower_components/angularjs-naturalsort/index.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
<!-- noVNC -->
|
||||
<script src="bower_components/noVNC/include/util.js"></script>
|
||||
<script>
|
||||
var INCLUDE_URI = 'bower_components/noVNC/include/';
|
||||
Util.load_scripts([
|
||||
'webutil.js',
|
||||
'base64.js',
|
||||
'websock.js',
|
||||
'des.js',
|
||||
'keysymdef.js',
|
||||
'keyboard.js',
|
||||
'input.js',
|
||||
'display.js',
|
||||
'jsunzip.js',
|
||||
'rfb.js',
|
||||
]);
|
||||
</script>
|
||||
<!-- /noVNC -->
|
||||
|
||||
<!-- build:js({.tmp,app}) scripts/scripts.js -->
|
||||
<script src="scripts/app.js"></script>
|
||||
<script src="scripts/directives.js"></script>
|
||||
<script src="scripts/filters.js"></script>
|
||||
<script src="scripts/services.js"></script>
|
||||
<script src="scripts/controllers/main.js"></script>
|
||||
<script src="scripts/controllers/nav-bar.js"></script>
|
||||
<script src="scripts/controllers/list.js"></script>
|
||||
<script src="scripts/controllers/host.js"></script>
|
||||
<script src="scripts/controllers/sr.js"></script>
|
||||
<script src="scripts/controllers/pool.js"></script>
|
||||
<script src="scripts/controllers/vm.js"></script>
|
||||
<script src="scripts/controllers/console.js"></script>
|
||||
<script src="scripts/controllers/settings.js"></script>
|
||||
<script src="scripts/controllers/new_vm.js"></script>
|
||||
<script src="scripts/controllers/new_sr.js"></script>
|
||||
<script src="scripts/controllers/delete_vms.js"></script>
|
||||
<script src="scripts/controllers/generic_modal.js"></script>
|
||||
<!-- endbuild -->
|
||||
</body>
|
||||
</html>
|
||||
93
app/index.jade
Normal file
@@ -0,0 +1,93 @@
|
||||
//- HTML 5 Doctype
|
||||
doctype html
|
||||
|
||||
//- The “no-js” class will be automatically removed if JavaScript is
|
||||
//- available.
|
||||
html.no-js(lang="en", dir="ltr")
|
||||
head
|
||||
meta(charset="utf-8")
|
||||
//- This file is a part of Xen Orchestra Web.
|
||||
//-
|
||||
//- Xen Orchestra Web is free software: you can redistribute it and/or
|
||||
//- modify it under the terms of the GNU Affero General Public License
|
||||
//- as published by the Free Software Foundation, either version 3 of
|
||||
//- the License, or (at your option) any later version.
|
||||
//-
|
||||
//- Xen Orchestra Web is distributed in the hope that it will be
|
||||
//- useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
//- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
//- Affero General Public License for more details.
|
||||
//-
|
||||
//- You should have received a copy of the GNU Affero General Public License
|
||||
//- along with Xen Orchestra Web. If not, see
|
||||
//- <http://www.gnu.org/licenses/>.
|
||||
//-
|
||||
//- @author Olivier Lambert <olivier.lambert@vates.fr>
|
||||
//- @license http://www.gnu.org/licenses/agpl-3.0-standalone.html GNU AGPLv3
|
||||
//-
|
||||
//- @package Xen Orchestra Web
|
||||
|
||||
//- Makes sure IE is using the last engine available.
|
||||
meta(http-equiv="X-UA-Compatible", content="IE=edge,chrome=1")
|
||||
|
||||
//- Replaces the “no-js” class by the “js” class if JavaScript is
|
||||
//- available.
|
||||
script.
|
||||
!function(d){d.className=d.className.replace(/\\bno-js\b/,'js')}(document.documentElement)
|
||||
|
||||
//- (To confirm.) For smartphones and tablets: sets the page
|
||||
//- width to the device width and prevents the page from being
|
||||
//- zoomed in when going to landscape mode.
|
||||
meta(name="viewport", content="width=device-width, initial-scale=1.0")
|
||||
|
||||
title Xen Orchestra
|
||||
meta(name="description", content="Web interface for XenServer/XAPI Hosts")
|
||||
meta(name="author", content="Vates SAS")
|
||||
|
||||
//- Place favicon.ico and apple-touch-icon.png in the root directory
|
||||
link(rel="stylesheet", href="styles/main.css")
|
||||
|
||||
//- Angular-xeditable CSS
|
||||
link(rel="stylesheet", href="bower_components/angular-xeditable/dist/css/xeditable.css")
|
||||
|
||||
//- Angular UI Select
|
||||
link(rel="stylesheet", href="bower_components/select2/select2.css")
|
||||
|
||||
link(rel="stylesheet", href="bower_components/angular-notify-toaster/toaster.css")
|
||||
body(ng-app="xoWebApp")
|
||||
|
||||
//- Conditional comments needs plain HTML.
|
||||
| <!--[if lt IE 7]>
|
||||
| <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
| <![endif]-->
|
||||
| <!--[if lt IE 9]>
|
||||
| <script src="bower_components/es5-shim/es5-shim.js"></script>
|
||||
| <script src="bower_components/json3/lib/json3.min.js"></script>
|
||||
| <![endif]-->
|
||||
|
||||
toaster-container
|
||||
|
||||
//- Navigation bar.
|
||||
navbar
|
||||
|
||||
//- Main content (managed by the router).
|
||||
.view-main(ui-view = "")
|
||||
|
||||
//- noVNC
|
||||
script(src="bower_components/noVNC/include/util.js")
|
||||
script.
|
||||
var INCLUDE_URI = 'bower_components/noVNC/include/';
|
||||
Util.load_scripts([
|
||||
'webutil.js',
|
||||
'base64.js',
|
||||
'websock.js',
|
||||
'des.js',
|
||||
'keysymdef.js',
|
||||
'keyboard.js',
|
||||
'input.js',
|
||||
'display.js',
|
||||
'jsunzip.js',
|
||||
'rfb.js',
|
||||
]);
|
||||
|
||||
script(src="app.js")
|
||||
20
app/modules/about/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
//====================================================================
|
||||
|
||||
/* global angular:false */
|
||||
require('angular');
|
||||
require('angular-ui-router');
|
||||
|
||||
//====================================================================
|
||||
|
||||
module.exports = angular.module('xoWebApp.about', [
|
||||
'ui.router',
|
||||
])
|
||||
.config(function ($stateProvider) {
|
||||
$stateProvider.state('about', {
|
||||
url: '/about',
|
||||
template: require('./view'),
|
||||
});
|
||||
})
|
||||
;
|
||||
46
app/modules/about/view.jade
Normal file
@@ -0,0 +1,46 @@
|
||||
// TODO: lots of stuff.
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.page-title About Xen Orchestra
|
||||
.grid
|
||||
// Vates
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-lightbulb-o(style="color: #e25440;")
|
||||
| Vates
|
||||
p.text-center
|
||||
| We are the team behind Xen Orchestra, we are Vates! We create Open Source products and we offer commercial support for Xen and Xen Orchestra. Want to know more about us? Go to our website!
|
||||
p.text-center
|
||||
img(src="images/arrow.png")
|
||||
br
|
||||
p.text-center
|
||||
a.btn.btn-success(href="https://vates.fr")
|
||||
i.fa.fa-hand-o-right
|
||||
| Our website
|
||||
// Open Source
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-thumbs-up(style="color: #e25440;")
|
||||
| Open Source
|
||||
p.text-center
|
||||
| This project is Open Source (AGPL), everyone is welcome aboard! You want a specific feature in XO? Report a bug? Go to our project website, read the FAQ and get involved in the project!
|
||||
p.text-center
|
||||
img(src="images/opensource.png")
|
||||
br
|
||||
p.text-center
|
||||
a.btn.btn-info(href="https://xen-orchestra.com")
|
||||
i.fa.fa-flask
|
||||
| Project website
|
||||
// Pro support
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-truck(style="color: #e25440;")
|
||||
| Pro Support Delivered
|
||||
p.text-center
|
||||
| Our XO Appliance can be delivered with professional support: stay relaxed, we got your back! You can also have assitance for deploying or upgrade your virtualized infrastructure through our deep understanding of Xen.
|
||||
p.text-center
|
||||
img(src="images/support.png")
|
||||
p.text-center
|
||||
a.btn.btn-primary(href="https://xen-orchestra.com/services/")
|
||||
i.fa.fa-envelope
|
||||
| Get services
|
||||
175
app/modules/console/index.coffee
Normal file
@@ -0,0 +1,175 @@
|
||||
require 'angular'
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.console', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'consoles_view',
|
||||
url: '/consoles/:id'
|
||||
controller: 'ConsoleCtrl'
|
||||
template: require './view'
|
||||
.controller 'ConsoleCtrl', ($scope, $stateParams, xoApi, xo) ->
|
||||
{id} = $stateParams
|
||||
{get} = xo
|
||||
push = Array::push.apply.bind Array::push
|
||||
merge = do ->
|
||||
(args...) ->
|
||||
result = []
|
||||
for arg in args
|
||||
push result, arg if arg?
|
||||
result
|
||||
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
->
|
||||
unless xoApi.user
|
||||
$scope.consoleUrl = ''
|
||||
$scope.VDIs = []
|
||||
return
|
||||
|
||||
VM = $scope.VM = xo.get id
|
||||
return unless VM? and VM.power_state is 'Running'
|
||||
|
||||
pool = get VM.poolRef
|
||||
return unless pool
|
||||
|
||||
$scope.consoleUrl = do ->
|
||||
for console in VM.consoles
|
||||
if console.protocol is 'rfb'
|
||||
return "#{console.location}&session_id=#{pool.$sessionId}"
|
||||
''
|
||||
|
||||
host = get VM.$container # host because the VM is running.
|
||||
return unless host
|
||||
|
||||
# FIXME: We should filter on connected SRs (PBDs)!
|
||||
SRs = get (merge host.SRs, pool.SRs)
|
||||
$scope.VDIs = do ->
|
||||
VDIs = []
|
||||
for SR in SRs
|
||||
push VDIs, SR.VDIs if SR.content_type is 'iso'
|
||||
get VDIs
|
||||
|
||||
cdDrive = do ->
|
||||
return VBD for VBD in (get VM.$VBDs) when VBD.is_cd_drive
|
||||
null
|
||||
|
||||
$scope.mountedIso =
|
||||
if cdDrive and cdDrive.VDI and (VDI = get cdDrive.VDI)
|
||||
VDI.UUID
|
||||
else
|
||||
''
|
||||
)
|
||||
|
||||
$scope.startVM = xo.vm.start
|
||||
$scope.stopVM = xo.vm.stop
|
||||
$scope.rebootVM = xo.vm.restart
|
||||
|
||||
$scope.eject = ->
|
||||
xo.vm.ejectCd id
|
||||
$scope.insert = (disc_id) ->
|
||||
xo.vm.insertCd id, disc_id, true
|
||||
.directive 'xoVnc', ($window) ->
|
||||
# This helper function parses a URL and returns its components:
|
||||
# protocol, hostname, port, path and query.
|
||||
parseUrl = (url) ->
|
||||
a = $window.document.createElement 'a'
|
||||
a.href = url
|
||||
{protocol, hostname, port, host, pathname, search, hash} = a
|
||||
|
||||
{
|
||||
# Protocol lowercased postfixed with ':'.
|
||||
protocol
|
||||
|
||||
hostname
|
||||
port
|
||||
|
||||
# Same has hostname[:port].
|
||||
host
|
||||
|
||||
# The path excluding the query string.
|
||||
pathname
|
||||
|
||||
# Query string (including '?').
|
||||
search
|
||||
|
||||
# Same has `pathname + search`.
|
||||
path: "#{pathname}#{search}"
|
||||
|
||||
# Fragment (including '#').
|
||||
hash
|
||||
}
|
||||
|
||||
# The directive definition.
|
||||
{
|
||||
restrict: 'E'
|
||||
|
||||
scope: {
|
||||
height: '@?'
|
||||
width: '@?'
|
||||
url: '@'
|
||||
remoteControl: '='
|
||||
}
|
||||
|
||||
replace: true
|
||||
template: '''
|
||||
<canvas height="{{height}}" width="{{width}}">
|
||||
Sorry, your browser does not support the canvas element.
|
||||
</canvas>
|
||||
'''
|
||||
|
||||
link: ($scope, $element, attrs) ->
|
||||
# Default options.
|
||||
$scope.$watch 'height', -> $scope.height ?= 480
|
||||
$scope.$watch 'width', -> $scope.width ?= 640
|
||||
|
||||
rfb = null
|
||||
|
||||
$scope.remoteControl = {
|
||||
sendCtrlAltDel: ->
|
||||
rfb.sendCtrlAltDel() if rfb?
|
||||
}
|
||||
|
||||
# Connects to the specified URL.
|
||||
$scope.$watch 'url', (url) ->
|
||||
# Properly disconnects first if necessary.
|
||||
if rfb?
|
||||
rfb.disconnect()
|
||||
rfb = null
|
||||
|
||||
# If the URL is empty, nothing to do.
|
||||
return unless url
|
||||
|
||||
# Creates the new RFB object.
|
||||
rfb = new $window.RFB {
|
||||
# Options.
|
||||
encrypt: false
|
||||
target: $element[0]
|
||||
wsProtocols: ['chat']
|
||||
|
||||
# Callbacks.
|
||||
onPasswordRequired: (rfb) ->
|
||||
rfb.sendPassword $window.prompt 'Password required:'
|
||||
onUpdateState: (args...) -> console.log args
|
||||
}
|
||||
|
||||
# Parse the URL.
|
||||
url = parseUrl url
|
||||
|
||||
# Connects.
|
||||
rfb.connect(
|
||||
url.hostname
|
||||
80 # Ignores the specified port and always use 80.
|
||||
'' # TODO: comment.
|
||||
url.path.substr 1 # Leading '/' is added by noVNC.
|
||||
)
|
||||
|
||||
# Properly disconnect if the console is closed.
|
||||
$scope.$on '$destroy', ->
|
||||
if rfb?
|
||||
rfb.disconnect()
|
||||
rfb = null
|
||||
}
|
||||
@@ -13,8 +13,8 @@
|
||||
.list-group
|
||||
|
||||
//- Toolbar
|
||||
.list-group-item: .row
|
||||
.col-md-10: .input-group
|
||||
.list-group-item: .row.text-center
|
||||
.col-sm-6: .input-group
|
||||
select.form-control(
|
||||
ng-model = 'mountedIso'
|
||||
ng-change = 'insert(mountedIso)'
|
||||
@@ -26,13 +26,21 @@
|
||||
ng-disabled = '!mountedIso'
|
||||
)
|
||||
i.fa.fa-eject
|
||||
.col-md-2: button.btn.btn-default(
|
||||
.col-sm-3: button.btn.btn-default(
|
||||
ng-click = 'vncRemote.sendCtrlAltDel()'
|
||||
)
|
||||
i.fa.fa-keyboard-o
|
||||
|
|
||||
| Ctrl+Alt+Del
|
||||
|
||||
// Action panel
|
||||
.col-sm-3
|
||||
.btn-group
|
||||
button.btn.btn-default.inversed(tooltip="Stop VM", type="button", xo-click="stopVM(VM.UUID)")
|
||||
i.fa.fa-stop.fa-fw
|
||||
button.btn.btn-default.inversed(tooltip="Start VM", type="button", xo-click="startVM(VM.UUID)")
|
||||
i.fa.fa-play.fa-fw
|
||||
button.btn.btn-default.inversed(tooltip="Reboot VM", type="button", xo-click="rebootVM(VM.UUID)")
|
||||
i.fa.fa-refresh.fa-fw
|
||||
//- Console
|
||||
.list-group-item
|
||||
xo-vnc.center-block(
|
||||
35
app/modules/delete-vms/index.coffee
Normal file
@@ -0,0 +1,35 @@
|
||||
require 'angular'
|
||||
|
||||
require 'angular-bootstrap'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.deleteVms', [
|
||||
'ui.bootstrap'
|
||||
|
||||
(require '../../services.coffee').name
|
||||
]
|
||||
.controller 'DeleteVmsCtrl', ($scope, $modalInstance, xo, VMsIds) ->
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
->
|
||||
$scope.VMs = xo.get VMsIds
|
||||
)
|
||||
|
||||
# Do disks have to be deleted for a given VM.
|
||||
disks = $scope.disks = {}
|
||||
do ->
|
||||
disks[id] = true for id in VMsIds
|
||||
|
||||
$scope.delete = ->
|
||||
$modalInstance.close ([id, disks[id]] for id in VMsIds)
|
||||
.service 'deleteVmsModal', ($modal, xo) ->
|
||||
return (ids) ->
|
||||
modal = $modal.open
|
||||
controller: 'DeleteVmsCtrl'
|
||||
template: require './view'
|
||||
resolve: VMsIds: -> ids
|
||||
|
||||
return modal.result.then (toDelete) ->
|
||||
for [id, deleteDisks] in toDelete
|
||||
xo.vm.delete id, deleteDisks
|
||||
24
app/modules/delete-vms/view.jade
Normal file
@@ -0,0 +1,24 @@
|
||||
form(ng-submit="delete()")
|
||||
.modal-header
|
||||
h3 VMs deletion
|
||||
.modal-body
|
||||
p
|
||||
| You are going to delete the following VMs, this is a
|
||||
strong dangerous action
|
||||
| !
|
||||
table.table
|
||||
tr
|
||||
th.col-sm-3 Name
|
||||
th.col-sm-6 Description
|
||||
th.col-sm-3 Delete disks?
|
||||
tbody
|
||||
tr(ng-repeat="VM in VMs | orderBy:natural('name_label') track by VM.UUID")
|
||||
td {{VM.name_label}}
|
||||
td {{VM.name_description}}
|
||||
td
|
||||
input(type="checkbox", ng-model="disks[VM.UUID]")
|
||||
.modal-footer
|
||||
button.btn.btn-primary(type="submit")
|
||||
| Delete
|
||||
button.btn.btn-warning(type="button", ng-click="$dismiss()")
|
||||
| Cancel
|
||||
43
app/modules/generic-modal/index.js
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
//====================================================================
|
||||
|
||||
/* global angular:false */
|
||||
require('angular');
|
||||
|
||||
require('angular-bootstrap');
|
||||
|
||||
//====================================================================
|
||||
|
||||
module.exports = angular.module('xoWebApp.GenericModal', [
|
||||
'ui.bootstrap',
|
||||
])
|
||||
.controller('GenericModalCtrl', function ($scope, $modalInstance, options) {
|
||||
$scope.title = options.title;
|
||||
$scope.message = options.message;
|
||||
|
||||
$scope.yesButtonLabel = options.yesButtonLabel || 'Ok';
|
||||
$scope.noButtonLabel = options.noButtonLabel;
|
||||
})
|
||||
.service('modal', function ($modal) {
|
||||
return {
|
||||
confirm: function (opts) {
|
||||
var modal = $modal.open({
|
||||
controller: 'GenericModalCtrl',
|
||||
template: require('./view'),
|
||||
resolve: {
|
||||
options: function () {
|
||||
return {
|
||||
title: opts.title,
|
||||
message: opts.message,
|
||||
noButtonLabel: 'Cancel',
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return modal.result;
|
||||
}
|
||||
};
|
||||
})
|
||||
;
|
||||
11
app/modules/generic-modal/view.jade
Normal file
@@ -0,0 +1,11 @@
|
||||
.modal-header
|
||||
h3
|
||||
i.fa.fa-exclamation-triangle.text-danger
|
||||
| {{title}}
|
||||
.modal-body
|
||||
| {{message}}
|
||||
.modal-footer
|
||||
button.btn.btn-primary(type="button", ng-click="$close()")
|
||||
| {{yesButtonLabel}}
|
||||
button.btn.btn-warning(ng-if="noButtonLabel", type="button", ng-click="$dismiss()")
|
||||
| {{noButtonLabel}}
|
||||
@@ -1,7 +1,19 @@
|
||||
'use strict'
|
||||
require 'angular'
|
||||
require 'angular-ui-router'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'MainCtrl', ($scope, $modal, modal, xo, dateFilter) ->
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.home', [
|
||||
'ui.router'
|
||||
|
||||
(require '../delete-vms').name
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'home',
|
||||
url: '/'
|
||||
controller: 'HomeCtrl'
|
||||
template: require './view'
|
||||
.controller 'HomeCtrl', ($scope, modal, xo, dateFilter, deleteVmsModal) ->
|
||||
VMs = []
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
@@ -74,18 +86,16 @@ angular.module('xoWebApp')
|
||||
$scope.deleteVMs = ->
|
||||
{selected_VMs} = $scope
|
||||
|
||||
VMsIds = (id for id, selected of selected_VMs when selected)
|
||||
modal = $modal.open {
|
||||
controller: 'DeleteVMsCtrl'
|
||||
templateUrl: 'views/delete_vms.html'
|
||||
resolve: {
|
||||
VMsIds: -> VMsIds
|
||||
}
|
||||
}
|
||||
deleteVmsModal (id for id, selected of selected_VMs when selected)
|
||||
|
||||
modal.result.then (toDelete) ->
|
||||
for [id, deleteDisks] in toDelete
|
||||
xo.vm.delete id, deleteDisks
|
||||
$scope.osType = (osName) ->
|
||||
switch osName
|
||||
when 'debian','ubuntu','centos','suse','redhat','oracle','gentoo','suse','fedora'
|
||||
'linux'
|
||||
when 'windows'
|
||||
'windows'
|
||||
else
|
||||
'other'
|
||||
|
||||
# VMs checkboxes.
|
||||
do ->
|
||||
@@ -150,11 +160,3 @@ angular.module('xoWebApp')
|
||||
# Unselects all VMs.
|
||||
$scope.selectVMs false
|
||||
|
||||
$scope.osType = (osName) ->
|
||||
switch osName
|
||||
when 'debian','ubuntu','centos','suse','redhat','oracle','gentoo'
|
||||
'linux'
|
||||
when 'windows'
|
||||
'windows'
|
||||
else
|
||||
'other'
|
||||
358
app/modules/home/view.jade
Normal file
@@ -0,0 +1,358 @@
|
||||
//- @todo Remove code duplication for the VMs listing by using a macro.
|
||||
.sub-bar
|
||||
.grid
|
||||
.grid-cell.overview
|
||||
// Stats
|
||||
i(tooltip="{{xo.pools.length}} pools")
|
||||
i.small {{xo.pools.length}}x
|
||||
|
|
||||
i.xo-icon-pool
|
||||
|
|
||||
|
|
||||
i(tooltip="{{hosts.length}} hosts")
|
||||
i.small {{hosts.length}}x
|
||||
|
|
||||
i.xo-icon-host
|
||||
|
|
||||
|
|
||||
i(tooltip="{{xo.$running_VMs.length}} of {{VMs.length}} VMs running")
|
||||
i.small {{xo.$running_VMs.length}}x
|
||||
|
|
||||
i.xo-icon-vm
|
||||
|
|
||||
|
|
||||
i(tooltip="{{xo.$vCPUs}} vCPUs used of {{xo.$CPUs}} CPUs")
|
||||
i.small {{xo.$vCPUs}}x
|
||||
|
|
||||
i.xo-icon-cpu
|
||||
|
|
||||
|
|
||||
i(tooltip="{{xo.$memory.usage | bytesToSize}} RAM allocated of {{xo.$memory.size | bytesToSize}}")
|
||||
i.small {{xo.$memory.usage | bytesToSize}}
|
||||
|
|
||||
i.xo-icon-memory
|
||||
.grid-cell
|
||||
.btn-group.before-action-bar.dropdown
|
||||
a.btn.navbar-btn.btn-default.dropdown-toggle.inversed
|
||||
input.inverse(type="checkbox", ng-model="master_selection", ng-change="selectVMs(master_selection)", ui-indeterminate="!(all || none)", stop-event="click")
|
||||
|
|
||||
i.fa.fa-caret-down
|
||||
ul.dropdown-menu.inverse(role="menu")
|
||||
li(ng-repeat="power_state in ['Halted', 'Running']")
|
||||
a(ng-click="selectVMs({power_state: power_state})")
|
||||
i.fa-fw(class="xo-icon-{{power_state | lowercase}}")
|
||||
| {{power_state}}
|
||||
li.divider
|
||||
li(ng-repeat="host in hosts | orderBy:natural('name_label') track by host.UUID", ng-if="host.VMs.length")
|
||||
a(ng-click="selectVMs({$container: host.ref})")
|
||||
i.xo-icon-host.fa-fw
|
||||
| On {{host.name_label}}
|
||||
.action-bar(ng-if="!none")
|
||||
|
|
||||
.btn-group
|
||||
button.btn.navbar-btn.btn-default.inversed(tooltip="Stop VM", type="button", ng-click="bulkAction('stopVM')")
|
||||
i.fa.fa-stop
|
||||
button.btn.navbar-btn.btn-default.inversed(tooltip="Start VM", type="button", ng-click="bulkAction('startVM')")
|
||||
i.fa.fa-play
|
||||
button.btn.navbar-btn.btn-default.inversed(tooltip="Reboot VM", type="button", ng-click="bulkAction('rebootVM')")
|
||||
i.fa.fa-refresh
|
||||
|
|
||||
.btn-group.dropdown
|
||||
button.btn.navbar-btn.btn-default.dropdown-toggle.inversed(tooltip="Migrate VM", type="button")
|
||||
i.fa.fa-share
|
||||
|
|
||||
i.fa.fa-caret-down
|
||||
ul.dropdown-menu.inverse(role="menu")
|
||||
li(ng-repeat="host in hosts | orderBy:natural('name_label') track by host.UUID")
|
||||
a(ng-click="bulkAction('migrateVM',host.UUID)")
|
||||
i.xo-icon-host.fa-fw
|
||||
| To {{host.name_label}}
|
||||
|
|
||||
.btn-group.dropdown
|
||||
button.btn.navbar-btn.btn-default.dropdown-toggle.inversed(type="button")
|
||||
| More
|
||||
|
|
||||
i.fa.fa-caret-down
|
||||
ul.dropdown-menu.inverse(role="menu")
|
||||
li
|
||||
a(ng-click="bulkAction('force_rebootVM')")
|
||||
i.fa.fa-bolt.fa-fw
|
||||
| Force reboot
|
||||
li
|
||||
a(ng-click="bulkAction('force_stopVM')")
|
||||
i.fa.fa-power-off.fa-fw
|
||||
| Force shutdown
|
||||
li.divider
|
||||
li
|
||||
a(ng-click="bulkAction('snapshotVM')")
|
||||
i.xo-icon-snapshot.fa-fw
|
||||
| Take a snapshot
|
||||
li
|
||||
a(ng-click="deleteVMs()")
|
||||
i.fa.fa-trash-o.fa-fw
|
||||
| Delete
|
||||
|
||||
//- FIXME: Ugly trick to force the pools to be under the sub bar.
|
||||
//- Add +7px to the 50px for having some space before the first pool.
|
||||
div(style="margin-top: 57px; visibility: hidden; height: 0") .
|
||||
|
||||
//- If we haven't any data
|
||||
div(ng-if="!pools.length")
|
||||
.grid
|
||||
.grid-cell.flat-panel.text-center
|
||||
h1 Welcome on Xen Orchestra!
|
||||
h3 It seems you aren't connected to any Xen host.
|
||||
p You can add any XAPI capable server by clicking on the menu icon "
|
||||
i.fa.fa-th
|
||||
| " and choose "
|
||||
i.fa.fa-cog
|
||||
| Settings"
|
||||
|
||||
p Enjoy Xen Orchestra!
|
||||
p
|
||||
a.btn.btn-primary.big(ui-sref="about")
|
||||
i.fa.fa-info-circle
|
||||
| About us
|
||||
//- If we have data
|
||||
div(ng-if="pools.length")
|
||||
//- Contains a pool and all its children (hosts).
|
||||
.grid.pool-block(ng-repeat="pool in pools | orderBy:natural('name_label') track by pool.UUID")
|
||||
//- Pseudo pool if it is not a named pool.
|
||||
.grid-cell.grid--gutters.pool-cell(ng-if="!pool.name_label")
|
||||
p.center(style="margin-top: 2em;") No pool connected
|
||||
//- Contains information about the pool if it is a named pool.
|
||||
.grid-cell.grid--gutters.pool-cell(ng-if="pool.name_label")
|
||||
//- Header (name + dropdown menu).
|
||||
.dropdown.dropdown-pool
|
||||
a.pool-name(ui-sref="pools_view({id: pool.UUID})")
|
||||
| {{pool.name_label}}
|
||||
a.dropdown-toggle
|
||||
|
|
||||
i.fa.fa-caret-down.big-caret
|
||||
ul.dropdown-menu.left(role="menu")
|
||||
li
|
||||
a(xo-click="new_sr()")
|
||||
i.xo-icon-sr.fa-fw
|
||||
| Add SR
|
||||
li
|
||||
a(xo-sref="VMs_new({container: pool.UUID})")
|
||||
i.xo-icon-vm.fa-fw
|
||||
| Add VM
|
||||
li.divider
|
||||
li
|
||||
a.disabled(xo-click="pool_disconnect(pool.UUID)")
|
||||
i.fa.fa-unlink.fa-fw
|
||||
| Disconnect
|
||||
//- /Header.
|
||||
//- Stats & SRs list.
|
||||
div
|
||||
//- Stats.
|
||||
ul.list-unstyled.stats
|
||||
li
|
||||
i(tooltip="{{pool.hosts.length}} hosts connected")
|
||||
i.small {{pool.hosts.length}}x
|
||||
|
|
||||
i.xo-icon-host
|
||||
|
|
||||
|
|
||||
i(tooltip="{{pool.$running_VMs.length}} of {{pool.$VMs.length}} VMs running")
|
||||
i.small {{pool.$running_VMs.length}}x
|
||||
|
|
||||
i.xo-icon-vm
|
||||
li(ng-if="pool.master", ng-init="master = (pool.master | resolve)")
|
||||
| Master:
|
||||
|
|
||||
a(ui-sref="hosts_view({id: master.UUID})") {{master.name_label}}
|
||||
li(ng-if="!pool.master") No master
|
||||
//- /Stats.
|
||||
//- SRs.
|
||||
div(ng-if="pool.SRs.length")
|
||||
p.center.small-caps SRs:
|
||||
table.table.table-hover.table-condensed
|
||||
tr(ng-repeat="SR in pool.SRs | resolve | orderBy:natural('name_label') track by SR.UUID", xo-sref="SRs_view({id: SR.UUID})")
|
||||
td.col-md-6.sr-name.no-border(ng-class="{'default-sr': SR.ref === pool.default_SR}")
|
||||
i.xo-icon-sr
|
||||
| {{SR.name_label}}
|
||||
td.col-md-6.right.no-border
|
||||
.progress.progress-small(tooltip="Disk: {{[SR.usage, SR.size] | %}} allocated")
|
||||
.progress-bar(role="progressbar", aria-valuenow="{{100*SR.usage/SR.size}}", aria-valuemin="0", aria-valuemax="100", style="width: {{[SR.usage, SR.size] | %}}")
|
||||
//- Contains all the hosts of this pool.
|
||||
.grid-cell.grid--gutters.hosts-vms-cells
|
||||
//- Contains a host and all its children (VMs).
|
||||
.grid(ng-repeat="host in pool.hosts | resolve | orderBy:natural('name_label') track by host.UUID")
|
||||
//- Contains information about the host.
|
||||
.grid-cell.host-cell
|
||||
//- Header (name + dropdown menu).
|
||||
.dropdown.dropdown-pool
|
||||
a.host-name(ui-sref="hosts_view({id: host.UUID})")
|
||||
| {{host.name_label}}
|
||||
a.dropdown-toggle
|
||||
|
|
||||
i.fa.fa-caret-down
|
||||
ul.dropdown-menu.left(role="menu")
|
||||
li
|
||||
a(xo-click="new_sr()")
|
||||
i.xo-icon-sr.fa-fw
|
||||
| Add SR
|
||||
li
|
||||
a(xo-sref="VMs_new({container: host.UUID})")
|
||||
i.xo-icon-vm.fa-fw
|
||||
| Add VM
|
||||
li.divider
|
||||
li
|
||||
a(ng-repeat="controller in [host.controller] | resolve track by controller.UUID", xo-sref="consoles_view({id: controller.UUID})")
|
||||
i.xo-icon-console.fa-fw
|
||||
| Console
|
||||
li
|
||||
a(xo-click="rebootHost(host.UUID)")
|
||||
i.fa.fa-refresh.fa-fw
|
||||
| Reboot
|
||||
li
|
||||
a(xo-click="shutdownHost(host.UUID)")
|
||||
i.fa.fa-power-off.fa-fw
|
||||
| Shutdown
|
||||
li
|
||||
a(xo-click="restartToolStack(host.UUID)")
|
||||
i.fa.fa-retweet.fa-fw
|
||||
| Restart toolstack
|
||||
li(ng-if="pool.name_label")
|
||||
a(xo-click="pool_removeHost(host.UUID)")
|
||||
i.fa.fa-cloud-upload.fa-fw
|
||||
| Remove from pool
|
||||
li(ng-if="!pool.name_label")
|
||||
a(xo-click="pool_addHost(host.UUID)")
|
||||
i.fa.fa-cloud-download.fa-fw
|
||||
| Add to pool
|
||||
//- /Header.
|
||||
//- Stats.
|
||||
ul.list-unstyled.stats
|
||||
//- Warning icon if host is halted or disabled
|
||||
li.text-danger(ng-if="host.power_state === 'Halted'")
|
||||
i.fa.fa-warning
|
||||
| Halted
|
||||
li.text-warning(ng-if="!host.enabled && host.power_state === 'Running'")
|
||||
i.fa.fa-warning
|
||||
| Disabled
|
||||
//- Memory
|
||||
li(ng-if="host.power_state === 'Running' && host.enabled")
|
||||
i.xo-icon-memory.i-progress
|
||||
.progress.progress-small(tooltip="RAM: {{[host.memory.usage, host.memory.size] | %}} allocated")
|
||||
.progress-bar(role="progressbar", aria-valuenow="{{100*host.memory.usage/host.memory.size}}", aria-valuemin="0", aria-valuemax="100", style="width: {{[host.memory.usage, host.memory.size] | %}}")
|
||||
//- Host address
|
||||
li.text-muted.substats
|
||||
i.xo-icon-network
|
||||
| {{host.address}}
|
||||
//- Contains all the VMs of this host.
|
||||
.grid.grid-cell.vm-cell
|
||||
//- If no VMs, fill the space with a message.
|
||||
.vms-notice(ng-if="!host.VMs.length")
|
||||
//- TODO: put 'Halted' state in xo-server
|
||||
//p(ng-if="'Halted' === host.power_state")
|
||||
//- | Host halted.
|
||||
p(ng-if="'shutdown' === values(host.current_operations)[0]")
|
||||
| Host halted.
|
||||
div(ng-if="'shutdown' !== values(host.current_operations)[0]")
|
||||
p(ng-if="!host.enabled")
|
||||
| Host disabled.
|
||||
p(ng-if="host.enabled")
|
||||
| No VMs on this host.
|
||||
//- /Message if no VMs.
|
||||
//- TODO: comment
|
||||
.table-responsive(ng-if="host.VMs.length")
|
||||
table.table.table-hover.table-condensed
|
||||
//- Contains a VM.
|
||||
tr(ng-repeat="VM in host.VMs | resolve | orderBy:natural('name_label') track by VM.UUID", xo-sref="VMs_view({id: VM.UUID})")
|
||||
//- Handle used for drag & drop.
|
||||
td.grab
|
||||
//- Checkbox used for selection.
|
||||
td.select-vm
|
||||
input(type="checkbox", ng-model="selected_VMs[VM.UUID]", ng-change="updateVMSelection(VM.UUID)")
|
||||
//- Power state
|
||||
td.vm-power-state
|
||||
i.xo-icon-working(ng-if="isVMWorking(VM)", tooltip="{{VM.power_state}} and {{values(VM.current_operations)[0]}}")
|
||||
i(class="xo-icon-{{VM.power_state | lowercase}}",ng-if="!isVMWorking(VM)", tooltip="{{VM.power_state}}")
|
||||
//- VM name.
|
||||
td.vm-name.col-md-2
|
||||
p.vm {{VM.name_label}}
|
||||
//- Quick actions.
|
||||
td.vm-quick-buttons.col-md-2
|
||||
.quick-buttons
|
||||
a(tooltip="Shutdown VM", xo-click="stopVM(VM.UUID)")
|
||||
i.fa.fa-stop
|
||||
a(tooltip="Start VM", xo-click="startVM(VM.UUID)")
|
||||
i.fa.fa-play
|
||||
a(tooltip="Reboot VM", xo-click="rebootVM(VM.UUID)")
|
||||
i.fa.fa-refresh
|
||||
a(tooltip="VM Console", xo-sref="consoles_view({id: VM.UUID})")
|
||||
i.xo-icon-console
|
||||
//- Description.
|
||||
td.vm-description.col-md-4
|
||||
i(class="xo-icon-{{osType(VM.os_version.distro)}}",ng-if="VM.os_version.distro", tooltip="{{VM.os_version.name}}")
|
||||
|
|
||||
i.fa.fa-fw(ng-if="!VM.os_version.distro")
|
||||
| {{VM.name_description}}
|
||||
//- Metrics.
|
||||
//- Memory
|
||||
td.vm-memory-stat.col-md-2
|
||||
.cpu
|
||||
| {{VM.memory.size | bytesToSize}}
|
||||
i.fa.fa-fw(ng-if="VM.PV_drivers")
|
||||
i.xo-icon-info.fa-fw(ng-if="!VM.PV_drivers", tooltip="Xen tools not installed")
|
||||
//- /Metrics.
|
||||
//- Address.
|
||||
td.text-muted.text-right.col-md-2
|
||||
| {{VM.addresses["0/ip"]}}
|
||||
//- Contains a pseudo-host which contains all VMs not in any hosts.
|
||||
.grid(ng-if="pool.VMs.length")
|
||||
//- This is where the information about a host would be displayed.
|
||||
.grid-cell.host-cell
|
||||
//- Contains all the VMs of this pool.
|
||||
.grid.grid-cell.vm-cell
|
||||
//- TODO: comment
|
||||
.table-responsive
|
||||
table.table.table-hover.table-condensed
|
||||
//- Contains a VM.
|
||||
tr(ng-repeat="VM in pool.VMs | resolve | orderBy:natural('name_label') track by VM.UUID", xo-sref="VMs_view({id: VM.UUID})")
|
||||
//- Handle used for drag & drop.
|
||||
td.grab
|
||||
//- Checkbox used for selection.
|
||||
td.select-vm
|
||||
input(type="checkbox", ng-model="selected_VMs[VM.UUID]", ng-change="updateVMSelection(VM.UUID)")
|
||||
//- Power state
|
||||
td.vm-power-state
|
||||
i.xo-icon-working(ng-if="isVMWorking(VM)", tooltip="{{VM.power_state}} and {{values(VM.current_operations)[0]}}")
|
||||
i(class="xo-icon-{{VM.power_state | lowercase}}",ng-if="!isVMWorking(VM)", tooltip="{{VM.power_state}}")
|
||||
//- VM name.
|
||||
td.vm-name.col-md-2
|
||||
p.vm {{VM.name_label}}
|
||||
//- Quick actions.
|
||||
td.vm-quick-buttons.col-md-2
|
||||
.quick-buttons
|
||||
a(tooltip="Shutdown VM", xo-click="stopVM(VM.UUID)")
|
||||
i.fa.fa-stop
|
||||
a(tooltip="Start VM", xo-click="startVM(VM.UUID)")
|
||||
i.fa.fa-play
|
||||
a(tooltip="Reboot VM", xo-click="rebootVM(VM.UUID)")
|
||||
i.fa.fa-refresh
|
||||
a(tooltip="VM Console")
|
||||
i.xo-icon-console
|
||||
//- Description.
|
||||
td.vm-description.col-md-4
|
||||
i(class="xo-icon-{{osType(VM.os_version.distro)}}",ng-if="VM.os_version.distro", tooltip="{{VM.os_version.name}}")
|
||||
|
|
||||
i.fa.fa-fw(ng-if="!VM.os_version.distro")
|
||||
| {{VM.name_description}}
|
||||
//- Metrics.
|
||||
//- Memory
|
||||
td.vm-memory-stat.col-md-2
|
||||
.cpu
|
||||
| {{VM.memory.size | bytesToSize}}
|
||||
i.fa.fa-fw(ng-if="VM.PV_drivers")
|
||||
i.xo-icon-info.fa-fw(ng-if="!VM.PV_drivers", tooltip="Xen tools not installed")
|
||||
//- /Metrics.
|
||||
//- Address.
|
||||
td.text-muted.text-right.col-md-2
|
||||
| {{VM.addresses["0/ip"]}}
|
||||
// /Pseudo host containing VMs not on any hosts.
|
||||
// /Hosts of this pool.
|
||||
// /Pool with its children.
|
||||
@@ -1,6 +1,17 @@
|
||||
'use strict'
|
||||
require 'angular'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.host', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'hosts_view',
|
||||
url: '/hosts/:id'
|
||||
controller: 'HostCtrl'
|
||||
template: require './view'
|
||||
.controller 'HostCtrl', ($scope, $stateParams, xoApi, xo, modal) ->
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
267
app/modules/host/view.jade
Normal file
@@ -0,0 +1,267 @@
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.page-title
|
||||
i.xo-icon-host(class="xo-color-{{host.power_state | lowercase}}")
|
||||
| {{host.name_label}}
|
||||
small(ng-if="pool.name_label")
|
||||
| (
|
||||
a(ui-sref="pools_view({id: pool.UUID})") {{pool.name_label}}
|
||||
| )
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-cogs(style="color: #e25440;")
|
||||
| General
|
||||
span.quick-edit(tooltip="Edit General settings", ng-click="hostSettings.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
.general-properties
|
||||
form(editable-form="", name="hostSettings", onbeforesave="saveHost($data)")
|
||||
dl.dl-horizontal
|
||||
dt Name
|
||||
dd
|
||||
span(editable-text="host.name_label", e-name="name_label", e-form="hostSettings")
|
||||
| {{host.name_label}}
|
||||
dt Description
|
||||
dd
|
||||
span(editable-text="host.name_description", e-name="name_description", e-form="hostSettings")
|
||||
| {{host.name_description}}
|
||||
dt Enabled
|
||||
dd
|
||||
span(editable-checkbox="host.enabled", e-name="enabled", e-form="hostSettings")
|
||||
| {{host.enabled}}
|
||||
dt Tags
|
||||
dd(ng-if="host.tags.length")
|
||||
span(ng-repeat="tag in host.tags")
|
||||
span.label.label-primary {{tag}}
|
||||
dd(ng-if="!host.tags.length")
|
||||
em No tags.
|
||||
dt CPUs
|
||||
dd {{host.CPUs["cpu_count"]}}x {{host.CPUs["modelname"]}}
|
||||
dt Hostname
|
||||
dd
|
||||
| {{host.hostname}}
|
||||
dt UUID
|
||||
dd {{host.UUID}}
|
||||
dt iQN
|
||||
dd {{host.iSCSI_name}}
|
||||
.btn-form(ng-show="hostSettings.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="hostSettings.$waiting", ng-click="hostSettings.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="submit", ng-disabled="hostSettings.$waiting")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-bar-chart-o(style="color: #e25440;")
|
||||
| Stats
|
||||
.grid
|
||||
.grid-cell
|
||||
p.stat-name vCPUs/CPU usage:
|
||||
p.center.big {{host.$vCPUs}}/{{host.CPUs["cpu_count"]}}
|
||||
.grid-cell
|
||||
p.stat-name RAM used:
|
||||
p.center.big {{[host.memory.usage, host.memory.size] | %}}
|
||||
.grid-cell
|
||||
p.stat-name Running VMs:
|
||||
p.center.big {{host.VMs.length}}
|
||||
// Action panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-flash(style="color: #e25440;")
|
||||
| Actions
|
||||
.grid-cell.text-center
|
||||
.grid
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Add SR", type="button", style="width: 90%", xo-click="new_sr()")
|
||||
i.xo-icon-sr.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Add VM", type="button", style="width: 90%", xo-sref="VMs_new({container: host.UUID})")
|
||||
i.xo-icon-vm.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Reboot host", type="button", style="width: 90%", xo-click="rebootHost(host.UUID)")
|
||||
i.fa.fa-refresh.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Shutdown host", type="button", style="width: 90%", xo-click="shutdownHost(host.UUID)")
|
||||
i.fa.fa-power-off.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Restart toolstack", type="button", style="width: 90%", xo-click="restartToolStack(host.UUID)")
|
||||
i.fa.fa-retweet.fa-2x.fa-fw
|
||||
.grid-cell.btn-group(ng-if="pool.name_label")
|
||||
button.btn(tooltip="Remove from pool", style="width: 90%", type="button", xo-click="pool_removeHost(host.UUID)")
|
||||
i.fa.fa-cloud-upload.fa-2x.fa-fw
|
||||
.grid-cell.btn-group(ng-if="!pool.name_label")
|
||||
button.btn(tooltip="Add to pool", style="width: 90%", type="button", xo-click="pool_addHost(host.UUID)")
|
||||
i.fa.fa-cloud-download.fa-2x.fa-fw
|
||||
.grid-cell.btn-group(style="margin-bottom: 0.5em")
|
||||
button.btn(tooltip="Host console", type="button", style="width: 90%", ng-repeat="controller in [host.controller] | resolve track by controller.UUID", xo-sref="consoles_view({id: controller.UUID})")
|
||||
i.xo-icon-console.fa-2x.fa-fw
|
||||
// TODO: Memory panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.xo-icon-memory(style="color: #e25440;")
|
||||
| Memory
|
||||
.progress
|
||||
.progress-bar-host(ng-repeat="controller in [host.controller] | resolve track by controller.UUID", role="progressbar", aria-valuemin="0", aria-valuenow="{{controller.memory.size}}", aria-valuemax="{{host.memory.size}}", style="width: {{[controller.memory.size, host.memory.size] | %}}", tooltip="{{host.name_label}}: {{[controller.memory.size, host.memory.size] | %}}")
|
||||
small {{host.name_label}}
|
||||
.progress-bar.progress-bar-vm(ng-repeat="VM in host.VMs | resolve | orderBy:natural('name_label') track by VM.UUID", role="progressbar", aria-valuemin="0", aria-valuenow="{{VM.memory.size}}", aria-valuemax="{{host.memory.size}}", style="width: {{[VM.memory.size, host.memory.size] | %}}", xo-sref="VMs_view({id: VM.UUID})", tooltip="{{VM.name_label}}: {{[VM.memory.size, host.memory.size] | %}}")
|
||||
small {{VM.name_label}}
|
||||
ul.list-inline.text-center
|
||||
li Total: {{host.memory.size | bytesToSize}}
|
||||
li Currently used: {{host.memory.usage | bytesToSize}}
|
||||
li Available: {{host.memory.size-host.memory.usage | bytesToSize}}
|
||||
// SR panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="hostSRs", onaftersave="saveTable()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.xo-icon-sr(style="color: #e25440;")
|
||||
| Storage
|
||||
span.quick-edit(tooltip="Edit storage", ng-click="hostSRs.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
table.table.table-hover
|
||||
tr
|
||||
th Name
|
||||
th Format
|
||||
th Size
|
||||
th Physical/Allocated usage
|
||||
th Type
|
||||
th Status
|
||||
// TODO: display PBD status for each SR of this host (connected or not)
|
||||
// Shared SR
|
||||
tr(xo-sref="SRs_view({id: SR.UUID})", ng-repeat="SR in pool.SRs | resolve | orderBy:natural('name_label') track by SR.UUID", ng-init="PBD = SRsToPBDs[SR.ref]")
|
||||
td
|
||||
| {{SR.name_label}}
|
||||
td {{SR.SR_type}}
|
||||
td {{SR.size | bytesToSize}}
|
||||
td
|
||||
.progress-condensed
|
||||
.progress-bar(role="progressbar", aria-valuemin="0", aria-valuenow="{{SR.usage}}", aria-valuemax="{{SR.size}}", style="width: {{[SR.physical_usage, SR.size] | %}}", tooltip="Physical usage: {{[SR.physical_usage, SR.size] | %}}")
|
||||
.progress-bar.progress-bar-info(role="progressbar", aria-valuemin="0", aria-valuenow="{{SR.physical_usage}}", aria-valuemax="{{SR.size}}", style="width: {{[(SR.usage-SR.physical_usage), SR.size] | %}}", tooltip="Allocated: {{[(SR.usage), SR.size] | %}}")
|
||||
td
|
||||
span.label.label-primary Shared
|
||||
td(ng-if="PBD.attached")
|
||||
span.label.label-success Connected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="hostSRs.$visible", ng-click="disconnectSR(SR.ref)", tooltip="Disconnect this SR")
|
||||
i.fa.fa-unlink.fa-lg
|
||||
td(ng-if="!PBD.attached")
|
||||
span.label.label-default Disconnected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="hostSRs.$visible", ng-click="removeSR(SR.ref)", tooltip="Remove this SR")
|
||||
i.fa.fa-trash-o.fa-lg
|
||||
// Local SR
|
||||
// TODO: migrate to SRs and not PBDs when implemented in xo-server spec
|
||||
tr(xo-sref="SRs_view({id: SR.UUID})", ng-repeat="SR in host.SRs | resolve | orderBy:natural('name_label') track by SR.UUID", ng-init="PBD = SRsToPBDs[SR.ref]")
|
||||
td
|
||||
| {{SR.name_label}}
|
||||
td {{SR.SR_type}}
|
||||
td {{SR.size | bytesToSize}}
|
||||
td
|
||||
.progress-condensed
|
||||
.progress-bar(role="progressbar", aria-valuemin="0", aria-valuenow="{{SR.usage}}", aria-valuemax="{{SR.size}}", style="width: {{[SR.physical_usage, SR.size] | %}}", tooltip="Physical usage: {{[SR.physical_usage, SR.size] | %}}")
|
||||
.progress-bar.progress-bar-info(role="progressbar", aria-valuemin="0", aria-valuenow="{{SR.physical_usage}}", aria-valuemax="{{SR.size}}", style="width: {{[(SR.usage-SR.physical_usage), SR.size] | %}}", tooltip="Allocated: {{[(SR.usage), SR.size] | %}}")
|
||||
td
|
||||
span.label.label-info Local
|
||||
td(ng-if="PBD.attached")
|
||||
span.label.label-success Connected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="hostSRs.$visible", ng-click="disconnectSR(SR.ref)", tooltip="Disconnect this SR")
|
||||
i.fa.fa-unlink.fa-lg
|
||||
td(ng-if="!PBD.attached")
|
||||
span.label.label-default Disabled
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="hostSRs.$visible", ng-click="removeSR(SR.ref)", tooltip="Remove this SR")
|
||||
i.fa.fa-trash-o.fa-lg
|
||||
.btn-form(ng-show="hostSRs.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="hostSRs.$waiting", ng-click="hostSRs.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="button", ng-disabled="hostSRs.$waiting", ng-click="saveChanges()")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
// Interfaces panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="hostNetwork", onaftersave="saveTable()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.xo-icon-network(style="color: #e25440;")
|
||||
| Interfaces
|
||||
span.quick-edit(tooltip="Edit network", ng-click="hostNetwork.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
table.table.table-hover
|
||||
th.col-md-1 Device
|
||||
th.col-md-1 Address
|
||||
th.col-md-2 MAC
|
||||
th.col-md-1 MTU
|
||||
th.col-md-1 Link status
|
||||
tr(ng-repeat="PIF in host.$PIFs | resolve | orderBy:natural('name_label') track by PIF.UUID")
|
||||
td
|
||||
| {{PIF.device}}
|
||||
span(ng-if="PIF.vlan > -1") .{{PIF.vlan}}
|
||||
span.label.label-primary(ng-if="PIF.management") XAPI
|
||||
td {{PIF.IP}} ({{PIF.mode}})
|
||||
td {{PIF.MAC}}
|
||||
td
|
||||
span(editable-text="PIF.MTU", e-name="mtu", e-form="hostNetwork", e-required="")
|
||||
| {{PIF.MTU}}
|
||||
td(ng-if="PIF.attached")
|
||||
span.label.label-success Connected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="hostNetwork.$visible", ng-click="disconnectSR(Network.ref)", tooltip="Disconnect this Network")
|
||||
i.fa.fa-unlink.fa-lg
|
||||
td(ng-if="!PIF.attached")
|
||||
span.label.label-default Disconnected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="hostNetwork.$visible", ng-click="removeNetwork(Network.ref)", tooltip="Remove this Network")
|
||||
i.fa.fa-trash-o.fa-lg
|
||||
.btn-form(ng-show="hostNetwork.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="hostNetwork.$waiting", ng-click="hostNetwork.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="button", ng-disabled="hostNetwork.$waiting", ng-click="saveChanges()")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
// CPU and Logs panels
|
||||
.grid
|
||||
// Task panel
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-tasks(style="color: #e25440;")
|
||||
| Pending tasks
|
||||
p.center(ng-if="!host.tasks.length") No recent tasks
|
||||
table.table.table-hover(ng-if="host.tasks.length")
|
||||
th.col-md-4 Date
|
||||
th.col-md-8 Name
|
||||
// TODO: working reverse order, from recent to oldest
|
||||
tr(ng-repeat="task in host.tasks | resolve | orderBy:'created':true track by task.UUID")
|
||||
td {{task.created}}
|
||||
td
|
||||
| {{task.name_label}}
|
||||
a.quick-remove(xo-click="removeTask(task.UUID)")
|
||||
i.fa.fa-trash-o.fa-fw
|
||||
// Logs panel
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="hostLogs", onaftersave="saveTable()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.fa.fa-comments(style="color: #e25440;")
|
||||
| Logs
|
||||
span.quick-edit(tooltip="Edit logs", ng-click="hostLogs.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
p.center(ng-if="!host.messages.length") No recent logs
|
||||
table.table.table-hover(ng-if="host.messages.length")
|
||||
th Date
|
||||
th Name
|
||||
tr(ng-repeat="message in host.messages | resolve | orderBy:'-time' track by message.UUID")
|
||||
td {{message.time*1e3 | date:"medium"}}
|
||||
td
|
||||
| {{message.name}}
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="hostLogs.$visible", ng-click="deleteLog(message.UUID)")
|
||||
i.fa.fa-trash-o.fa-lg(tooltip="Remove this log entry")
|
||||
.btn-form(ng-show="hostLogs.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="hostLogs.$waiting", ng-click="hostLogs.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
24
app/modules/list/index.js
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
//====================================================================
|
||||
|
||||
/* global angular:false */
|
||||
require('angular');
|
||||
require('angular-ui-router');
|
||||
|
||||
//====================================================================
|
||||
|
||||
module.exports = angular.module('xoWebApp.list', [
|
||||
'ui.router',
|
||||
])
|
||||
.config(function ($stateProvider) {
|
||||
$stateProvider.state('list', {
|
||||
url: '/list',
|
||||
controller: 'ListCtrl',
|
||||
template: require('./view'),
|
||||
});
|
||||
})
|
||||
.controller('ListCtrl', function ($scope, xo) {
|
||||
$scope.byTypes = xo.byTypes;
|
||||
})
|
||||
;
|
||||
161
app/modules/list/view.jade
Normal file
@@ -0,0 +1,161 @@
|
||||
// TODO: print a message when no entries.
|
||||
// If it's a (named) pool.
|
||||
.grid.flat-object(ng-repeat="pool in byTypes.pool | filter:listFilter | orderBy:natural('name_label') track by pool.UUID", ng-if="pool.name_label", xo-sref="pools_view({id: pool.UUID})")
|
||||
// Icon.
|
||||
.grid-cell.flat-cell.flat-cell-type
|
||||
i.xo-icon-pool
|
||||
// Properties & tags.
|
||||
.grid-cell
|
||||
// Properties.
|
||||
.grid
|
||||
.grid-cell
|
||||
.grid
|
||||
.grid-cell.flat-cell.flat-cell-name
|
||||
| {{pool.name_label}}
|
||||
.grid-cell.flat-cell.flat-cell-description
|
||||
i {{pool.name_description}}
|
||||
.grid-cell.flat-cell(ng-init="default_SR = (pool.default_SR | resolve)")
|
||||
div(ng-if="default_SR")
|
||||
| Default SR:
|
||||
a(ui-sref="SRs_view({id: default_SR.UUID})") {{default_SR.name_label}}
|
||||
div(ng-if="!default_SR")
|
||||
em No default SR.
|
||||
.grid-cell.flat-cell(ng-init="master = (pool.master | resolve)")
|
||||
div(ng-if="master")
|
||||
| Master:
|
||||
a(ui-sref="hosts_view({id: master.UUID})") {{master.name_label}}
|
||||
div(ng-if="!master")
|
||||
em Unknown master.
|
||||
.grid-cell.flat-cell
|
||||
div(ng-if="pool.HA_enabled")
|
||||
| HA enabled
|
||||
div(ng-if="!pool.HA_enabled")
|
||||
| HA disabled
|
||||
.grid-cell.flat-cell
|
||||
| {{pool.$running_hosts.length}}/{{pool.hosts.length}} hosts
|
||||
// /Properties.
|
||||
// Tags.
|
||||
.grid
|
||||
.grid-cell
|
||||
.grid-cell.flat-cell-tag
|
||||
i.fa.fa-tag
|
||||
span(ng-repeat="tag in pool.tags")
|
||||
span.label.label-primary {{tag}}
|
||||
// /Tags.
|
||||
// /Properties & tags.
|
||||
// /Pool.
|
||||
// If it's a host.
|
||||
.grid.flat-object(ng-repeat="host in byTypes.host | filter:listFilter | orderBy:natural('name_label') track by host.UUID", xo-sref="hosts_view({id: host.UUID})")
|
||||
// Icon.
|
||||
.grid-cell.flat-cell.flat-cell-type
|
||||
i.xo-icon-host(class="xo-color-{{host.power_state | lowercase}}")
|
||||
// Properties & tags.
|
||||
.grid-cell
|
||||
// Properties.
|
||||
.grid
|
||||
.grid-cell
|
||||
.grid
|
||||
.grid-cell.flat-cell.flat-cell-name
|
||||
| {{host.name_label}}
|
||||
.grid-cell.flat-cell.flat-cell-description
|
||||
i {{host.name_description}}
|
||||
.grid-cell.flat-cell
|
||||
| Address: {{host.address}}
|
||||
.grid-cell.flat-cell
|
||||
| {{host.$vCPUs}} vCPUs used on {{host.CPUs["cpu_count"]}} CPUs
|
||||
.grid-cell.flat-cell
|
||||
| {{host.memory.usage | bytesToSize}} used of {{host.memory.size | bytesToSize}} ({{[host.memory.usage, host.memory.size] | %}})
|
||||
.grid-cell.flat-cell
|
||||
| {{host.VMs.length}} VMs running
|
||||
// /Properties.
|
||||
// Tags.
|
||||
.grid
|
||||
.grid-cell
|
||||
.grid-cell.flat-cell-tag
|
||||
i.fa.fa-tag
|
||||
span(ng-repeat="tag in host.tags")
|
||||
span.label.label-primary {{tag}}
|
||||
// /Tags.
|
||||
// /Properties & tags.
|
||||
// /Host.
|
||||
// If it's a VM.
|
||||
.grid.flat-object(ng-repeat="VM in byTypes.VM | filter:listFilter | orderBy:natural('name_label') track by VM.UUID", xo-sref="VMs_view({id: VM.UUID})")
|
||||
// Icon.
|
||||
.grid-cell.flat-cell.flat-cell-type
|
||||
i.xo-icon-vm(class="xo-color-{{VM.power_state | lowercase}}")
|
||||
// Properties & tags.
|
||||
.grid-cell
|
||||
// Properties.
|
||||
.grid
|
||||
.grid-cell
|
||||
.grid
|
||||
.grid-cell.flat-cell.flat-cell-name
|
||||
| {{VM.name_label}}
|
||||
.grid-cell.flat-cell.flat-cell-description
|
||||
i {{VM.name_description}}
|
||||
.grid-cell.flat-cell
|
||||
| Address: {{VM.addresses["0/ip"]}}
|
||||
.grid-cell.flat-cell
|
||||
| {{VM.CPUs.number}} vCPUs
|
||||
.grid-cell.flat-cell
|
||||
| {{VM.memory.size | bytesToSize}} RAM
|
||||
.grid-cell.flat-cell(ng-init="container = (VM.$container | resolve)")
|
||||
div(ng-if="'pool' === container.type")
|
||||
| Resident on:
|
||||
a(ui-sref="pools_view({id: container.UUID})") {{container.name_label}}
|
||||
div(ng-if="'host' === container.type", ng-init="pool = (container.poolRef | resolve)")
|
||||
| Resident on:
|
||||
a(ui-sref="hosts_view({id: container.UUID})") {{container.name_label}}
|
||||
small(ng-if="pool.name_label")
|
||||
| (
|
||||
a(ui-sref="pools_view({id: pool.UUID})") {{pool.name_label}}
|
||||
| )
|
||||
// /Properties.
|
||||
// Tags.
|
||||
.grid
|
||||
.grid-cell
|
||||
.grid-cell.flat-cell-tag
|
||||
i.fa.fa-tag
|
||||
span(ng-repeat="tag in VM.tags")
|
||||
span.label.label-primary {{tag}}
|
||||
// /Tags.
|
||||
// /Properties & tags.
|
||||
// /VM.
|
||||
// If it's a SR.
|
||||
.grid.flat-object(ng-repeat="SR in byTypes.SR | filter:listFilter | orderBy:natural('name_label') track by SR.UUID", xo-sref="SRs_view({id: SR.UUID})")
|
||||
// Icon.
|
||||
.grid-cell.flat-cell.flat-cell-type
|
||||
i.xo-icon-sr
|
||||
// Properties & tags.
|
||||
.grid-cell
|
||||
// Properties.
|
||||
.grid
|
||||
.grid-cell
|
||||
.grid
|
||||
.grid-cell.flat-cell.flat-cell-name
|
||||
| {{SR.name_label}}
|
||||
.grid-cell.flat-cell.flat-cell-description
|
||||
i {{SR.name_description}}
|
||||
.grid-cell.flat-cell
|
||||
| Usage: {{[SR.usage, SR.size] | %}} ({{SR.usage | bytesToSize}}/{{SR.size | bytesToSize}})
|
||||
.grid-cell.flat-cell
|
||||
| Type: {{SR.SR_type}}
|
||||
.grid-cell.flat-cell(ng-init="container = (SR.$container | resolve)")
|
||||
div(ng-if="'pool' === container.type")
|
||||
strong
|
||||
| Shared on
|
||||
a(ui-sref="pools_view({id: container.UUID})") {{container.name_label}}
|
||||
div(ng-if="'host' === container.type")
|
||||
| Connected to
|
||||
a(ui-sref="hosts_view({id: container.UUID})") {{container.name_label}}
|
||||
// /Properties.
|
||||
// Tags.
|
||||
.grid
|
||||
.grid-cell
|
||||
.grid-cell.flat-cell-tag
|
||||
i.fa.fa-tag
|
||||
span(ng-repeat="tag in SR.tags")
|
||||
span.label.label-primary {{tag}}
|
||||
// /Tags.
|
||||
// /Properties & tags.
|
||||
// /SR.
|
||||
33
app/modules/login/index.coffee
Normal file
@@ -0,0 +1,33 @@
|
||||
require 'angular'
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.login', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'login',
|
||||
url: '/login'
|
||||
controller: 'LoginCtrl'
|
||||
template: require './view'
|
||||
.controller 'LoginCtrl', ($scope, $state, $rootScope, xoApi) ->
|
||||
toState = $rootScope._login?.state.name ? 'home'
|
||||
toStateParams = $rootScope._login?.stateParams
|
||||
delete $rootScope._login
|
||||
|
||||
$scope.$watch(
|
||||
-> xoApi.user
|
||||
(user) ->
|
||||
# When the user is logged in, go the wanted view, fallbacks on
|
||||
# the home view if necessary.
|
||||
if user
|
||||
$state.go toState, toStateParams
|
||||
.catch ->
|
||||
$state.go 'home'
|
||||
)
|
||||
|
||||
Object.defineProperties $scope,
|
||||
user: get: -> xoApi.get
|
||||
status: get: -> xoApi.status
|
||||
$scope.logIn = xoApi.logIn
|
||||
50
app/modules/login/view.jade
Normal file
@@ -0,0 +1,50 @@
|
||||
//- Hide the navbar for this view.
|
||||
style.
|
||||
.navbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.container
|
||||
div.row-login
|
||||
div.page-header
|
||||
img(src = 'images/logo_small.png')
|
||||
h2 Xen Orchestra
|
||||
form.form-horizontal(
|
||||
ng-submit = 'logIn(email, password, true)'
|
||||
)
|
||||
fieldset
|
||||
legend.login: h3 Sign in
|
||||
div.form-group
|
||||
div.col-sm-12
|
||||
input.form-control.input-sm(
|
||||
name = 'email'
|
||||
type = 'text'
|
||||
placeholder = 'Username'
|
||||
ng-model = 'email'
|
||||
required
|
||||
fix-autofill
|
||||
)
|
||||
div.form-group
|
||||
div.col-sm-12
|
||||
input.form-control.input-sm(
|
||||
name = 'password'
|
||||
type = 'password'
|
||||
placeholder = 'Password'
|
||||
ng-model = 'password'
|
||||
required
|
||||
fix-autofill
|
||||
)
|
||||
div.form-group
|
||||
div.col-sm-12
|
||||
button.btn.btn-login.btn-block.btn-success(
|
||||
id = 'login'
|
||||
name = 'login'
|
||||
)
|
||||
i.fa.fa-sign-in
|
||||
| Login
|
||||
p.status(ng-if = '"disconnected" === status')
|
||||
i.xo-icon-error.fa-2x(tooltip = 'You are not connected to XO-Server')
|
||||
p.status(ng-if = '"connecting" === status')
|
||||
i.fa.fa-refresh.fa-spin.fa-2x(tooltip = 'Connecting to XO-Server')
|
||||
p.status(ng-if = '"connected" === status')
|
||||
i.xo-icon-success.fa-2x(tooltip = 'You are connected to XO-Server')
|
||||
33
app/modules/navbar/index.coffee
Normal file
@@ -0,0 +1,33 @@
|
||||
require 'angular'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.navbar', []
|
||||
.controller 'NavbarCtrl', ($scope, $state, xoApi) ->
|
||||
# TODO: It would make sense to inject xoApi in the scope.
|
||||
$scope.$watch(
|
||||
-> xoApi.status
|
||||
(status) ->
|
||||
$scope.status = status
|
||||
)
|
||||
$scope.$watch(
|
||||
-> xoApi.user
|
||||
(user) ->
|
||||
$scope.user = user
|
||||
)
|
||||
$scope.logIn = xoApi.logIn
|
||||
$scope.logOut = ->
|
||||
xoApi.logOut()
|
||||
$state.go 'login'
|
||||
|
||||
# When a searched is entered, we must switch to the list view if
|
||||
# necessary.
|
||||
$scope.ensureListView = ->
|
||||
$state.go 'list'
|
||||
.directive 'navbar', ->
|
||||
return {
|
||||
restrict: 'E'
|
||||
controller: 'NavbarCtrl'
|
||||
template: require './view'
|
||||
replace: true
|
||||
}
|
||||
97
app/modules/navbar/view.jade
Normal file
@@ -0,0 +1,97 @@
|
||||
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
|
||||
//- Brand and toggle get grouped for better mobile display
|
||||
.navbar-header
|
||||
//- Button used to (un)collapse on mobile display.
|
||||
button.navbar-toggle(type="button", ng-init="collapsed = true", ng-click="collapsed = !collapsed")
|
||||
span.sr-only Toggle navigation
|
||||
span.icon-bar
|
||||
span.icon-bar
|
||||
span.icon-bar
|
||||
//- Brand name.
|
||||
a.navbar-brand(ui-sref="home") Xen Orchestra
|
||||
//- All navbar items are collapsed on mobile display.
|
||||
.collapse.navbar-collapse(ng-class="!collapsed && 'in'")
|
||||
//- Search form of the navbar.
|
||||
form.navbar-form.navbar-left(role="search", style="width: 250px")
|
||||
//- Forced width due to issue with `input`s (https://github.com/twbs/bootstrap/issues/9950.
|
||||
.input-group
|
||||
input.form-control.inverse(type="text", placeholder="", ng-model="$root.listFilter", ng-change="ensureListView()")
|
||||
span.input-group-btn
|
||||
button.btn.btn-search(type="button", ng-click="ensureListView()")
|
||||
i.fa.fa-search
|
||||
//- /Search form.
|
||||
ul.nav.navbar-nav
|
||||
li
|
||||
a(href="https://xen-orchestra.com/services?from=xoa", target="_blank")
|
||||
i.xo-icon-info
|
||||
| Unregistered version
|
||||
//- Right items of the navbar.
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
li.navbar-text(ng-if="'disconnected' === status")
|
||||
i.xo-icon-error
|
||||
| Disconnected from XO-Server
|
||||
li.navbar-text(ng-if="'connecting' === status")
|
||||
i.fa.fa-refresh.fa-spin
|
||||
| Connecting to XO-Server
|
||||
//- Main menu.
|
||||
li.dropdown
|
||||
a.dropdown-toggle.inverse
|
||||
i.fa.fa-th
|
||||
ul.dropdown-menu.inverse
|
||||
li(ui-sref-active="active", ui-route="/")
|
||||
a(ui-sref="home")
|
||||
i.fa.fa-indent
|
||||
| Tree view
|
||||
li(ui-sref-active="active", ui-route="/list")
|
||||
a(ui-sref="list")
|
||||
i.fa.fa-align-justify
|
||||
| Flat view
|
||||
li.disabled(ui-sref-active="active", ui-route="/graph")
|
||||
a(ui-sref="graph")
|
||||
i.fa.fa-sitemap
|
||||
| Graphs view
|
||||
li.divider
|
||||
li.disabled
|
||||
a
|
||||
i.fa.fa-clock-o
|
||||
| Scheduler
|
||||
li(ui-sref-active="active", ui-route="/settings")
|
||||
a(ui-sref="settings")
|
||||
i.fa.fa-cog
|
||||
| Settings
|
||||
li.divider
|
||||
li(ui-sref-active="active", ui-route="/about")
|
||||
a(ui-sref="about")
|
||||
i.fa.fa-info-circle(style="color:#5bc0de")
|
||||
| About
|
||||
//- /Main menu.
|
||||
//- Displayed only when the user is connected.
|
||||
li(ng-if="user")
|
||||
a
|
||||
i.fa.fa-user
|
||||
| {{user.email}}
|
||||
li(ng-if="user")
|
||||
a(ng-click="logOut()")
|
||||
i.fa.fa-sign-out
|
||||
//- /When user is connected.
|
||||
//- Displayed only when the user is not connected.
|
||||
li.dropdown(ng-if="!user")
|
||||
a.dropdown-toggle
|
||||
| Log in
|
||||
i.fa.fa-sign-in
|
||||
form.dropdown-menu.login-form-dark(ng-submit="logIn(login.email, login.password, true)", ng-click="$event.stopPropagation()")
|
||||
.input-group
|
||||
span.input-group-addon
|
||||
i.fa.fa-user.fa-fw
|
||||
input.form-control(type="text", placeholder="Email", ng-model="login.email", name="email", required="")
|
||||
.input-group
|
||||
span.input-group-addon
|
||||
i.fa.fa-key.fa-fw
|
||||
input.form-control(type="password", placeholder="Password", name="password", ng-model="login.password", required="")
|
||||
button.btn.btn-primary.btn-block(type="submit")
|
||||
i.fa.fa-sign-in
|
||||
| Log in
|
||||
//- /When user is not connected.
|
||||
//- /Right items.
|
||||
//- /Navbar items.
|
||||
//- /Navbar.
|
||||
21
app/modules/new-sr/index.coffee
Normal file
@@ -0,0 +1,21 @@
|
||||
require 'angular'
|
||||
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.newSr', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'SRs_new',
|
||||
url: '/srs/new/:container'
|
||||
controller: 'NewSrCtrl'
|
||||
template: require './view'
|
||||
.controller 'NewSrCtrl', ($scope, $stateParams, xo) ->
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
->
|
||||
$scope.container = xo.get $stateParams.container
|
||||
)
|
||||
|
||||
44
app/modules/new-sr/view.jade
Normal file
@@ -0,0 +1,44 @@
|
||||
// TODO: lots of stuff.
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.page-title
|
||||
i.xo-icon-vm
|
||||
| Add SR on {{container.name_label}}
|
||||
// Add server panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-info-circle(style="color: #e25440;")
|
||||
| SR info
|
||||
form.form-horizontal(role="form")
|
||||
.form-group
|
||||
label.col-sm-3.control-label Type
|
||||
.col-sm-9
|
||||
select.form-control
|
||||
option(value="") -- Choose a type of SR --
|
||||
optgroup(label="VDI SR")
|
||||
option(value="NFS") NFS
|
||||
option(value="ISCSI") Software iSCSI
|
||||
option(value="HBA") HBA
|
||||
optgroup(label="ISO SR")
|
||||
option(value="CIFSISO") CIFS
|
||||
option(value="NFSISO") NFS
|
||||
.form-group
|
||||
label.col-sm-3.control-label Name
|
||||
.col-sm-9
|
||||
input.form-control(type="text", placeholder="")
|
||||
.form-group
|
||||
label.col-sm-3.control-label Description
|
||||
.col-sm-9
|
||||
input.form-control(type="text", placeholder="SR Created by Xen Orchestra")
|
||||
p.text-center
|
||||
a.btn.btn-primary.big
|
||||
i.fa.fa-times
|
||||
| Clear
|
||||
a.btn.btn-success.big
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-link(style="color: #e25440;")
|
||||
| Connection
|
||||
@@ -1,7 +1,18 @@
|
||||
'use strict'
|
||||
require 'angular'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'NewVmCtrl', (
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.newVms', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'VMs_new',
|
||||
url: '/vms/new/:container'
|
||||
controller: 'NewVms'
|
||||
template: require './view'
|
||||
.controller 'NewVms', (
|
||||
$scope, $stateParams, $state
|
||||
xoApi, xo
|
||||
bytesToSizeFilter, sizeToBytesFilter
|
||||
@@ -159,18 +170,21 @@ angular.module('xoWebApp')
|
||||
VIF.MAC = VIF.MAC.trim()
|
||||
delete VIF.MAC unless VIF.MAC
|
||||
|
||||
|
||||
if installation_method is 'cdrom'
|
||||
installation = {
|
||||
method: 'cdrom'
|
||||
repository: installation_cdrom
|
||||
}
|
||||
else
|
||||
else if installation_network
|
||||
matches = /^(http|ftp|nfs)/i.exec installation_network
|
||||
# FIXME: handles invalid methods.
|
||||
throw new Error 'invalid network URL' unless matches
|
||||
installation = {
|
||||
method: matches[1].toLowerCase()
|
||||
repository: installation_network
|
||||
}
|
||||
else
|
||||
installation = undefined
|
||||
|
||||
data = {
|
||||
installation
|
||||
197
app/modules/new-vm/view.jade
Normal file
@@ -0,0 +1,197 @@
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.page-title
|
||||
i.xo-icon-vm
|
||||
| Add VM on
|
||||
a(ng-if="'pool' === container.type", ui-sref="pools_view({id: container.UUID})")
|
||||
| {{container.name_label}}
|
||||
a(ng-if="'host' === container.type", ui-sref="hosts_view({id: container.UUID})")
|
||||
| {{container.name_label}}
|
||||
//- Add server panel
|
||||
form.form-horizontal(ng-submit="createVM()")
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-info-circle(style="color: #e25440;")
|
||||
| VM info
|
||||
.form-group
|
||||
label.col-sm-3.control-label Template
|
||||
.col-sm-9
|
||||
select.form-control(ng-model="template", ng-options="template.name_label for template in templates | orderBy:natural('name_label') track by template.UUID", required="")
|
||||
.form-group
|
||||
label.col-sm-3.control-label Name
|
||||
.col-sm-9
|
||||
input.form-control(type="text", placeholder="Name of your new VM", required="", ng-model="name_label")
|
||||
.form-group
|
||||
label.col-sm-3.control-label Description
|
||||
.col-sm-9
|
||||
input.form-control(type="text", placeholder="Optional description of you new VM", ng-model="name_description")
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-dashboard(style="color: #e25440;")
|
||||
| Performances
|
||||
.form-group
|
||||
label.col-sm-3.control-label vCPUs
|
||||
.col-sm-9
|
||||
input.form-control(type="text", placeholder="{{template.CPUs.number}}", ng-model="CPUs")
|
||||
.form-group
|
||||
label.col-sm-3.control-label RAM
|
||||
.col-sm-9
|
||||
input.form-control(type="text", placeholder="{{template.memory.size | bytesToSize}}", ng-model="memory")
|
||||
.grid
|
||||
//- Install panel
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-download(style="color: #e25440;")
|
||||
| Install settings
|
||||
.form-group(ng-show="availableMethods.cdrom")
|
||||
label.col-sm-3.control-label ISO/DVD
|
||||
.col-sm-9
|
||||
.input-group
|
||||
span.input-group-addon
|
||||
input(
|
||||
type = 'radio'
|
||||
name = 'installation_method'
|
||||
ng-model = 'installation_method'
|
||||
value = 'cdrom'
|
||||
required
|
||||
)
|
||||
select.form-control.disabled(
|
||||
ng-disabled="'cdrom' !== installation_method"
|
||||
ng-model="installation_cdrom"
|
||||
required
|
||||
)
|
||||
option(value = '') Please select
|
||||
optgroup(ng-repeat="SR in ISO_SRs | orderBy:natural('name_label') track by SR.UUID", ng-if="SR.VDIs.length", label="{{SR.name_label}}")
|
||||
option(ng-repeat="VDI in SR.VDIs | resolve | orderBy:natural('name_label') track by VDI.UUID", ng-value="VDI.UUID")
|
||||
| {{VDI.name_label}}
|
||||
.form-group(ng-show="availableMethods.http || availableMethods.ftp || availableMethods.nfs")
|
||||
label.col-sm-3.control-label Network
|
||||
.col-sm-9
|
||||
.input-group
|
||||
span.input-group-addon
|
||||
input(
|
||||
type = 'radio'
|
||||
name = 'installation_method'
|
||||
ng-model = 'installation_method'
|
||||
value = 'network'
|
||||
required
|
||||
)
|
||||
input.form-control(type="text", ng-disabled="'network' !== installation_method", placeholder="e.g: http://ftp.debian.org/debian", ng-model="installation_network")
|
||||
|
||||
//- <div class="form-group"> FIXME
|
||||
//- <label class="col-sm-3 control-label">Home server</label>
|
||||
//- <div class="col-sm-9">
|
||||
//- <select class="form-control">
|
||||
//- <option>Default (auto)</option>
|
||||
//- </select>
|
||||
//- </div>
|
||||
//- </div>
|
||||
|
||||
//- Interface panel
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.xo-icon-network(style="color: #e25440;")
|
||||
| Interfaces
|
||||
table.table.table-hover
|
||||
tr
|
||||
th MAC
|
||||
th Network
|
||||
th.col-md-1  
|
||||
//- Buttons
|
||||
tr(ng-repeat="VIF in VIFs track by VIF.id")
|
||||
td
|
||||
input.form-control(type="text", ng-model="VIF.MAC", ng-pattern="/^\s*[0-9a-f]{2}(:[0-9a-f]{2}){5}\s*$/i", placeholder="00:00:00:00:00")
|
||||
td
|
||||
select.form-control(
|
||||
ng-options = 'network.UUID as network.name_label for network in (networks | orderBy:natural("name_label"))'
|
||||
ng-model = 'VIF.network'
|
||||
required
|
||||
)
|
||||
option(value = '') Please select
|
||||
td
|
||||
.pull-right
|
||||
button.btn.btn-default(type="button", ng-click="removeVIF($index)", title="Remove this interface")
|
||||
i.fa.fa-times
|
||||
.btn-form
|
||||
p.center
|
||||
.btn-form
|
||||
p.center
|
||||
button.btn.btn-success(type="button", ng-click="addVIF()")
|
||||
i.fa.fa-plus
|
||||
| Add interface
|
||||
//- end of misc and interface panel
|
||||
//- Disk panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.xo-icon-sr(style="color: #e25440;")
|
||||
| Disks
|
||||
table.table.table-hover
|
||||
tr
|
||||
th.col-md-2 SR
|
||||
th.col-md-1 Bootable?
|
||||
th.col-md-2 Size
|
||||
th.col-md-2 Name
|
||||
th.col-md-4 Description
|
||||
th.col-md-1  
|
||||
//- Buttons
|
||||
tr(ng-repeat="VDI in VDIs track by VDI.id")
|
||||
td
|
||||
select.form-control(ng-model="VDI.SR", ng-options="SR.UUID as SR.name_label for SR in (writable_SRs | orderBy:natural('name_label'))")
|
||||
td.text-center
|
||||
input(type="checkbox", ng-model="VDI.bootable")
|
||||
td
|
||||
input.form-control(type="text", ng-model="VDI.size", required="")
|
||||
td
|
||||
input.form-control(type="text", placeholder="Name of this virtual disk", ng-model="VDI.name_label")
|
||||
td
|
||||
input.form-control(type="text", placeholder="Description of this virtual disk", ng-model="VDI.name_description")
|
||||
td
|
||||
.btn-group
|
||||
button.btn.btn-default(type="button", ng-click="moveVDI($index, -1)", ng-disabled="$first", title="Move this disk up")
|
||||
i.fa.fa-chevron-up
|
||||
button.btn.btn-default(type="button", ng-click="moveVDI($index, 1)", ng-disabled="$last", title="Move this disk down")
|
||||
i.fa.fa-chevron-down
|
||||
.pull-right
|
||||
button.btn.btn-default(type="button", ng-click="removeVDI($index)", title="Remove this disk")
|
||||
i.fa.fa-times
|
||||
.btn-form
|
||||
p.center
|
||||
.btn-form
|
||||
p.center
|
||||
button.btn.btn-success(type="button", ng-click="addVDI()")
|
||||
i.fa.fa-plus
|
||||
| Add disk
|
||||
//- Confirmation panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-flag-checkered(style="color: #e25440;")
|
||||
| Summary
|
||||
.grid
|
||||
.grid-cell
|
||||
p.stat-name
|
||||
| Name:
|
||||
p.center.big {{name_label}}
|
||||
.grid-cell
|
||||
p.stat-name
|
||||
| Template:
|
||||
p.center {{template.name_label}}
|
||||
.grid
|
||||
.grid-cell
|
||||
p.stat-name vCPUs
|
||||
p.center.big {{CPUs || template.CPUs.number}}
|
||||
.grid-cell
|
||||
p.stat-name RAM
|
||||
p.center.big {{(memory) || (template.memory.size | bytesToSize)}}
|
||||
.grid-cell
|
||||
p.stat-name Disks
|
||||
p.center.big {{(VDIs.length) || (template.$VBDs.length) || 0}}
|
||||
.grid-cell
|
||||
p.stat-name Interfaces
|
||||
p.center.big {{VIFs.length}}
|
||||
p.center
|
||||
button.btn.btn-lg.btn-primary(type="submit")
|
||||
i.fa.fa-play
|
||||
| Create VM
|
||||
@@ -1,6 +1,17 @@
|
||||
'use strict'
|
||||
require 'angular'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.pool', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'pools_view',
|
||||
url: '/pools/:id'
|
||||
controller: 'PoolCtrl'
|
||||
template: require './view'
|
||||
.controller 'PoolCtrl', ($scope, $stateParams, xoApi, xo) ->
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
137
app/modules/pool/view.jade
Normal file
@@ -0,0 +1,137 @@
|
||||
// TODO: lots of stuff.
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.page-title
|
||||
i.xo-icon-pool
|
||||
| {{pool.name_label}}
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-cogs(style="color: #e25440;")
|
||||
| General
|
||||
span.quick-edit(tooltip="Edit General settings", ng-click="poolSettings.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
.general-properties
|
||||
form(editable-form="", name="poolSettings", onbeforesave="savePool($data)")
|
||||
dl.dl-horizontal
|
||||
dt Name
|
||||
dd
|
||||
span(editable-text="pool.name_label", e-name="name_label", e-form="poolSettings")
|
||||
| {{pool.name_label}}
|
||||
dt Description
|
||||
dd
|
||||
span(editable-text="pool.name_description", e-name="name_description", e-form="poolSettings")
|
||||
| {{pool.name_description}}
|
||||
dt Master
|
||||
dd(ng-repeat="master in [pool.master] | resolve")
|
||||
a(ui-sref="hosts_view({id: master.UUID})")
|
||||
| {{master.name_label}}
|
||||
dt Tags
|
||||
dd
|
||||
span(ng-repeat="tag in pool.tags")
|
||||
span.label.label-primary {{tag}}
|
||||
dt(ng-if="pool.default_SR") Default SR
|
||||
dd(ng-if="pool.default_SR", ng-init="default_SR = (pool.default_SR | resolve)")
|
||||
a(ui-sref="SRs_view({id: default_SR.UUID})") {{default_SR.name_label}}
|
||||
dt HA
|
||||
dd
|
||||
| {{pool.HA_enabled}}
|
||||
dt UUID
|
||||
dd {{pool.UUID}}
|
||||
.btn-form(ng-show="poolSettings.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="poolSettings.$waiting", ng-click="poolSettings.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="submit", ng-disabled="poolSettings.$waiting")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-bar-chart-o(style="color: #e25440;")
|
||||
| Stats
|
||||
.grid
|
||||
.grid-cell
|
||||
p.stat-name Hosts:
|
||||
p.center.big {{pool.hosts.length}}
|
||||
.grid-cell
|
||||
p.stat-name Running:
|
||||
p.center.big {{pool.$running_hosts.length}}
|
||||
// Action panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-flash(style="color: #e25440;")
|
||||
| Actions
|
||||
.grid
|
||||
.grid-cell.text-center.grid-button(tooltip="Add SR", xo-sref="SRs_new({container: pool.UUID})")
|
||||
i.xo-icon-sr.fa-2x.fa-fw
|
||||
.grid-cell.text-center.grid-button(tooltip="Add VM", xo-sref="VMs_new({container: pool.UUID})")
|
||||
i.xo-icon-vm.fa-2x.fa-fw
|
||||
.grid-cell.text-center.grid-button(tooltip="Add Host")
|
||||
i.xo-icon-host.fa-2x.fa-fw
|
||||
.grid-cell.text-center.grid-button(tooltip="Disconnect")
|
||||
i.fa.fa-unlink.fa-2x.fa-fw
|
||||
// Hosts panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.xo-icon-host(style="color: #e25440;")
|
||||
| Hosts
|
||||
table.table.table-hover.table-condensed
|
||||
th Name
|
||||
th.col-md-4 Description
|
||||
th.col-md-6 Memory
|
||||
tr(xo-sref="hosts_view({id: host.UUID})", ng-repeat="host in pool.hosts | resolve | orderBy:natural('name_label') track by host.UUID")
|
||||
td {{host.name_label}}
|
||||
td {{host.name_description}}
|
||||
td
|
||||
.progress-condensed
|
||||
.progress-bar(role="progressbar", aria-valuemin="0", aria-valuenow="{{host.memory.usage}}", aria-valuemax="{{host.memory.size}}", style="width: {{[host.memory.usage, host.memory.size] | %}}")
|
||||
// Shared SR panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.xo-icon-sr(style="color: #e25440;")
|
||||
| Shared SR
|
||||
table.table.table-hover
|
||||
th Name
|
||||
th Description
|
||||
th Type
|
||||
th Size
|
||||
th.col-md-4 Physical/Allocated usage
|
||||
tr(xo-sref="SRs_view({id: SR.UUID})", ng-repeat="SR in pool.SRs | resolve | orderBy:natural('name_label') track by SR.UUID")
|
||||
td {{SR.name_label}}
|
||||
td {{SR.name_description}}
|
||||
td {{SR.SR_type}}
|
||||
td {{SR.size | bytesToSize}}
|
||||
td
|
||||
.progress-condensed
|
||||
.progress-bar(role="progressbar", aria-valuemin="0", aria-valuenow="{{SR.usage}}", aria-valuemax="{{SR.size}}", style="width: {{[SR.physical_usage, SR.size] | %}}", tooltip="Physical usage: {{[SR.physical_usage, SR.size] | %}}")
|
||||
.progress-bar.progress-bar-info(role="progressbar", aria-valuemin="0", aria-valuenow="{{SR.physical_usage}}", aria-valuemax="{{SR.size}}", style="width: {{[(SR.usage-SR.physical_usage), SR.size] | %}}", tooltip="Allocated: {{[(SR.usage), SR.size] | %}}")
|
||||
// TODO: CPU and Logs panels
|
||||
.grid
|
||||
// Logs panel
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="poolLogs", onaftersave="saveTable()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.fa.fa-comments(style="color: #e25440;")
|
||||
| Logs
|
||||
span.quick-edit(tooltip="Edit logs", ng-click="poolLogs.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
p.center(ng-if="!pool.messages.length") No recent logs
|
||||
table.table.table-hover(ng-if="pool.messages.length")
|
||||
th Date
|
||||
th Name
|
||||
tr(ng-repeat="message in pool.messages | resolve | orderBy:'-time' track by message.UUID")
|
||||
td {{message.time*1e3 | date:"medium"}}
|
||||
td
|
||||
| {{message.name}}
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="poolLogs.$visible", ng-click="deleteLog(message.UUID)")
|
||||
i.fa.fa-trash-o.fa-lg(tooltip="Remove this log entry")
|
||||
.btn-form(ng-show="poolLogs.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="poolLogs.$waiting", ng-click="poolLogs.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
@@ -1,9 +1,19 @@
|
||||
'use strict'
|
||||
require 'angular'
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
# FIXME: Mutualize the code between users and servers.
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'SettingsCtrl', ($scope, xoApi) ->
|
||||
module.exports = angular.module 'xoWebApp.settings', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'settings',
|
||||
url: '/settings'
|
||||
controller: 'SettingsCtrl'
|
||||
template: require './view'
|
||||
.controller 'SettingsCtrl', ($scope, xo) ->
|
||||
$scope.permissions = [
|
||||
{
|
||||
label: 'None'
|
||||
@@ -27,7 +37,7 @@ angular.module('xoWebApp')
|
||||
do ->
|
||||
# Fetches them.
|
||||
$scope.users = []
|
||||
xoApi.call('user.getAll').then (users) ->
|
||||
xo.user.getAll().then (users) ->
|
||||
$scope.users = users
|
||||
|
||||
# Which ones are selected?
|
||||
@@ -59,13 +69,15 @@ angular.module('xoWebApp')
|
||||
{id} = user
|
||||
if selected[id]
|
||||
delete selected[id]
|
||||
xoApi.call 'user.delete', {id}
|
||||
|
||||
# FIXME: this cast should not be necessary.
|
||||
xo.user.delete "#{id}"
|
||||
else
|
||||
# Only sets the password if not empty.
|
||||
delete user.password unless user.password
|
||||
|
||||
# TODO: only update users which have been modified.
|
||||
xoApi.call 'user.set', user
|
||||
xo.user.set user
|
||||
|
||||
# Remove the password from the interface.
|
||||
delete user.password
|
||||
@@ -79,7 +91,7 @@ angular.module('xoWebApp')
|
||||
continue unless email
|
||||
|
||||
# Sends the order to XO-Server.
|
||||
xoApi.call 'user.create', {email, permission, password}
|
||||
xo.user.create {email, permission, password}
|
||||
|
||||
# The password should not be displayed.
|
||||
delete user.password
|
||||
@@ -96,7 +108,7 @@ angular.module('xoWebApp')
|
||||
do ->
|
||||
# Fetches them.
|
||||
$scope.servers = []
|
||||
xoApi.call('server.getAll').then (servers) ->
|
||||
xo.server.getAll().then (servers) ->
|
||||
$scope.servers = servers
|
||||
|
||||
# Which ones are selected?
|
||||
@@ -125,13 +137,13 @@ angular.module('xoWebApp')
|
||||
{id} = server
|
||||
if selected[id]
|
||||
delete selected[id]
|
||||
xoApi.call 'server.remove', {id}
|
||||
xo.server.remove id
|
||||
else
|
||||
# Only sets the password if not empty.
|
||||
delete server.password unless server.password
|
||||
|
||||
# TODO: only update servers which have been modified.
|
||||
xoApi.call 'server.set', server
|
||||
xo.server.set server
|
||||
|
||||
# Remove the password from the interface.
|
||||
delete server.password
|
||||
@@ -145,7 +157,7 @@ angular.module('xoWebApp')
|
||||
continue unless host
|
||||
|
||||
# Sends the order to XO-Server.
|
||||
xoApi.call 'server.add', {host, username, password}
|
||||
xo.server.add {host, username, password}
|
||||
|
||||
# The password should not be displayed.
|
||||
delete server.password
|
||||
78
app/modules/settings/view.jade
Normal file
@@ -0,0 +1,78 @@
|
||||
// TODO: lots of stuff.
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.page-title
|
||||
i.fa.fa-cog
|
||||
| XO Settings
|
||||
// Add server panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-link(style="color: #e25440;")
|
||||
| Connected servers
|
||||
form(ng-submit="saveServers()", autocomplete="off")
|
||||
table.table.table-hover
|
||||
tr
|
||||
th Host
|
||||
th User
|
||||
th Password
|
||||
th Delete
|
||||
tr(ng-repeat="server in servers | orderBy:natural('host') track by server.id")
|
||||
td
|
||||
input.form-control(type="text", ng-model="server.host")
|
||||
td
|
||||
input.form-control(type="text", ng-model="server.username")
|
||||
td
|
||||
input.form-control(type="password", ng-model="server.password", placeholder="Fill to change the password")
|
||||
td
|
||||
input(type="checkbox", ng-model="selectedServers[server.id]")
|
||||
tr(ng-repeat="server in newServers")
|
||||
td
|
||||
input.form-control(type="text", ng-model="server.host", placeholder="address[:port]")
|
||||
td
|
||||
input.form-control(type="text", ng-model="server.username", placeholder="user")
|
||||
td
|
||||
input.form-control(type="password", ng-model="server.password", placeholder="password")
|
||||
td  
|
||||
p.text-center
|
||||
button.btn.btn-primary(type="submit")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
|
|
||||
button.btn.btn-success(type="button", ng-click="addServer()")
|
||||
i.fa.fa-plus
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-users(style="color: #e25440;")
|
||||
| Users
|
||||
form(ng-submit="saveUsers()", autocomplete="off")
|
||||
table.table.table-hover
|
||||
tr
|
||||
th Email
|
||||
th Permissions
|
||||
th Password
|
||||
th Delete
|
||||
tr(ng-repeat="user in users | orderBy:natural('email') track by user.id")
|
||||
td
|
||||
input.form-control(type="text", ng-model="user.email")
|
||||
td
|
||||
select.form-control(ng-options="p.value as p.label for p in permissions", ng-model="user.permission")
|
||||
td
|
||||
input.form-control(type="password", ng-model="user.password", placeholder="Fill to change the password")
|
||||
td
|
||||
input(type="checkbox", ng-model="selectedUsers[user.id]")
|
||||
tr(ng-repeat="user in newUsers")
|
||||
td
|
||||
input.form-control(type="text", ng-model="user.email", placeholder="email")
|
||||
td
|
||||
select.form-control(ng-options="p.value as p.label for p in permissions", ng-model="user.permission")
|
||||
td
|
||||
input.form-control(type="password", ng-model="user.password", placeholder="password")
|
||||
td  
|
||||
p.text-center
|
||||
button.btn.btn-primary(type="submit")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
|
|
||||
button.btn.btn-success(type="button", ng-click="addUser()")
|
||||
i.fa.fa-plus
|
||||
@@ -1,6 +1,17 @@
|
||||
'use strict'
|
||||
require 'angular'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.sr', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'SRs_view',
|
||||
url: '/srs/:id'
|
||||
controller: 'SrCtrl'
|
||||
template: require './view'
|
||||
.controller 'SrCtrl', ($scope, $stateParams, xoApi, xo, modal) ->
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
166
app/modules/sr/view.jade
Normal file
@@ -0,0 +1,166 @@
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.page-title
|
||||
i.xo-icon-sr
|
||||
| {{SR.name_label}}
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-cogs(style="color: #e25440;")
|
||||
| General
|
||||
span.quick-edit(tooltip="Edit General settings", ng-click="srSettings.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
.general-properties
|
||||
form(editable-form="", name="srSettings", onbeforesave="saveSR($data)")
|
||||
dl.dl-horizontal
|
||||
dt Name
|
||||
dd
|
||||
span(editable-text="SR.name_label", e-name="name_label", e-form="srSettings")
|
||||
| {{SR.name_label}}
|
||||
dt Description
|
||||
dd
|
||||
span(editable-text="SR.name_description", e-name="name_description", e-form="srSettings")
|
||||
| {{SR.name_description}}
|
||||
dt Content type:
|
||||
dd {{SR.SR_type}}
|
||||
dt Tags
|
||||
dd(ng-if="SR.tags.length")
|
||||
span(ng-repeat="tag in SR.tags")
|
||||
span.label.label-primary {{tag}}
|
||||
dd(ng-if="!SR.tags.length")
|
||||
em No tags.
|
||||
dt Shared
|
||||
div(ng-repeat="container in [SR.$container] | resolve")
|
||||
dd(ng-if="'pool' === container.type")
|
||||
| Yes (
|
||||
a(ui-sref="pools_view({id: container.UUID})") {{container.name_label}}
|
||||
| )
|
||||
dd(ng-if="'host' === container.type") No
|
||||
dt Size
|
||||
dd {{SR.size | bytesToSize}}
|
||||
dt UUID
|
||||
dd {{SR.UUID}}
|
||||
.btn-form(ng-show="srSettings.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="srSettings.$waiting", ng-click="srSettings.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="submit", ng-disabled="srSettings.$waiting")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-bar-chart-o(style="color: #e25440;")
|
||||
| Stats
|
||||
.grid
|
||||
.grid-cell
|
||||
p.stat-name Physical Alloc:
|
||||
p.center.big {{[SR.physical_usage, SR.size] | %}}
|
||||
.grid-cell
|
||||
p.stat-name Virtual Alloc:
|
||||
p.center.big {{[SR.usage, SR.size] | %}}
|
||||
.grid-cell
|
||||
p.stat-name VDIs:
|
||||
p.center.big {{SR.VDIs.length}}
|
||||
// TODO: Space panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.xo-icon-memory(style="color: #e25440;")
|
||||
| VDI Map
|
||||
.progress
|
||||
.progress-bar.progress-bar-vm(ng-if="((VDI.size/SR.size)*100) > 0.5", ng-repeat="VDI in SR.VDIs | resolve | orderBy:natural('name_label') track by VDI.UUID", role="progressbar", aria-valuemin="0", aria-valuenow="{{VDI.size}}", aria-valuemax="{{SR.size}}", style="width: {{[VDI.size, SR.size] | %}}", xo-sref="VDIs_view({id: VDI.UUID})", tooltip="{{VDI.name_label}} ({{[VDI.size, SR.size] | %}})")
|
||||
// display the name only if it fits in its progress bar
|
||||
span(ng-if="VDI.name_label.length < ((VDI.size/SR.size)*100)") {{VDI.name_label}}
|
||||
ul.list-inline.text-center
|
||||
li Total: {{SR.size | bytesToSize}}
|
||||
li Currently used: {{SR.usage | bytesToSize}}
|
||||
li Available: {{SR.size-SR.usage | bytesToSize}}
|
||||
// TODO: VDIs.
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="srDisks", onbeforesave="saveVDI()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.fa.fa-hdd-o(style="color: #e25440;")
|
||||
| Virtual disks
|
||||
span.quick-edit(tooltip="Edit disks", ng-click="srDisks.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
span.quick-edit(tooltip="Rescan", ng-click="rescanSr(SR.UUID)")
|
||||
i.fa.fa-refresh.fa-fw
|
||||
table.table.table-hover
|
||||
tr
|
||||
th Name
|
||||
th Description
|
||||
th Size
|
||||
th Attached to:
|
||||
tr(ng-repeat="VDI in SR.VDIs | resolve | orderBy:natural('name_label')", xo-sref="VDIs_view({id: VDI.UUID})")
|
||||
td
|
||||
span(editable-text="VDI.name_label", e-name="name_description", e-form="srDisks")
|
||||
| {{VDI.name_label}}
|
||||
span.label.label-info(ng-if="VDI.$snapshot_of") snapshot
|
||||
td
|
||||
span(editable-text="VDI.name_description", e-name="name_description", e-form="srDisks")
|
||||
| {{VDI.name_description}}
|
||||
td
|
||||
span(editable-text="VDI.size | bytesToSize", e-name="size", e-form="srDisks")
|
||||
| {{VDI.size | bytesToSize}}
|
||||
td(ng-if="((VDI.$VBD | resolve).VM)", ng-init="VBD = (VDI.$VBD | resolve)")
|
||||
| {{(((VDI.$VBD | resolve).VM) | resolve).name_label}}
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="srDisks.$visible", ng-click="disconnectVBD(VBD.UUID)", tooltip="Disconnect this disk")
|
||||
i.fa.fa-unlink.fa-lg
|
||||
td(ng-if="!((VDI.$VBD | resolve).VM)")
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="srDisks.$visible", ng-click="deleteVDI(VDI.UUID)", tooltip="Destroy this disk")
|
||||
i.fa.fa-trash-o.fa-lg
|
||||
.btn-form(ng-show="srDisks.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="srDisks.$waiting", ng-click="srDisks.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="submit", ng-disabled="srDisks.$waiting")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
button.btn.btn-success.pull-right(type="button", ng-disabled="srDisks.$waiting", ng-click="addVDI()")
|
||||
i.fa.fa-plus
|
||||
| Add Disk
|
||||
// /VDIs.
|
||||
// Hosts.
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="srHosts", onaftersave="saveTable()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.fa.fa-link(style="color: #e25440;")
|
||||
| Connected hosts
|
||||
span.quick-edit(tooltip="Edit connected hosts", ng-click="srHosts.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
span.quick-edit(tooltip="Reconnect all hosts", ng-click="reconnectAllHosts()")
|
||||
i.fa.fa-plus-square.fa-fw
|
||||
table.table.table-hover
|
||||
th Name
|
||||
th Status
|
||||
tr(ng-repeat="PBD in SR.$PBDs | resolve", ng-init="host = (PBD.host | resolve)", xo-sref="hosts_view({id: host.UUID})")
|
||||
td {{host.name_label}}
|
||||
td(ng-if="PBD.attached")
|
||||
span.label.label-success Connected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="srHosts.$visible", ng-click="disconnectPBD(host.UUID)", tooltip="Disconnect this host")
|
||||
i.fa.fa-unlink.fa-lg
|
||||
td(ng-if="!PBD.attached")
|
||||
span.label.label-default Disabled
|
||||
button.btn.btn-sm.btn-primary.pull-right(type="button", ng-show="srHosts.$visible", ng-click="connectPBD(host.UUID)", tooltip="Connect this host")
|
||||
i.fa.fa-link.fa-lg
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-comments(style="color: #e25440;")
|
||||
| Logs
|
||||
p.center(ng-if="!SR.messages.length") No recent logs
|
||||
table.table.table-hover(ng-if="SR.messages.length")
|
||||
th.col-md-1 Date
|
||||
th.col-md-1 Name
|
||||
tr(ng-repeat="message in SR.messages | resolve | orderBy:'-time' track by message.UUID")
|
||||
td {{message.time*1e3 | date:"medium"}}
|
||||
td
|
||||
| {{message.name}}
|
||||
a.quick-remove(tooltip="Remove log")
|
||||
i.fa.fa-trash-o.fa-fw
|
||||
// /Hosts.
|
||||
@@ -1,8 +1,19 @@
|
||||
'use strict'
|
||||
require 'angular'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'VmCtrl', (
|
||||
$scope, $stateParams
|
||||
require 'angular-ui-router'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
module.exports = angular.module 'xoWebApp.pool', [
|
||||
'ui.router'
|
||||
]
|
||||
.config ($stateProvider) ->
|
||||
$stateProvider.state 'VMs_view',
|
||||
url: '/vms/:id'
|
||||
controller: 'PoolCtrl'
|
||||
template: require './view'
|
||||
.controller 'PoolCtrl', (
|
||||
$scope, $state, $stateParams
|
||||
xoApi, xo
|
||||
sizeToBytesFilter, bytesToSizeFilter
|
||||
modal
|
||||
@@ -42,12 +53,14 @@ angular.module('xoWebApp')
|
||||
$scope.force_rebootVM = (id) -> xo.vm.restart id, true
|
||||
$scope.migrateVM = xo.vm.migrate
|
||||
$scope.destroyVM = (id) ->
|
||||
modal.confirm({
|
||||
modal.confirm
|
||||
title: 'VM deletion'
|
||||
message: 'Are you sure you want to delete this VM? (including its disks)'
|
||||
}).then ->
|
||||
.then ->
|
||||
# FIXME: provides a way to not delete its disks.
|
||||
xo.vm.delete id, true
|
||||
.then ->
|
||||
$state.go 'home'
|
||||
|
||||
$scope.saveSnapshot = (id, $data) ->
|
||||
snapshot = get (id)
|
||||
@@ -106,12 +119,25 @@ angular.module('xoWebApp')
|
||||
|
||||
xoApi.call 'vbd.disconnect', {id: UUID}
|
||||
|
||||
$scope.cloneVM = (id, vm_name, full_copy) ->
|
||||
clone_name = "#{vm_name}_clone"
|
||||
console.log "Copy VM #{id} #{clone_name} with full copy at #{full_copy}"
|
||||
xo.vm.clone id, clone_name, full_copy
|
||||
|
||||
$scope.snapshotVM = (id, vm_name) ->
|
||||
date = dateFilter Date.now(), 'yyyy-MM-ddTHH:mmZ'
|
||||
snapshot_name = "#{vm_name}_#{date}"
|
||||
console.log "Snapshot #{snapshot_name} from VM #{id}"
|
||||
xo.vm.createSnapshot id, snapshot_name
|
||||
|
||||
$scope.convertVM = (id) ->
|
||||
console.log "Convert VM #{id}"
|
||||
modal.confirm({
|
||||
title: 'VM to template'
|
||||
message: 'Are you sure you want to convert this VM into a template?'
|
||||
}).then ->
|
||||
xo.vm.convert id
|
||||
|
||||
$scope.deleteSnapshot = (id) ->
|
||||
console.log "Delete snapshot #{id}"
|
||||
modal.confirm({
|
||||
@@ -135,9 +161,12 @@ angular.module('xoWebApp')
|
||||
|
||||
$scope.osType = (osName) ->
|
||||
switch osName
|
||||
when 'debian','ubuntu','centos','suse','redhat','oracle','gentoo'
|
||||
when 'debian','ubuntu','centos','suse','redhat','oracle','gentoo','suse','fedora'
|
||||
'linux'
|
||||
when 'windows'
|
||||
'windows'
|
||||
else
|
||||
'other'
|
||||
|
||||
$scope.saveDisks = ($data) ->
|
||||
console.log $data
|
||||
328
app/modules/vm/view.jade
Normal file
@@ -0,0 +1,328 @@
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.page-title
|
||||
i.xo-icon-vm(class="xo-color-{{VM.power_state | lowercase}}")
|
||||
| {{VM.name_label}}
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-cogs(style="color: #e25440;")
|
||||
| General
|
||||
span.quick-edit(tooltip="Edit General settings", ng-click="vmSettings.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
.general-properties
|
||||
form(editable-form="", name="vmSettings", onbeforesave="saveVM($data)")
|
||||
dl.dl-horizontal
|
||||
dt Name
|
||||
dd
|
||||
span(editable-text="VM.name_label", e-name="name_label", e-form="vmSettings")
|
||||
| {{VM.name_label}}
|
||||
dt Description
|
||||
dd
|
||||
span(editable-text="VM.name_description", e-name="name_description", e-form="vmSettings")
|
||||
| {{VM.name_description}}
|
||||
dt(ng-if="VM.power_state == ('Running' || 'Paused')") Running on:
|
||||
dt(ng-if="VM.power_state == ('Halted')") Resident on:
|
||||
dd(ng-repeat="container in [VM.$container] | resolve")
|
||||
a(xo-sref="hosts_view({id: container.UUID})")
|
||||
| {{container.name_label}}
|
||||
dt Address
|
||||
dd(ng-repeat="IP in VM.addresses") {{IP}}
|
||||
dt Tags
|
||||
dd
|
||||
dt vCPUs
|
||||
dd
|
||||
span(editable-text="VM.CPUs.number", e-name="CPUs", e-form="vmSettings")
|
||||
| {{VM.CPUs.number}}
|
||||
dt RAM
|
||||
dd
|
||||
span(editable-text="memorySize", e-name="memory", e-form="vmSettings")
|
||||
| {{memorySize}}
|
||||
dt UUID
|
||||
dd {{VM.UUID}}
|
||||
.btn-form(ng-show="vmSettings.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="vmSettings.$waiting", ng-click="vmSettings.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="submit", ng-disabled="vmSettings.$waiting")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-bar-chart-o(style="color: #e25440;")
|
||||
| Stats
|
||||
.grid
|
||||
.grid-cell
|
||||
p.stat-name vCPUs
|
||||
p.center.big {{VM.CPUs.number}}
|
||||
.grid-cell
|
||||
p.stat-name RAM
|
||||
p.center.big {{VM.memory.size | bytesToSize}}
|
||||
.grid-cell
|
||||
p.stat-name Disks
|
||||
p.center.big {{VM.$VBDs.length || 0}}
|
||||
br
|
||||
.grid
|
||||
.grid-cell(ng-if="VM.os_version.distro")
|
||||
p.stat-name OS:
|
||||
p.center.big
|
||||
i(class="xo-icon-{{osType(VM.os_version.distro)}}",tooltip="{{VM.os_version.name}}", style="color: black;")
|
||||
.grid-cell
|
||||
p.stat-name Xen tools:
|
||||
p.center
|
||||
span(ng-if="VM.PV_drivers", style="color:green;") Installed
|
||||
span(ng-if="!VM.PV_drivers") NOT installed
|
||||
// Action panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
p.flat-panel-title
|
||||
i.fa.fa-flash(style="color: #e25440;")
|
||||
| Actions
|
||||
.grid-cell.text-center
|
||||
.grid
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Stop VM", type="button", style="width: 90%", xo-click="stopVM(VM.UUID)")
|
||||
i.fa.fa-stop.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Start VM", type="button", style="width: 90%", xo-click="startVM(VM.UUID)")
|
||||
i.fa.fa-play.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Reboot VM", type="button", style="width: 90%", xo-click="rebootVM(VM.UUID)")
|
||||
i.fa.fa-refresh.fa-2x.fa-fw
|
||||
.grid-cell.btn-group.dropdown
|
||||
button.btn.dropdown-toggle(tooltip="Migrate VM", type="button", style="width: 90%", data-toggle="dropdown")
|
||||
i.fa.fa-share.fa-2x.fa-fw
|
||||
<span class="caret"></span>
|
||||
ul.dropdown-menu.left(role="menu")
|
||||
li(ng-repeat="host in hosts | orderBy:natural('name_label') track by host.UUID")
|
||||
a(ng-click="migrateVM(VM.UUID, host.UUID)")
|
||||
i.xo-icon-host.fa-fw
|
||||
| To {{host.name_label}}
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Force Reboot", type="button", style="width: 90%", xo-click="force_rebootVM(VM.UUID)")
|
||||
i.fa.fa-flash.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Force Shutdown", type="button", style="width: 90%", xo-click="force_stopVM(VM.UUID)")
|
||||
i.fa.fa-power-off.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Delete VM", type="button", style="width: 90%", xo-click="destroyVM(VM.UUID)")
|
||||
i.fa.fa-trash-o.fa-2x.fa-fw
|
||||
.grid-cell.btn-group.dropdown
|
||||
button.btn.dropdown-toggle(tooltip="Create a clone", style="width: 90%", type="button", data-toggle="dropdown")
|
||||
i.fa.fa-files-o.fa-2x.fa-fw
|
||||
<span class="caret"></span>
|
||||
ul.dropdown-menu.left(role="menu")
|
||||
li
|
||||
a(ng-click="cloneVM(VM.UUID,VM.name_label,false)")
|
||||
i.fa.fa-code-fork.fa-fw
|
||||
| Fast clone
|
||||
li
|
||||
a(ng-click="cloneVM(VM.UUID,VM.name_label,true)")
|
||||
i.xo-icon-sr.fa-fw
|
||||
| Full disk copy
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Convert to template", type="button", style="width: 90%", xo-click="convertVM(VM.UUID)")
|
||||
i.fa.fa-thumb-tack.fa-2x.fa-fw
|
||||
.grid-cell.btn-group
|
||||
button.btn(tooltip="Create a snapshot", style="width: 90%", type="button", xo-click="snapshotVM(VM.UUID,VM.name_label)")
|
||||
i.xo-icon-snapshot.fa-2x.fa-fw
|
||||
.grid-cell.btn-group(style="margin-bottom: 0.5em")
|
||||
button.btn(tooltip="VM Console", type="button", style="width: 90%", xo-sref="consoles_view({id: VM.UUID})")
|
||||
i.xo-icon-console.fa-2x.fa-fw
|
||||
// Disk panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="vmDisks", onaftersave="saveTable()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.xo-icon-sr(style="color: #e25440;")
|
||||
| Disk
|
||||
span.quick-edit(tooltip="Edit disks", ng-click="vmDisks.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
table.table.table-hover
|
||||
tr
|
||||
th Name
|
||||
th Description
|
||||
th Size
|
||||
th SR
|
||||
th Status
|
||||
// FIXME: ng-init seems to disrupt the implicit $watch.
|
||||
tr(ng-repeat="VDI in VDIs | orderBy:natural('name_label') track by VDI.UUID", ng-init="SR = (VDI.$SR | resolve); VBD = (VDI.$VBD | resolve)", xo-sref="SRs_view({id: SR.UUID})")
|
||||
td
|
||||
span(editable-text="VDI.name_label", e-name="name_label", e-form="vmDisks", e-required="")
|
||||
| {{VDI.name_label}}
|
||||
td
|
||||
span(editable-text="VDI.name_description", e-name="name_description", e-form="vmDisks")
|
||||
| {{VDI.name_description}}
|
||||
td
|
||||
span(editable-text="VDI.size | bytesToSize", e-name="size", e-form="vmDisks")
|
||||
| {{VDI.size | bytesToSize}}
|
||||
td
|
||||
// Are SR editable? will trigger moving VDI to the new SR
|
||||
| {{SR.name_label}}
|
||||
td(ng-if="VBD.attached")
|
||||
span.label.label-success Connected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="vmDisks.$visible", ng-click="disconnectVBD(VBD.UUID)")
|
||||
i.fa.fa-unlink.fa-lg(tooltip="Disconnect this disk")
|
||||
td(ng-if="!VBD.attached")
|
||||
span.label.label-default(editable-checkbox="VBD.attached", e-title="Connected?", e-form="vmDisks", e-required="") Disconnected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="vmDisks.$visible", ng-click="deleteVDI(VDI.UUID)")
|
||||
i.fa.fa-trash-o.fa-lg(tooltip="Remove this disk")
|
||||
tr(ng-repeat="VDI in newVDIs | resolve | orderBy:natural('name_label') track by VDI.UUID")
|
||||
.btn-form(ng-show="vmDisks.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="vmDisks.$waiting", ng-click="vmDisks.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="submit", ng-disabled="vmDisks.$waiting", ng-click="saveChanges()")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
button.btn.btn-success.pull-right(type="button", ng-disabled="vmDisks.$waiting", ng-click="addVDI()")
|
||||
i.fa.fa-plus
|
||||
| Add Disk
|
||||
// TODO: Interface panel
|
||||
.grid
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="vmInterfaces", onaftersave="saveTable()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.xo-icon-network(style="color: #e25440;")
|
||||
| Interface
|
||||
span.quick-edit(tooltip="Edit interfaces", ng-click="vmInterfaces.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
table.table.table-hover
|
||||
th Device
|
||||
th MAC
|
||||
th MTU
|
||||
th Network
|
||||
th Link status
|
||||
tr(ng-repeat="VIF in VM.VIFs | resolve | orderBy:natural('name_label') track by VIF.UUID")
|
||||
td VIF \#{{VIF.device}}
|
||||
td
|
||||
| {{VIF.MAC}}
|
||||
td
|
||||
span(editable-text="VIF.MTU", e-name="mtu", e-form="vmInterfaces", e-required="")
|
||||
| {{VIF.MTU}}
|
||||
td
|
||||
| {{(VIF.$network | resolve).name_label}}
|
||||
td(ng-if="VIF.attached")
|
||||
span.label.label-success(editable-checkbox="VIF.attached") Connected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="vmInterfaces.$visible", ng-click="disconnectVIF(VIF.UUID)")
|
||||
i.fa.fa-ban.fa-lg(tooltip="Disconnect this interface")
|
||||
td(ng-if="!VIF.attached")
|
||||
span.label.label-default(editable-checkbox="VIF.attached") Disconnected
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="vmInterfaces.$visible", ng-click="deleteVIF(VIF.UUID)")
|
||||
i.fa.fa-trash-o.fa-lg(tooltip="Remove this interface")
|
||||
.btn-form(ng-show="vmInterfaces.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="vmInterfaces.$waiting", ng-click="vmInterfaces.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="button", ng-disabled="vmInterfaces.$waiting", ng-click="saveChanges()")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
button.btn.btn-success.pull-right(type="button", ng-disabled="vmInterfaces.$waiting", ng-click="addNetwork()")
|
||||
i.fa.fa-plus
|
||||
| Add Interface
|
||||
// TODO: User/Group panel : DISPLAY ONLY THIS PANEL IF YOU ARE ADMIN
|
||||
//- .grid
|
||||
//- .grid-cell.flat-panel
|
||||
//- form(editable-form="", name="vmUsers", onaftersave="saveTable()", oncancel="cancel()")
|
||||
//- p.flat-panel-title
|
||||
//- i.fa.fa-user(style="color: #e25440;")
|
||||
//- | Users
|
||||
//- span.quick-edit(tooltip="Edit interfaces", ng-click="vmUsers.$show()")
|
||||
//- i.fa.fa-edit.fa-fw
|
||||
//- table.table.table-hover
|
||||
//- th User name
|
||||
//- th Permission
|
||||
//- .btn-form(ng-show="vmUsers.$visible")
|
||||
//- p.center
|
||||
//- button.btn.btn-default(type="button", ng-disabled="vmUsers.$waiting", ng-click="vmUsers.$cancel()")
|
||||
//- i.fa.fa-times
|
||||
//- | Cancel
|
||||
//- button.btn.btn-primary(type="button", ng-disabled="vmUsers.$waiting", ng-click="saveChanges()")
|
||||
//- i.fa.fa-save
|
||||
//- | Save
|
||||
//- button.btn.btn-success.pull-right(type="button", ng-disabled="vmUsers.$waiting", ng-click="addNetwork()")
|
||||
//- i.fa.fa-plus
|
||||
//- | Add User
|
||||
//- .grid-cell.flat-panel
|
||||
//- form(editable-form="", name="vmGroups", onaftersave="saveTable()", oncancel="cancel()")
|
||||
//- p.flat-panel-title
|
||||
//- i.fa.fa-users(style="color: #e25440;")
|
||||
//- | Groups
|
||||
//- span.quick-edit(tooltip="Edit interfaces", ng-click="vmGroups.$show()")
|
||||
//- i.fa.fa-edit.fa-fw
|
||||
//- table.table.table-hover
|
||||
//- th Group name
|
||||
//- th Permission
|
||||
//- .btn-form(ng-show="vmGroups.$visible")
|
||||
//- p.center
|
||||
//- button.btn.btn-default(type="button", ng-disabled="vmGroups.$waiting", ng-click="vmGroups.$cancel()")
|
||||
//- i.fa.fa-times
|
||||
//- | Cancel
|
||||
//- button.btn.btn-primary(type="button", ng-disabled="vmGroups.$waiting", ng-click="saveChanges()")
|
||||
//- i.fa.fa-save
|
||||
//- | Save
|
||||
//- button.btn.btn-success.pull-right(type="button", ng-disabled="vmGroups.$waiting", ng-click="addNetwork()")
|
||||
//- i.fa.fa-plus
|
||||
//- | Add Group
|
||||
// TODO: Snapshot and Logs panels
|
||||
.grid
|
||||
// Snap panel
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="vmSnap", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.xo-icon-snapshot(style="color: #e25440;")
|
||||
| Snapshots
|
||||
span.quick-edit(tooltip="Edit snapshots", ng-click="vmSnap.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
p.center(ng-if="!VM.snapshots.length") No snapshots
|
||||
table.table.table-hover(ng-if="VM.snapshots.length")
|
||||
th Date
|
||||
th Name
|
||||
tr(ng-repeat="snapshot in VM.snapshots | resolve | orderBy:'-snapshot_time' track by snapshot.UUID")
|
||||
td {{snapshot.snapshot_time*1e3 | date:"medium"}}
|
||||
td
|
||||
span(editable-text="snapshot.name_label", e-name="name_label", e-form="vmSnap", onbeforesave="saveSnapshot(snapshot.UUID, $data)")
|
||||
| {{snapshot.name_label}}
|
||||
span.pull-right
|
||||
button.btn.btn-sm.btn-danger(type="button", ng-show="vmSnap.$visible", ng-click="deleteSnapshot(snapshot.UUID)")
|
||||
i.fa.fa-trash-o.fa-lg(tooltip="Remove this snapshot")
|
||||
button.btn.btn-sm.btn-danger(type="button", ng-show="vmSnap.$visible", ng-click="revertSnapshot(snapshot.UUID)")
|
||||
i.fa.fa-undo.fa-lg(tooltip="Revert VM to this snapshot")
|
||||
.btn-form(ng-show="vmSnap.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="vmSnap.$waiting", ng-click="vmSnap.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
|
|
||||
button.btn.btn-primary(type="submit", ng-disabled="vmSnap.$waiting", ng-click="saveChanges()")
|
||||
i.fa.fa-save
|
||||
| Save
|
||||
// Logs panel
|
||||
.grid-cell.flat-panel
|
||||
form(editable-form="", name="vmLogs", onaftersave="saveTable()", oncancel="cancel()")
|
||||
p.flat-panel-title
|
||||
i.fa.fa-comments(style="color: #e25440;")
|
||||
| Logs
|
||||
span.quick-edit(tooltip="Edit logs", ng-click="vmLogs.$show()")
|
||||
i.fa.fa-edit.fa-fw
|
||||
p.center(ng-if="!VM.messages.length") No recent logs
|
||||
table.table.table-hover(ng-if="VM.messages.length")
|
||||
th Date
|
||||
th Name
|
||||
tr(ng-repeat="message in VM.messages | resolve | orderBy:'-time' track by message.UUID")
|
||||
td {{message.time*1e3 | date:"medium"}}
|
||||
td
|
||||
| {{message.name}}
|
||||
button.btn.btn-sm.btn-danger.pull-right(type="button", ng-show="vmLogs.$visible", ng-click="deleteLog(message.UUID)")
|
||||
i.fa.fa-trash-o.fa-lg(tooltip="Remove this log entry")
|
||||
.btn-form(ng-show="vmLogs.$visible")
|
||||
p.center
|
||||
button.btn.btn-default(type="button", ng-disabled="vmLogs.$waiting", ng-click="vmLogs.$cancel()")
|
||||
i.fa.fa-times
|
||||
| Cancel
|
||||
@@ -1,83 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
angular.module('xoWebApp', [
|
||||
'ngCookies'
|
||||
|
||||
'ui.bootstrap'
|
||||
'ui.indeterminate'
|
||||
'ui.router'
|
||||
'ui.select2'
|
||||
|
||||
'naturalSort'
|
||||
'toaster'
|
||||
'xeditable'
|
||||
])
|
||||
.config ($stateProvider, $urlRouterProvider, $tooltipProvider) ->
|
||||
# Redirects unmatched URLs to `/`.
|
||||
$urlRouterProvider.otherwise '/'
|
||||
|
||||
# Sets up the different states for our module.
|
||||
$stateProvider
|
||||
.state 'home',
|
||||
url: '/'
|
||||
controller: 'MainCtrl'
|
||||
templateUrl: 'views/main.html'
|
||||
|
||||
.state 'list',
|
||||
url: '/list'
|
||||
controller: 'ListCtrl'
|
||||
templateUrl: 'views/list.html'
|
||||
|
||||
.state 'hosts_view',
|
||||
url: '/hosts/:id'
|
||||
controller: 'HostCtrl'
|
||||
templateUrl: 'views/host.html'
|
||||
|
||||
.state 'SRs_view',
|
||||
url: '/srs/:id'
|
||||
controller: 'SrCtrl'
|
||||
templateUrl: 'views/sr.html'
|
||||
|
||||
.state 'SRs_new',
|
||||
url: '/srs/new/:container'
|
||||
controller: 'NewSrCtrl'
|
||||
templateUrl: 'views/new_sr.html'
|
||||
|
||||
.state 'pools_view',
|
||||
url: '/pools/:id'
|
||||
controller: 'PoolCtrl'
|
||||
templateUrl: 'views/pool.html'
|
||||
|
||||
.state 'VMs_new',
|
||||
url: '/vms/new/:container'
|
||||
controller: 'NewVmCtrl'
|
||||
templateUrl: 'views/new_vm.html'
|
||||
|
||||
.state 'VMs_view',
|
||||
url: '/vms/:id'
|
||||
controller: 'VmCtrl'
|
||||
templateUrl: 'views/vm.html'
|
||||
|
||||
.state 'consoles_view',
|
||||
url: '/consoles/:id'
|
||||
controller: 'ConsoleCtrl'
|
||||
templateUrl: 'views/console.html'
|
||||
|
||||
.state 'about',
|
||||
url: '/about'
|
||||
templateUrl: 'views/about.html'
|
||||
|
||||
.state 'settings',
|
||||
url: '/settings'
|
||||
controller: 'SettingsCtrl'
|
||||
templateUrl: 'views/settings.html'
|
||||
|
||||
# Changes the default settings for the tooltips.
|
||||
$tooltipProvider.options
|
||||
appendToBody: true
|
||||
placement: 'bottom'
|
||||
|
||||
.run (editableOptions, editableThemes) ->
|
||||
editableThemes.bs3.inputClass = 'input-sm'
|
||||
editableThemes.bs3.buttonsClass = 'btn-sm'
|
||||
editableOptions.theme = 'bs3'
|
||||
@@ -1,60 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'ConsoleCtrl', ($scope, $stateParams, xoApi, xo) ->
|
||||
{id} = $stateParams
|
||||
{get} = xo
|
||||
push = Array::push.apply.bind Array::push
|
||||
merge = do ->
|
||||
(args...) ->
|
||||
result = []
|
||||
for arg in args
|
||||
push result, arg if arg?
|
||||
result
|
||||
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
->
|
||||
unless xoApi.user
|
||||
$scope.consoleUrl = ''
|
||||
$scope.VDIs = []
|
||||
return
|
||||
|
||||
VM = $scope.VM = xo.get id
|
||||
return unless VM? and VM.power_state is 'Running'
|
||||
|
||||
pool = get VM.poolRef
|
||||
return unless pool
|
||||
|
||||
$scope.consoleUrl = do ->
|
||||
for console in VM.consoles
|
||||
if console.protocol is 'rfb'
|
||||
return "#{console.location}&session_id=#{pool.$sessionId}"
|
||||
''
|
||||
|
||||
host = get VM.$container # host because the VM is running.
|
||||
return unless host
|
||||
|
||||
# FIXME: We should filter on connected SRs (PBDs)!
|
||||
SRs = get (merge host.SRs, pool.SRs)
|
||||
$scope.VDIs = do ->
|
||||
VDIs = []
|
||||
for SR in SRs
|
||||
push VDIs, SR.VDIs if SR.content_type is 'iso'
|
||||
get VDIs
|
||||
|
||||
cdDrive = do ->
|
||||
return VBD for VBD in (get VM.$VBDs) when VBD.is_cd_drive
|
||||
null
|
||||
|
||||
$scope.mountedIso =
|
||||
if cdDrive and cdDrive.VDI and (VDI = get cdDrive.VDI)
|
||||
VDI.UUID
|
||||
else
|
||||
''
|
||||
)
|
||||
|
||||
$scope.eject = ->
|
||||
xo.vm.ejectCd id
|
||||
$scope.insert = (disc_id) ->
|
||||
xo.vm.insertCd id, disc_id, true
|
||||
@@ -1,17 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'DeleteVMsCtrl', ($scope, $modalInstance, xo, VMsIds) ->
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
->
|
||||
$scope.VMs = xo.get VMsIds
|
||||
)
|
||||
|
||||
# Do disks have to be deleted for a given VM.
|
||||
disks = $scope.disks = {}
|
||||
do ->
|
||||
disks[id] = true for id in VMsIds
|
||||
|
||||
$scope.delete = ->
|
||||
$modalInstance.close ([id, disks[id]] for id in VMsIds)
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'GenericModalCtrl', ($scope, $modalInstance, options) ->
|
||||
$scope.title = options.title
|
||||
$scope.message = options.message
|
||||
|
||||
$scope.yesButtonLabel = options.yesButtonLabel ? 'Ok'
|
||||
$scope.noButtonLabel = options.noButtonLabel
|
||||
@@ -1,5 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'ListCtrl', ($scope, xo) ->
|
||||
$scope.byTypes = xo.byTypes
|
||||
@@ -1,22 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'NavBarCtrl', ($scope, $location, xoApi) ->
|
||||
# TODO: It would make sense to inject xoApi in the scope.
|
||||
$scope.$watch(
|
||||
-> xoApi.status
|
||||
(status) ->
|
||||
$scope.status = status
|
||||
)
|
||||
$scope.$watch(
|
||||
-> xoApi.user
|
||||
(user) ->
|
||||
$scope.user = user
|
||||
)
|
||||
$scope.logIn = xoApi.logIn
|
||||
$scope.logOut = xoApi.logOut
|
||||
|
||||
# When a searched is entered, we must switch to the list view if
|
||||
# necessary.
|
||||
$scope.ensureListView = ->
|
||||
$location.path '/list'
|
||||
@@ -1,10 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
angular.module('xoWebApp')
|
||||
.controller 'NewSrCtrl', ($scope, $stateParams, xo) ->
|
||||
$scope.$watch(
|
||||
-> xo.revision
|
||||
->
|
||||
$scope.container = xo.get $stateParams.container
|
||||
)
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
angular.module('xoWebApp')
|
||||
require 'angular'
|
||||
require 'angular-cookies'
|
||||
|
||||
require 'angular-notify-toaster'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
# TODO: split into multiple modules.
|
||||
module.exports = angular.module 'xoWebApp.services', [
|
||||
'ngCookies'
|
||||
|
||||
'toaster'
|
||||
]
|
||||
|
||||
# Inspired by https://github.com/MathieuTurcotte/node-backoff.
|
||||
#
|
||||
@@ -89,23 +101,6 @@ angular.module('xoWebApp')
|
||||
# Returns the service.
|
||||
BackOff
|
||||
|
||||
.service 'modal', ($modal) ->
|
||||
{
|
||||
confirm: ({title, message}) ->
|
||||
modal = $modal.open {
|
||||
controller: 'GenericModalCtrl'
|
||||
templateUrl: 'views/generic_modal.html'
|
||||
resolve: {
|
||||
options: -> {
|
||||
title
|
||||
message
|
||||
noButtonLabel: 'Cancel'
|
||||
}
|
||||
}
|
||||
}
|
||||
modal.result
|
||||
}
|
||||
|
||||
.service 'notify', (toaster) ->
|
||||
notifier = (level) ->
|
||||
(options) ->
|
||||
@@ -155,6 +150,9 @@ angular.module('xoWebApp')
|
||||
# Promises linked to the requests.
|
||||
deferreds = {}
|
||||
|
||||
# Listeners for notifications (grouped by method).
|
||||
listeners = {}
|
||||
|
||||
# When the socket is closed, request are enqueued.
|
||||
queue = []
|
||||
|
||||
@@ -239,6 +237,16 @@ angular.module('xoWebApp')
|
||||
# deferred (if any).
|
||||
socket.addEventListener 'message', (event) ->
|
||||
response = JSON.parse event.data
|
||||
|
||||
unless 'id' of response
|
||||
# It is not a response but a notification.
|
||||
if 'method' of response and 'params' of response
|
||||
xoApi.emit response.method, response.params
|
||||
$rootScope.$digest()
|
||||
else
|
||||
console.error 'invalid message received', response
|
||||
return
|
||||
|
||||
id = response.id
|
||||
deferred = deferreds[id]
|
||||
|
||||
@@ -297,12 +305,31 @@ angular.module('xoWebApp')
|
||||
send 'session.signOut' if socket
|
||||
xoApi.user = null
|
||||
$cookieStore.remove 'token'
|
||||
|
||||
# EventEmitter methods.
|
||||
emit: (event, params...) ->
|
||||
listener.apply xoApi, params for listener in listeners[event] ? []
|
||||
on: (event, listener) ->
|
||||
(listeners[event] ?= []).push listener
|
||||
once: (event, listener) ->
|
||||
onceListener = (params...) ->
|
||||
xoApi.removeListener event, onceListener
|
||||
listener.apply this, params
|
||||
xoApi.on event, onceListener
|
||||
removeAllListeners: (event) ->
|
||||
delete listeners[event]
|
||||
removeListener: (event, listener) ->
|
||||
return unless event of listeners
|
||||
for candidate, i in listeners[event]
|
||||
if candidate is listener
|
||||
listeners[event].splice i, 1
|
||||
return
|
||||
}
|
||||
|
||||
# This service provides access to XO objects.
|
||||
#
|
||||
# Deprecated: use the service `xo` instead.
|
||||
.service 'xoObjects', ($timeout, xoApi) ->
|
||||
.service 'xoObjects', ($timeout, xoApi, $rootScope) ->
|
||||
byRefs = Object.create null
|
||||
byUUIDs = Object.create null
|
||||
{
|
||||
@@ -320,23 +347,40 @@ angular.module('xoWebApp')
|
||||
byUUIDs[key] ? byRefs[key]
|
||||
}
|
||||
|
||||
do helper = ->
|
||||
xoApi.call('xo.getAllObjects').then (objects) ->
|
||||
# Empty collections.
|
||||
delete byTypes[key] for key of byTypes
|
||||
byRefs = Object.create null
|
||||
byUUIDs = Object.create null
|
||||
xoApi.call('xo.getAllObjects').then (objects) ->
|
||||
# Empty collections.
|
||||
delete byTypes[key] for key of byTypes
|
||||
byRefs = Object.create null
|
||||
byUUIDs = Object.create null
|
||||
|
||||
all = objects
|
||||
for object in all
|
||||
byUUIDs[object.UUID] = object if object.UUID?
|
||||
byRefs[object.ref] = object if object.ref?
|
||||
(byTypes[object.type] ?= []).push object
|
||||
all = objects
|
||||
for object in all
|
||||
byUUIDs[object.UUID] = object if object.UUID?
|
||||
byRefs[object.ref] = object if object.ref?
|
||||
(byTypes[object.type] ?= []).push object
|
||||
|
||||
++xoObjects.revision
|
||||
|
||||
# Fetches objects again after 5 seconds.
|
||||
$timeout helper, 5e3, false
|
||||
++xoObjects.revision
|
||||
xoApi.on 'all', (event) ->
|
||||
switch event.type
|
||||
when 'exit'
|
||||
for object in event.items
|
||||
delete byUUIDs[object.UUID] if 'UUID' of object
|
||||
delete byRefs[object.ref] if 'ref' of object
|
||||
list = byTypes[object.type] ? []
|
||||
for candidate, i in list
|
||||
if candidate.ref is object.ref
|
||||
list.splice i, 1
|
||||
break
|
||||
else
|
||||
for object in event.items
|
||||
byUUIDs[object.UUID] = object if 'UUID' of object
|
||||
byRefs[object.ref] = object if 'ref' of object
|
||||
list = byTypes[object.type] ?= []
|
||||
index = do ->
|
||||
return i for candidate, i in list when candidate.ref is object.ref
|
||||
list.length
|
||||
list[index] = object
|
||||
++xoObjects.revision
|
||||
|
||||
xoObjects
|
||||
|
||||
@@ -354,6 +398,7 @@ angular.module('xoWebApp')
|
||||
|
||||
{argsMapper, notification} = options ? {}
|
||||
|
||||
# FIXME: default mapper should be identity.
|
||||
argsMapper ?= (id) -> {id}
|
||||
|
||||
(args...) ->
|
||||
@@ -387,12 +432,11 @@ angular.module('xoWebApp')
|
||||
|
||||
get: xoObjects.get
|
||||
|
||||
pool: {
|
||||
pool:
|
||||
disconnect: action 'Disconnect pool'
|
||||
new_sr: action 'New SR' #temp fix before creating SR
|
||||
}
|
||||
|
||||
host: {
|
||||
host:
|
||||
attach: action 'Atach host'#, 'host.attach'
|
||||
detach: action 'Detach host', 'host.detach'
|
||||
restart: action 'Restart host', 'host.restart'
|
||||
@@ -401,22 +445,39 @@ angular.module('xoWebApp')
|
||||
stop: action 'Stop host', 'host.stop'
|
||||
new_sr: action 'New SR' #temp fix before creating SR
|
||||
# TODO: attach/set
|
||||
}
|
||||
|
||||
message: {
|
||||
delete: action, 'Delete message'
|
||||
}
|
||||
log:
|
||||
delete: action 'Delete Log', 'message.delete'
|
||||
|
||||
pbd: {
|
||||
message:
|
||||
delete: action 'Delete message'
|
||||
|
||||
pbd:
|
||||
delete: action 'Delete PBD'
|
||||
disconnect: action 'Disconnect PBD'
|
||||
}
|
||||
|
||||
task: {
|
||||
delete: action, 'Delete task'
|
||||
}
|
||||
server:
|
||||
add: action 'Add server', 'server.add', argsMapper: (params) -> params
|
||||
remove: action 'Remove server', 'server.remove', argsMapper: (id) -> {id}
|
||||
getAll: action 'Getting server', 'server.getAll'
|
||||
set: action 'Save server', 'server.set', argsMapper: (params) -> params
|
||||
|
||||
vm: {
|
||||
task:
|
||||
delete: action 'Delete task'
|
||||
|
||||
user:
|
||||
create: action 'Create user', 'user.create', argsMapper: (params) -> params
|
||||
delete: action 'Delete user', 'user.delete', argsMapper: (id) -> {id}
|
||||
getAll: action 'Getting user', 'user.getAll'
|
||||
set: action 'Save user', 'user.set', argsMapper: (params) -> params
|
||||
|
||||
vm:
|
||||
convert: action 'Convert VM', 'vm.convert', {
|
||||
argsMapper: (id) -> {id}
|
||||
}
|
||||
clone: action 'Copy VM', 'vm.clone', {
|
||||
argsMapper: (id, name, full_copy) -> {id, name, full_copy} #todo : sr ref to choose target SR
|
||||
}
|
||||
createSnapshot: action 'Create VM snapshot', 'vm.snapshot', {
|
||||
argsMapper: (id, name) -> {id, name}
|
||||
}
|
||||
@@ -439,16 +500,9 @@ angular.module('xoWebApp')
|
||||
}
|
||||
revert: action 'Revert snapshot', 'vm.revert'
|
||||
# TODO: create/set/pause/suspend
|
||||
}
|
||||
|
||||
vdi: {
|
||||
vdi:
|
||||
delete: action 'Delete VDI', 'vdi.delete'
|
||||
}
|
||||
|
||||
log: {
|
||||
delete: action 'Delete Log', 'message.delete'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Adds the revision property.
|
||||
@@ -87,6 +87,9 @@ a, [ng-click], [xo-click], [xo-sref]
|
||||
.xo-icon-error
|
||||
@extend .fa, .fa-exclamation-circle, .text-danger
|
||||
|
||||
.xo-icon-success
|
||||
@extend .fa, .fa-check-circle, .text-success
|
||||
|
||||
////
|
||||
// Objects
|
||||
////
|
||||
|
||||
@@ -11,45 +11,47 @@ input.form-control.inverse {background-color: #666; color: #f8f8f8; border: 0px;
|
||||
.btn-search:hover {background-color: #1d4457; color: #f8f8f8;}
|
||||
|
||||
html {
|
||||
background-color: #edece4;
|
||||
/* Possibility to get a wallpaper for the background: see later in admin maybe, for the lulz */
|
||||
/*background: url(bg.jpg) no-repeat center center fixed;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;*/
|
||||
background-color: #edece4;
|
||||
/* Possibility to get a wallpaper for the background: see later in admin maybe, for the lulz */
|
||||
/*background: url(bg.jpg) no-repeat center center fixed;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;*/
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.navbar-under { background-color: #f8f8f8;}
|
||||
|
||||
|
||||
td.vcenter {
|
||||
vertical-align: middle;
|
||||
vertical-align: middle;
|
||||
}
|
||||
input[type="checkbox"]{
|
||||
padding-top: 0px;
|
||||
line-height: normal;
|
||||
padding-top: 0px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
input.inverse {margin: 0;}
|
||||
/* progress bar */
|
||||
.progress-small {
|
||||
|
||||
height: 0.6em;
|
||||
width: 4em;
|
||||
display: inline-block;
|
||||
float: center;
|
||||
margin: 3px;
|
||||
height: 0.6em;
|
||||
width: 4em;
|
||||
display: inline-block;
|
||||
float: center;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
// FIXME: What is it?
|
||||
//.i-progress { float: center; margin-right: 0em;}
|
||||
|
||||
.pool-block { margin-left: 1em; margin-right: 1em; margin-top: 0.5em; margin-bottom: 0;}
|
||||
// Pool block
|
||||
// A block display a whole pool
|
||||
.pool-block { margin-left: 0.8em; margin-right: 0.8em; margin-top: 0.3em; margin-bottom: 0;}
|
||||
.pool-cell { margin-bottom: 0.5em; margin-left: 2em; margin-right: 1em; min-height: 6em; max-width: 190px; background-color: white; }
|
||||
/* row for all hosts/vms in a pool */
|
||||
.hosts-vms-cells { min-height: 6em; margin-right: -0.5em; }
|
||||
@@ -57,19 +59,19 @@ input.inverse {margin: 0;}
|
||||
.host-cell { max-width: 156px; background-color: white; margin-bottom: 0.5em;}
|
||||
/* pool size mini : 195 px ; host : 161 px; */
|
||||
.vm-cell {
|
||||
margin-left: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
background-color: white;
|
||||
/* allow empty space */
|
||||
/*align-self: flex-start;*/
|
||||
margin-left: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
background-color: white;
|
||||
/* allow empty space */
|
||||
/*align-self: flex-start;*/
|
||||
}
|
||||
.grey {color: #666; font-size: 0.9em;}
|
||||
|
||||
/* drodown head link for pools/hosts */
|
||||
.dropdown-pool { border-bottom: 1px solid #edece4; text-align: center;}
|
||||
.dropdown-pool a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
.pool-name {font-size: 1.8em;}
|
||||
.host-name {font-size: 20px;}
|
||||
@@ -99,22 +101,22 @@ td.vm-memory-stat {text-align: right;}
|
||||
/* the main bar */
|
||||
.navbar-inverse
|
||||
{
|
||||
background-color: #242628;
|
||||
border-color: #2e3133;
|
||||
//font-variant:small-caps;
|
||||
background-color: #242628;
|
||||
border-color: #2e3133;
|
||||
//font-variant:small-caps;
|
||||
}
|
||||
.fa {font-variant: normal;}
|
||||
/* the big subbar */
|
||||
.sub-bar
|
||||
{
|
||||
height:50px;
|
||||
top:50px;
|
||||
position: fixed;
|
||||
background:#242628;
|
||||
border-bottom:1px solid #2e3133;
|
||||
width:100%;
|
||||
margin:0px auto;
|
||||
z-index: 1020;
|
||||
height:50px;
|
||||
top:50px;
|
||||
position: fixed;
|
||||
background:#242628;
|
||||
border-bottom:1px solid #2e3133;
|
||||
width:100%;
|
||||
margin:0px auto;
|
||||
z-index: 1020;
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +168,7 @@ a.btn.navbar-btn.btn-default.dropdown-toggle.inversed {background-color:#444; bo
|
||||
/* tooltip hack to avoid be hidden by other elements */
|
||||
/*
|
||||
.tooltip {
|
||||
position: fixed;
|
||||
position: fixed;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -179,12 +181,13 @@ a.btn.navbar-btn.btn-default.dropdown-toggle.inversed {background-color:#444; bo
|
||||
|
||||
/* flex */
|
||||
.grid {
|
||||
display: flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.grid-cell {
|
||||
flex: 1;
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
flex: 1;
|
||||
-webkit-flex: 1;
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
}
|
||||
/* stats name in a grid cell */
|
||||
.stat-name {margin-top: 1em; text-align: center; font-variant: small-caps;}
|
||||
@@ -205,21 +208,65 @@ a.btn.navbar-btn.btn-default.dropdown-toggle.inversed {background-color:#444; bo
|
||||
|
||||
|
||||
.grid-button {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0.5em;
|
||||
background-color: #efefef;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0.5em;
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
.grid-button:hover {
|
||||
background-color: #e25440;
|
||||
background-color: #e25440;
|
||||
}
|
||||
|
||||
/* With gutters */
|
||||
.grid--gutters {
|
||||
margin-left: -0.5em;
|
||||
margin-left: -0.5em;
|
||||
}
|
||||
.grid--gutters > .grid-cell {
|
||||
padding-left: 0.5em;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
.page-header {
|
||||
border-bottom: 1px solid #FFFFFF;
|
||||
padding-bottom: 9px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-control-login {
|
||||
border: 1px solid #D6D6D6;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
height: 50px;
|
||||
padding: 6px 15px;
|
||||
}
|
||||
|
||||
.row-login {
|
||||
background: #fff;
|
||||
//padding: 20px 50px 20px 50px;
|
||||
padding: 1em 5em 1em 5em;
|
||||
}
|
||||
|
||||
legend.login {
|
||||
border: medium none;
|
||||
color: #111;
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
line-height: inherit;
|
||||
margin-bottom: 15px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
padding: 10px;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<!-- TODO: lots of stuff. -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="page-title">About Xen Orchestra</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<!-- Vates -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-lightbulb-o" style="color: #e25440;"></i> Vates</p>
|
||||
<p class="text-center">We are the team behind Xen Orchestra, we are Vates! We create Open Source products and we offer commercial support for Xen and Xen Orchestra. Want to know more about us? Go to our website!
|
||||
</p>
|
||||
<p class="text-center"><img src="images/arrow.png"></p><br/>
|
||||
<p class="text-center"><a class="btn btn-success" href="https://vates.fr"><i class="fa fa-hand-o-right"></i> Our website</a></p>
|
||||
</div>
|
||||
|
||||
<!-- Open Source -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-thumbs-up" style="color: #e25440;"></i> Open Source</p>
|
||||
<p class="text-center">This project is Open Source (AGPL), everyone is welcome aboard! You want a specific feature in XO? Report a bug? Go to our project website, read the FAQ and get involved in the project!</p>
|
||||
<p class="text-center"><img src="images/opensource.png"></p><br/>
|
||||
<p class="text-center"><a class="btn btn-info" href="https://xen-orchestra.com"><i class="fa fa-flask"></i> Project website</a></p>
|
||||
</div>
|
||||
|
||||
<!-- Pro support -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-truck" style="color: #e25440;"></i> Pro Support Delivered</p>
|
||||
<p class="text-center">Our XO Appliance can be delivered with professional support: stay relaxed, we got your back! You can also have assitance for deploying or upgrade your virtualized infrastructure through our deep understanding of Xen.</p>
|
||||
<p class="text-center"><img src="images/support.png"></p>
|
||||
<p class="text-center"><a class="btn btn-primary" href="https://xen-orchestra.com/services/"><i class="fa fa-envelope"></i> Get services</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,41 +0,0 @@
|
||||
<form ng-submit="delete()">
|
||||
<div class="modal-header">
|
||||
<h3>VMs deletion</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
You are going to delete the following VMs, this is a <strong>dangerous
|
||||
action</strong>!
|
||||
</p>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th class="col-sm-3">Name</th>
|
||||
<th class="col-sm-6">Description</th>
|
||||
<th class="col-sm-3">Delete disks?</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr ng-repeat="VM in VMs | orderBy:natural('name_label') track by VM.UUID">
|
||||
<td>{{VM.name_label}}</td>
|
||||
<td>{{VM.name_description}}</td>
|
||||
<td><input type="checkbox" ng-model="disks[VM.UUID]"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-warning"
|
||||
ng-click="$dismiss()"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,23 +0,0 @@
|
||||
<div class="modal-header">
|
||||
<h3><i class="fa fa-exclamation-triangle text-danger"></i> {{title}}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{message}}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
ng-click="$close()"
|
||||
>
|
||||
{{yesButtonLabel}}
|
||||
</button>
|
||||
<button
|
||||
ng-if="noButtonLabel"
|
||||
type="button"
|
||||
class="btn btn-warning"
|
||||
ng-click="$dismiss()"
|
||||
>
|
||||
{{noButtonLabel}}
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,400 +0,0 @@
|
||||
<!-- TODO: lots of stuff. -->
|
||||
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="page-title">
|
||||
<i class="xo-icon-host xo-color-{{host.power_state | lowercase}}"></i>
|
||||
{{host.name_label}}
|
||||
<small ng-if="pool.name_label">
|
||||
(<a ui-sref="pools_view({id: pool.UUID})">{{pool.name_label}}</a>)
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-cogs" style="color: #e25440;"></i> General
|
||||
<span class="quick-edit" tooltip="Edit General settings" ng-click="hostSettings.$show()">
|
||||
<i class="fa fa-edit fa-fw"></i>
|
||||
</span>
|
||||
</p>
|
||||
<div class="general-properties">
|
||||
<form editable-form name="hostSettings" onbeforesave="saveHost($data)">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>
|
||||
<span editable-text="host.name_label" e-name="name_label" e-form="hostSettings">
|
||||
{{host.name_label}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
<span editable-text="host.name_description" e-name="name_description" e-form="hostSettings">
|
||||
{{host.name_description}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>Enabled</dt>
|
||||
<dd>
|
||||
<span editable-checkbox="host.enabled" e-name="enabled" e-form="hostSettings">
|
||||
{{host.enabled}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>Tags</dt>
|
||||
<dd ng-if="host.tags.length">
|
||||
<span ng-repeat="tag in host.tags">
|
||||
<span class="label label-primary">{{tag}}</span>
|
||||
</span>
|
||||
</dd>
|
||||
<dd ng-if="!host.tags.length">
|
||||
<em>No tags.</em>
|
||||
</dd>
|
||||
<dt>CPUs</dt>
|
||||
<dd>{{host.CPUs["cpu_count"]}}x {{host.CPUs["modelname"]}}</dd>
|
||||
<dt>Hostname</dt>
|
||||
<dd>
|
||||
{{host.hostname}}
|
||||
</dd>
|
||||
<dt>UUID</dt>
|
||||
<dd>{{host.UUID}}</dd>
|
||||
<dt>iQN</dt>
|
||||
<dd>{{host.iSCSI_name}}</dd>
|
||||
</dl>
|
||||
<div class="btn-form" ng-show="hostSettings.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="hostSettings.$waiting" ng-click="hostSettings.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="submit" ng-disabled="hostSettings.$waiting" class="btn btn-primary">
|
||||
<i class="fa fa-save"></i> Save
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-bar-chart-o" style="color: #e25440;"></i> Stats</p>
|
||||
<div class="grid">
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">vCPUs/CPU usage:</p>
|
||||
<p class="center big">{{host.$vCPUs}}/{{host.CPUs["cpu_count"]}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">RAM used:</p>
|
||||
<p class="center big">{{[host.memory.usage, host.memory.size] | %}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Running VMs:</p>
|
||||
<p class="center big">{{host.VMs.length}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-flash" style="color: #e25440;"></i> Actions</p>
|
||||
<div class="grid-cell text-center">
|
||||
<div class="grid">
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Add SR" type="button" style="width: 90%" class="btn" xo-click="new_sr()">
|
||||
<i class="xo-icon-sr fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Add VM" type="button" style="width: 90%" class="btn" xo-sref="VMs_new({container: host.UUID})">
|
||||
<i class="xo-icon-vm fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Reboot host" type="button" style="width: 90%" class="btn" xo-click="rebootHost(host.UUID)">
|
||||
<i class="fa fa-refresh fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Shutdown host" type="button" style="width: 90%" class="btn" xo-click="shutdownHost(host.UUID)">
|
||||
<i class="fa fa-power-off fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Restart toolstack" type="button" style="width: 90%" class="btn" xo-click="restartToolStack(host.UUID)">
|
||||
<i class="fa fa-retweet fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group" ng-if="pool.name_label">
|
||||
<button tooltip="Remove from pool" style="width: 90%" type="button" class="btn" xo-click="pool_removeHost(host.UUID)">
|
||||
<i class="fa fa-cloud-upload fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group" ng-if="!pool.name_label">
|
||||
<button tooltip="Add to pool" style="width: 90%" type="button" class="btn" xo-click="pool_addHost(host.UUID)">
|
||||
<i class="fa fa-cloud-download fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group" style="margin-bottom: 0.5em">
|
||||
<button
|
||||
tooltip="Host console"
|
||||
type="button"
|
||||
style="width: 90%"
|
||||
class="btn"
|
||||
ng-repeat="controller in [host.controller] | resolve track by controller.UUID"
|
||||
xo-sref="consoles_view({id: controller.UUID})">
|
||||
<i class="xo-icon-console fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Memory panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title">
|
||||
<i class="xo-icon-memory" style="color: #e25440;"></i> Memory
|
||||
</p>
|
||||
<div class="progress">
|
||||
<div
|
||||
ng-repeat="controller in [host.controller] | resolve track by controller.UUID"
|
||||
class="progress-bar-host"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{controller.memory.size}}"
|
||||
aria-valuemax="{{host.memory.size}}"
|
||||
style="width: {{[controller.memory.size, host.memory.size] | %}}"
|
||||
tooltip="{{host.name_label}}: {{[controller.memory.size, host.memory.size] | %}}"
|
||||
>
|
||||
<small>{{host.name_label}}</small>
|
||||
</div>
|
||||
<div
|
||||
ng-repeat="VM in host.VMs | resolve | orderBy:natural('name_label') track by VM.UUID"
|
||||
class="progress-bar progress-bar-vm"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{VM.memory.size}}"
|
||||
aria-valuemax="{{host.memory.size}}"
|
||||
style="width: {{[VM.memory.size, host.memory.size] | %}}"
|
||||
xo-sref="VMs_view({id: VM.UUID})"
|
||||
tooltip="{{VM.name_label}}: {{[VM.memory.size, host.memory.size] | %}}"
|
||||
>
|
||||
<small>{{VM.name_label}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-inline text-center">
|
||||
<li>Total: {{host.memory.size | bytesToSize}}</li>
|
||||
<li>Currently used: {{host.memory.usage | bytesToSize}}</li>
|
||||
<li>Available: {{host.memory.size-host.memory.usage | bytesToSize}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SR panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="hostSRs" onaftersave="saveTable()" oncancel="cancel()">
|
||||
<p class="flat-panel-title"><i class="xo-icon-sr" style="color: #e25440;"></i> Storage
|
||||
<span class="quick-edit" tooltip="Edit storage" ng-click="hostSRs.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Format</th>
|
||||
<th>Size</th>
|
||||
<th>Physical/Allocated usage</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
<!-- TODO: display PBD status for each SR of this host (connected or not) -->
|
||||
<!-- Shared SR -->
|
||||
<tr
|
||||
xo-sref="SRs_view({id: SR.UUID})"
|
||||
ng-repeat="SR in pool.SRs | resolve | orderBy:natural('name_label') track by SR.UUID"
|
||||
ng-init="PBD = SRsToPBDs[SR.ref]"
|
||||
>
|
||||
<td>
|
||||
{{SR.name_label}}
|
||||
</td>
|
||||
<td>{{SR.SR_type}}</td>
|
||||
<td>{{SR.size | bytesToSize}}</td>
|
||||
<td>
|
||||
<div class="progress-condensed">
|
||||
<div
|
||||
class="progress-bar"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{SR.usage}}"
|
||||
aria-valuemax="{{SR.size}}"
|
||||
style="width: {{[SR.physical_usage, SR.size] | %}}"
|
||||
tooltip="Physical usage: {{[SR.physical_usage, SR.size] | %}}"
|
||||
></div>
|
||||
<div
|
||||
class="progress-bar progress-bar-info"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{SR.physical_usage}}"
|
||||
aria-valuemax="{{SR.size}}"
|
||||
style="width: {{[(SR.usage-SR.physical_usage), SR.size] | %}}"
|
||||
tooltip="Allocated: {{[(SR.usage), SR.size] | %}}"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="label label-primary">Shared</span></td>
|
||||
<td ng-if="PBD.attached">
|
||||
<span class="label label-success">Connected</span>
|
||||
<button type="button" ng-show="hostSRs.$visible" ng-click="disconnectSR(SR.ref)" class="btn btn-sm btn-danger pull-right" tooltip="Disconnect this SR"><i class="fa fa-unlink fa-lg"></i></button>
|
||||
</td>
|
||||
<td ng-if="!PBD.attached">
|
||||
<span class="label label-default">Disconnected</span>
|
||||
<button type="button" ng-show="hostSRs.$visible" ng-click="removeSR(SR.ref)" class="btn btn-sm btn-danger pull-right" tooltip="Remove this SR"><i class="fa fa-trash-o fa-lg"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Local SR -->
|
||||
<!-- TODO: migrate to SRs and not PBDs when implemented in xo-server spec -->
|
||||
<tr
|
||||
xo-sref="SRs_view({id: SR.UUID})"
|
||||
ng-repeat="SR in host.SRs | resolve | orderBy:natural('name_label') track by SR.UUID"
|
||||
ng-init="PBD = SRsToPBDs[SR.ref]"
|
||||
>
|
||||
<td>
|
||||
{{SR.name_label}}
|
||||
</td>
|
||||
<td>{{SR.SR_type}}</td>
|
||||
<td>{{SR.size | bytesToSize}}</td>
|
||||
<td>
|
||||
<div class="progress-condensed">
|
||||
<div
|
||||
class="progress-bar"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{SR.usage}}"
|
||||
aria-valuemax="{{SR.size}}"
|
||||
style="width: {{[SR.physical_usage, SR.size] | %}}"
|
||||
tooltip="Physical usage: {{[SR.physical_usage, SR.size] | %}}"
|
||||
></div>
|
||||
<div
|
||||
class="progress-bar progress-bar-info"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{SR.physical_usage}}"
|
||||
aria-valuemax="{{SR.size}}"
|
||||
style="width: {{[(SR.usage-SR.physical_usage), SR.size] | %}}"
|
||||
tooltip="Allocated: {{[(SR.usage), SR.size] | %}}"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="label label-info">Local</span></td>
|
||||
<td ng-if="PBD.attached">
|
||||
<span class="label label-success">Connected</span>
|
||||
<button type="button" ng-show="hostSRs.$visible" ng-click="disconnectSR(SR.ref)" class="btn btn-sm btn-danger pull-right" tooltip="Disconnect this SR"><i class="fa fa-unlink fa-lg"></i></button>
|
||||
</td>
|
||||
<td ng-if="!PBD.attached">
|
||||
<span class="label label-default">Disabled</span>
|
||||
<button type="button" ng-show="hostSRs.$visible" ng-click="removeSR(SR.ref)" class="btn btn-sm btn-danger pull-right" tooltip="Remove this SR"><i class="fa fa-trash-o fa-lg"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="hostSRs.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="hostSRs.$waiting" ng-click="hostSRs.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="button" ng-disabled="hostSRs.$waiting" ng-click="saveChanges()" class="btn btn-primary"><i class="fa fa-save"></i> Save</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interfaces panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="hostNetwork" onaftersave="saveTable()" oncancel="cancel()">
|
||||
<p class="flat-panel-title"><i class="xo-icon-network" style="color: #e25440;"></i> Interfaces
|
||||
<span class="quick-edit" tooltip="Edit network" ng-click="hostNetwork.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<th class="col-md-1">Device</th>
|
||||
<th class="col-md-1">Address</th>
|
||||
<th class="col-md-2">MAC</th>
|
||||
<th class="col-md-1">MTU</th>
|
||||
<th class="col-md-1">Link status</th>
|
||||
<tr ng-repeat="PIF in host.$PIFs | resolve | orderBy:natural('name_label') track by PIF.UUID">
|
||||
<td>{{PIF.device}}<span ng-if="PIF.vlan > -1">.{{PIF.vlan}}</span> <span ng-if="PIF.management" class="label label-primary">XAPI</span></td>
|
||||
<td>{{PIF.IP}} ({{PIF.mode}})</td>
|
||||
<td>{{PIF.MAC}}</td>
|
||||
<td>
|
||||
<span editable-text="PIF.MTU" e-name="mtu" e-form="hostNetwork" e-required>
|
||||
{{PIF.MTU}}
|
||||
</span>
|
||||
</td>
|
||||
<td ng-if="PIF.attached">
|
||||
<span class="label label-success">Connected</span>
|
||||
<button type="button" ng-show="hostNetwork.$visible" ng-click="disconnectSR(Network.ref)" class="btn btn-sm btn-danger pull-right" tooltip="Disconnect this Network"><i class="fa fa-unlink fa-lg"></i></button>
|
||||
</td>
|
||||
<td ng-if="!PIF.attached">
|
||||
<span class="label label-default">Disconnected</span>
|
||||
<button type="button" ng-show="hostNetwork.$visible" ng-click="removeNetwork(Network.ref)" class="btn btn-sm btn-danger pull-right" tooltip="Remove this Network"><i class="fa fa-trash-o fa-lg"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="hostNetwork.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="hostNetwork.$waiting" ng-click="hostNetwork.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="button" ng-disabled="hostNetwork.$waiting" ng-click="saveChanges()" class="btn btn-primary"><i class="fa fa-save"></i> Save</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- CPU and Logs panels -->
|
||||
<div class="grid">
|
||||
<!-- Task panel -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-tasks" style="color: #e25440;"></i> Pending tasks</p>
|
||||
<p class="center" ng-if="!host.tasks.length">No recent tasks</p>
|
||||
<table class="table table-hover" ng-if="host.tasks.length">
|
||||
<th class="col-md-4">Date</th>
|
||||
<th class="col-md-8">Name</th>
|
||||
<!-- TODO: working reverse order, from recent to oldest -->
|
||||
<tr ng-repeat="task in host.tasks | resolve | orderBy:'created':true track by task.UUID">
|
||||
<td>{{task.created}}</td>
|
||||
<td>
|
||||
{{task.name_label}}
|
||||
<a class="quick-remove" xo-click="removeTask(task.UUID)">
|
||||
<i class="fa fa-trash-o fa-fw"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Logs panel -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="hostLogs" onaftersave="saveTable()" oncancel="cancel()">
|
||||
<p class="flat-panel-title"><i class="fa fa-comments" style="color: #e25440;"></i> Logs
|
||||
<span class="quick-edit" tooltip="Edit logs" ng-click="hostLogs.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<p class="center" ng-if="!host.messages.length">No recent logs</p>
|
||||
<table class="table table-hover" ng-if="host.messages.length">
|
||||
<th>Date</th>
|
||||
<th>Name</th>
|
||||
<tr ng-repeat="message in host.messages | resolve | orderBy:'-time' track by message.UUID">
|
||||
<td>{{message.time*1e3 | date:'medium'}}</td>
|
||||
<td>
|
||||
{{message.name}}
|
||||
<button type="button" ng-show="hostLogs.$visible" ng-click="deleteLog(message.UUID)" class="btn btn-sm btn-danger pull-right"><i class="fa fa-trash-o fa-lg" tooltip="Remove this log entry"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="hostLogs.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="hostLogs.$waiting" ng-click="hostLogs.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1,268 +0,0 @@
|
||||
<!-- TODO: print a message when no entries. -->
|
||||
|
||||
<!-- If it's a (named) pool. -->
|
||||
<div
|
||||
ng-repeat="pool in byTypes.pool | filter:listFilter | orderBy:natural('name_label') track by pool.UUID"
|
||||
ng-if="pool.name_label"
|
||||
class="grid flat-object"
|
||||
xo-sref="pools_view({id: pool.UUID})"
|
||||
>
|
||||
|
||||
<!-- Icon. -->
|
||||
<div class="grid-cell flat-cell flat-cell-type">
|
||||
<i class="xo-icon-pool"></i>
|
||||
</div>
|
||||
|
||||
<!-- Properties & tags. -->
|
||||
<div class="grid-cell">
|
||||
|
||||
<!-- Properties. -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell">
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-cell flat-cell-name">
|
||||
{{pool.name_label}}
|
||||
</div>
|
||||
<div class="grid-cell flat-cell flat-cell-description">
|
||||
<i>{{pool.name_description}}</i>
|
||||
</div>
|
||||
<div class="grid-cell flat-cell" ng-init="default_SR = (pool.default_SR | resolve)">
|
||||
<div ng-if="default_SR">
|
||||
Default SR: <a ui-sref="SRs_view({id: default_SR.UUID})">{{default_SR.name_label}}</a>
|
||||
</div>
|
||||
<div ng-if="!default_SR">
|
||||
<em>No default SR.</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-cell flat-cell" ng-init="master = (pool.master | resolve)">
|
||||
<div ng-if="master">
|
||||
Master: <a ui-sref="hosts_view({id: master.UUID})">{{master.name_label}}</a>
|
||||
</div>
|
||||
<div ng-if="!master">
|
||||
<em>Unknown master.</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
<div ng-if="pool.HA_enabled">
|
||||
HA enabled
|
||||
</div>
|
||||
<div ng-if="!pool.HA_enabled">
|
||||
HA disabled
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
{{pool.$running_hosts.length}}/{{pool.hosts.length}} hosts
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Properties. -->
|
||||
|
||||
<!-- Tags. -->
|
||||
<div class="grid">
|
||||
<div class=" grid-cell">
|
||||
<div class="grid-cell flat-cell-tag">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span ng-repeat="tag in pool.tags">
|
||||
<span class="label label-primary">{{tag}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Tags. -->
|
||||
|
||||
</div>
|
||||
<!-- /Properties & tags. -->
|
||||
|
||||
</div>
|
||||
<!-- /Pool. -->
|
||||
|
||||
<!-- If it's a host. -->
|
||||
<div
|
||||
ng-repeat="host in byTypes.host | filter:listFilter | orderBy:natural('name_label') track by host.UUID"
|
||||
class="grid flat-object"
|
||||
xo-sref="hosts_view({id: host.UUID})"
|
||||
>
|
||||
|
||||
<!-- Icon. -->
|
||||
<div class="grid-cell flat-cell flat-cell-type">
|
||||
<i class="xo-icon-host xo-color-{{host.power_state | lowercase}}"></i>
|
||||
</div>
|
||||
|
||||
<!-- Properties & tags. -->
|
||||
<div class="grid-cell">
|
||||
|
||||
<!-- Properties. -->
|
||||
<div class="grid ">
|
||||
<div class="grid-cell">
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-cell flat-cell-name">
|
||||
{{host.name_label}}
|
||||
</div>
|
||||
<div class="grid-cell flat-cell flat-cell-description">
|
||||
<i>{{host.name_description}}</i>
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
Address: {{host.address}}
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
{{host.$vCPUs}} vCPUs used on {{host.CPUs["cpu_count"]}} CPUs
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
{{host.memory.usage | bytesToSize}} used of {{host.memory.size | bytesToSize}} ({{[host.memory.usage, host.memory.size] | %}})
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
{{host.VMs.length}} VMs running
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Properties. -->
|
||||
|
||||
<!-- Tags. -->
|
||||
<div class="grid">
|
||||
<div class=" grid-cell">
|
||||
<div class="grid-cell flat-cell-tag">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span ng-repeat="tag in host.tags">
|
||||
<span class="label label-primary">{{tag}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Tags. -->
|
||||
|
||||
</div>
|
||||
<!-- /Properties & tags. -->
|
||||
|
||||
</div>
|
||||
<!-- /Host. -->
|
||||
|
||||
<!-- If it's a VM. -->
|
||||
<div
|
||||
ng-repeat="VM in byTypes.VM | filter:listFilter | orderBy:natural('name_label') track by VM.UUID"
|
||||
class="grid flat-object"
|
||||
xo-sref="VMs_view({id: VM.UUID})"
|
||||
>
|
||||
|
||||
<!-- Icon. -->
|
||||
<div class="grid-cell flat-cell flat-cell-type">
|
||||
<i class="xo-icon-vm xo-color-{{VM.power_state | lowercase}}"></i>
|
||||
</div>
|
||||
|
||||
<!-- Properties & tags. -->
|
||||
<div class="grid-cell">
|
||||
|
||||
<!-- Properties. -->
|
||||
<div class="grid ">
|
||||
<div class="grid-cell">
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-cell flat-cell-name">
|
||||
{{VM.name_label}}
|
||||
</div>
|
||||
<div class="grid-cell flat-cell flat-cell-description">
|
||||
<i>{{VM.name_description}}</i>
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
Address: {{VM.addresses['0/ip']}}
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
{{VM.CPUs.number}} vCPUs
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
{{VM.memory.size | bytesToSize}} RAM
|
||||
</div>
|
||||
<div class="grid-cell flat-cell" ng-init="container = (VM.$container | resolve)">
|
||||
<div ng-if="'pool' === container.type">
|
||||
Resident on: <a ui-sref="pools_view({id: container.UUID})">{{container.name_label}}</a>
|
||||
</div>
|
||||
<div ng-if="'host' === container.type" ng-init="pool = (container.poolRef | resolve)">
|
||||
Resident on: <a ui-sref="hosts_view({id: container.UUID})">{{container.name_label}}</a> (<a ui-sref="pools_view({id: pool.UUID})">{{pool.name_label}}</a>)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Properties. -->
|
||||
|
||||
<!-- Tags. -->
|
||||
<div class="grid">
|
||||
<div class=" grid-cell">
|
||||
<div class="grid-cell flat-cell-tag">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span ng-repeat="tag in VM.tags">
|
||||
<span class="label label-primary">{{tag}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Tags. -->
|
||||
|
||||
</div>
|
||||
<!-- /Properties & tags. -->
|
||||
|
||||
</div>
|
||||
<!-- /VM. -->
|
||||
|
||||
<!-- If it's a SR. -->
|
||||
<div
|
||||
ng-repeat="SR in byTypes.SR | filter:listFilter | orderBy:natural('name_label') track by SR.UUID"
|
||||
class="grid flat-object"
|
||||
xo-sref="SRs_view({id: SR.UUID})"
|
||||
>
|
||||
|
||||
<!-- Icon. -->
|
||||
<div class="grid-cell flat-cell flat-cell-type">
|
||||
<i class="xo-icon-sr"></i>
|
||||
</div>
|
||||
|
||||
<!-- Properties & tags. -->
|
||||
<div class="grid-cell">
|
||||
|
||||
<!-- Properties. -->
|
||||
<div class="grid ">
|
||||
<div class="grid-cell">
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-cell flat-cell-name">
|
||||
{{SR.name_label}}
|
||||
</div>
|
||||
<div class="grid-cell flat-cell flat-cell-description">
|
||||
<i>{{SR.name_description}}</i>
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
Usage: {{[SR.usage, SR.size] | %}} ({{SR.usage | bytesToSize}}/{{SR.size | bytesToSize}})
|
||||
</div>
|
||||
<div class="grid-cell flat-cell">
|
||||
Type: {{SR.SR_type}}
|
||||
</div>
|
||||
<div class="grid-cell flat-cell" ng-init="container = (SR.$container | resolve)">
|
||||
<div ng-if="'pool' === container.type">
|
||||
<strong>Shared on <a ui-sref="pools_view({id: container.UUID})">{{container.name_label}}</a></strong>
|
||||
</div>
|
||||
<div ng-if="'host' === container.type">
|
||||
Connected to <a ui-sref="hosts_view({id: container.UUID})">{{container.name_label}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Properties. -->
|
||||
|
||||
<!-- Tags. -->
|
||||
<div class="grid">
|
||||
<div class=" grid-cell">
|
||||
<div class="grid-cell flat-cell-tag">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span ng-repeat="tag in SR.tags">
|
||||
<span class="label label-primary">{{tag}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Tags. -->
|
||||
|
||||
</div>
|
||||
<!-- /Properties & tags. -->
|
||||
|
||||
</div>
|
||||
<!-- /SR. -->
|
||||
@@ -1,512 +0,0 @@
|
||||
<!-- @todo Remove code duplication for the VMs listing by using a macro. -->
|
||||
|
||||
<div class="sub-bar">
|
||||
<div class="grid">
|
||||
<div class="grid-cell overview">
|
||||
<!--Stats-->
|
||||
<i tooltip="{{xo.pools.length}} pools">
|
||||
<i class="small">{{xo.pools.length}}x</i>
|
||||
<i class="xo-icon-pool"></i>
|
||||
</i>
|
||||
|
||||
<i tooltip="{{hosts.length}} hosts">
|
||||
<i class="small">{{hosts.length}}x</i>
|
||||
<i class="xo-icon-host"></i>
|
||||
</i>
|
||||
|
||||
<i tooltip="{{xo.$running_VMs.length}} of {{VMs.length}} VMs running">
|
||||
<i class="small">{{xo.$running_VMs.length}}x</i>
|
||||
<i class="xo-icon-vm"></i>
|
||||
</i>
|
||||
|
||||
<i tooltip="{{xo.$vCPUs}} vCPUs used of {{xo.$CPUs}} CPUs">
|
||||
<i class="small">{{xo.$vCPUs}}x</i>
|
||||
<i class="xo-icon-cpu"></i>
|
||||
</i>
|
||||
|
||||
<i tooltip="{{xo.$memory.usage | bytesToSize}} RAM allocated of {{xo.$memory.size | bytesToSize}}">
|
||||
<i class="small">{{xo.$memory.usage | bytesToSize}}</i>
|
||||
<i class="xo-icon-memory"></i>
|
||||
</i>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<div class="btn-group before-action-bar">
|
||||
<a class="btn navbar-btn btn-default dropdown-toggle inversed">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="inverse"
|
||||
ng-model="master_selection"
|
||||
ng-change="selectVMs(master_selection)"
|
||||
ui-indeterminate="!(all || none)"
|
||||
stop-event="click"
|
||||
/>
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu inverse" role="menu">
|
||||
<li ng-repeat="power_state in ['Halted', 'Running']">
|
||||
<a ng-click="selectVMs({power_state: power_state})">
|
||||
<i class="fa-fw xo-icon-{{power_state | lowercase}}"></i>
|
||||
{{power_state}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li ng-repeat="host in hosts | orderBy:natural('name_label') track by host.UUID" ng-if="host.VMs.length">
|
||||
<a ng-click="selectVMs({$container: host.ref})">
|
||||
<i class="xo-icon-host fa-fw"></i>
|
||||
On {{host.name_label}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="action-bar" ng-if="!none">
|
||||
<div class="btn-group">
|
||||
<button tooltip="Stop VM" type="button" class="btn navbar-btn btn-default inversed" ng-click="bulkAction('stopVM')">
|
||||
<i class="fa fa-stop"></i>
|
||||
</button>
|
||||
<button tooltip="Start VM" type="button" class="btn navbar-btn btn-default inversed" ng-click="bulkAction('startVM')">
|
||||
<i class="fa fa-play"></i>
|
||||
</button>
|
||||
<button tooltip="Reboot VM" type="button" class="btn navbar-btn btn-default inversed" ng-click="bulkAction('rebootVM')">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button tooltip="Migrate VM" type="button" class="btn navbar-btn btn-default dropdown-toggle inversed">
|
||||
<i class="fa fa-share"></i> <i class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu inverse" role="menu">
|
||||
<li ng-repeat="host in hosts | orderBy:natural('name_label') track by host.UUID">
|
||||
<a ng-click="bulkAction('migrateVM',host.UUID)">
|
||||
<i class="xo-icon-host fa-fw"></i>
|
||||
To {{host.name_label}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn navbar-btn btn-default dropdown-toggle inversed">
|
||||
More <i class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu inverse" role="menu">
|
||||
<li><a ng-click="bulkAction('force_rebootVM')"><i class="fa fa-bolt fa-fw"></i> Force reboot</a></li>
|
||||
<li><a ng-click="bulkAction('force_stopVM')"><i class="fa fa-power-off fa-fw"></i> Force shutdown</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a ng-click="deleteVMs()"><i class="fa fa-trash-o fa-fw"></i> Delete</a></li>
|
||||
<li><a ng-click="bulkAction('snapshotVM')"><i class="xo-icon-snapshot fa-fw"></i> Take a snapshot</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FIXME: Ugly trick to force the pools to be under the sub bar. -->
|
||||
<div style="margin-top: 50px; visibility: hidden; height: 0">.</div>
|
||||
|
||||
<!-- If we haven't any data -->
|
||||
<div ng-if="!pools.length">
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel text-center">
|
||||
<h1>Welcome on Xen Orchestra!</h1>
|
||||
<h3>It seems you aren't connected to any Xen host.</h3>
|
||||
<p>You can add any XAPI capable server by following these steps:</p>
|
||||
<ul class="list-unstyled">
|
||||
<li>Login with pre-filled account (top right of this screen)</li>
|
||||
<li>Click on the menu icon " <i class="fa fa-th"></i> " and choose " <i class="fa fa-cog"></i> Settings "</li>
|
||||
<li>Then you can add a server (no need to reload, refresh is auto)</li>
|
||||
<li>Enjoy Xen Orchestra!</li>
|
||||
</ul>
|
||||
<p><a class="btn btn-primary big" ui-sref="about"><i class="fa fa-info-circle"></i> About us</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- If we have data -->
|
||||
<div ng-if="pools.length">
|
||||
<!-- Contains a pool and all its children (hosts). -->
|
||||
<div ng-repeat="pool in pools | orderBy:natural('name_label') track by pool.UUID" class="grid pool-block">
|
||||
|
||||
<!-- Pseudo pool if it is not a named pool. -->
|
||||
<div ng-if="!pool.name_label" class="grid-cell grid--gutters pool-cell">
|
||||
<p class="center" style="margin-top: 2em;">No pool connected</p>
|
||||
</div>
|
||||
|
||||
<!-- Contains information about the pool if it is a named pool. -->
|
||||
<div class="grid-cell grid--gutters pool-cell" ng-if="pool.name_label">
|
||||
|
||||
<!-- Header (name + dropdown menu). -->
|
||||
<div class="dropdown dropdown-pool">
|
||||
<a class="pool-name" ui-sref="pools_view({id: pool.UUID})">
|
||||
{{pool.name_label}}
|
||||
</a>
|
||||
<a class="dropdown-toggle"> <i class="fa fa-caret-down big-caret"></i> </a>
|
||||
<ul class="dropdown-menu left" role="menu">
|
||||
<li><a xo-click="new_sr()"><i class="xo-icon-sr fa-fw"></i> Add SR</a></li>
|
||||
<li><a xo-sref="VMs_new({container: pool.UUID})"><i class="xo-icon-vm fa-fw"></i> Add VM</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a xo-click="pool_disconnect(pool.UUID)" class="disabled"><i class="fa fa-unlink fa-fw"></i> Disconnect</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- /Header. -->
|
||||
|
||||
<!-- Stats & SRs list. -->
|
||||
<div>
|
||||
|
||||
<!-- Stats. -->
|
||||
<ul class="list-unstyled stats">
|
||||
<li>
|
||||
<i tooltip="{{pool.hosts.length}} hosts connected">
|
||||
<i class="small">{{pool.hosts.length}}x</i>
|
||||
<i class="xo-icon-host"></i>
|
||||
</i>
|
||||
|
||||
<i tooltip="{{pool.$running_VMs.length}} of {{pool.$VMs.length}} VMs running">
|
||||
<i class="small">{{pool.$running_VMs.length}}x</i>
|
||||
<i class="xo-icon-vm"></i>
|
||||
</i>
|
||||
</li>
|
||||
<li ng-if="pool.master" ng-init="master = (pool.master | resolve)">
|
||||
Master : <a ui-sref="hosts_view({id: master.UUID})">{{master.name_label}}</a>
|
||||
</li>
|
||||
<li ng-if="!pool.master">No master</li>
|
||||
</ul>
|
||||
<!-- /Stats. -->
|
||||
|
||||
<!-- SRs. -->
|
||||
<div ng-if="pool.SRs.length">
|
||||
<p class="center small-caps">SRs:</p>
|
||||
<table class="table table-hover table-condensed">
|
||||
<tr
|
||||
ng-repeat="SR in pool.SRs | resolve | orderBy:natural('name_label') track by SR.UUID"
|
||||
xo-sref="SRs_view({id: SR.UUID})"
|
||||
>
|
||||
<td
|
||||
class="col-md-6 sr-name no-border"
|
||||
ng-class="{'default-sr': SR.ref === pool.default_SR}"
|
||||
>
|
||||
<i class="xo-icon-sr"></i>
|
||||
{{SR.name_label}}
|
||||
</td>
|
||||
<td class="col-md-6 right no-border">
|
||||
<div class="progress progress-small" tooltip="Disk: {{[SR.usage, SR.size] | %}} allocated">
|
||||
<div
|
||||
class="progress-bar"
|
||||
role="progressbar"
|
||||
aria-valuenow="{{100*SR.usage/SR.size}}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
style="width: {{[SR.usage, SR.size] | %}}"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /SRs. -->
|
||||
|
||||
</div>
|
||||
<!-- /Stats & SRs list. -->
|
||||
|
||||
</div>
|
||||
<!-- /Information about the pool. -->
|
||||
|
||||
<!-- Contains all the hosts of this pool. -->
|
||||
<div class="grid-cell grid--gutters hosts-vms-cells">
|
||||
|
||||
<!-- Contains a host and all its children (VMs). -->
|
||||
<div
|
||||
ng-repeat="host in pool.hosts | resolve | orderBy:natural('name_label') track by host.UUID"
|
||||
class="grid"
|
||||
>
|
||||
|
||||
<!-- Contains information about the host. -->
|
||||
<div class="grid-cell host-cell">
|
||||
|
||||
<!-- Header (name + dropdown menu). -->
|
||||
<div class="dropdown dropdown-pool">
|
||||
<a class="host-name" ui-sref="hosts_view({id: host.UUID})">
|
||||
{{host.name_label}}
|
||||
</a>
|
||||
<a class="dropdown-toggle"> <i class="fa fa-caret-down"></i> </a>
|
||||
<ul class="dropdown-menu left" role="menu">
|
||||
<li><a xo-click="new_sr()"><i class="xo-icon-sr fa-fw"></i> Add SR</a></li>
|
||||
<li><a xo-sref="VMs_new({container: host.UUID})"><i class="xo-icon-vm fa-fw"></i> Add VM</a></li>
|
||||
<li class="divider"></li>
|
||||
<li >
|
||||
<a
|
||||
ng-repeat="controller in [host.controller] | resolve track by controller.UUID"
|
||||
xo-sref="consoles_view({id: controller.UUID})">
|
||||
<i class="xo-icon-console fa-fw"></i> Console
|
||||
</a>
|
||||
</li>
|
||||
<li><a xo-click="rebootHost(host.UUID)"><i class="fa fa-refresh fa-fw"></i> Reboot</a></li>
|
||||
<li><a xo-click="shutdownHost(host.UUID)"><i class="fa fa-power-off fa-fw"></i> Shutdown</a></li>
|
||||
<li><a xo-click="restartToolStack(host.UUID)"><i class="fa fa-retweet fa-fw"></i> Restart toolstack</a></li>
|
||||
<li ng-if="pool.name_label"><a xo-click="pool_removeHost(host.UUID)"><i class="fa fa-cloud-upload fa-fw"></i> Remove from pool</a></li>
|
||||
<li ng-if="!pool.name_label"><a xo-click="pool_addHost(host.UUID)"><i class="fa fa-cloud-download fa-fw"></i> Add to pool</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- /Header. -->
|
||||
|
||||
<!-- Stats. -->
|
||||
<ul class="list-unstyled stats">
|
||||
<!-- Warning icon if host is halted or disabled -->
|
||||
<li ng-if="'Halted' === host.power_state" class="text-danger">
|
||||
<i class="fa fa-warning"></i> Halted
|
||||
</li>
|
||||
<li ng-if="'Halted' !== host.power_state && !host.enabled" class="text-warning">
|
||||
<i class="fa fa-warning"></i> Disabled
|
||||
</li>
|
||||
<!-- Memory -->
|
||||
<li ng-if="host.power_state === 'Running'">
|
||||
<i class="xo-icon-memory i-progress"></i>
|
||||
<div
|
||||
class="progress progress-small"
|
||||
tooltip="RAM: {{[host.memory.usage, host.memory.size] | %}} allocated">
|
||||
<div
|
||||
class="progress-bar"
|
||||
role="progressbar"
|
||||
aria-valuenow="{{100*host.memory.usage/host.memory.size}}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
style="width: {{[host.memory.usage, host.memory.size] | %}}"
|
||||
></div>
|
||||
</div>
|
||||
</li>
|
||||
<!-- Host address -->
|
||||
<li class="text-muted substats">
|
||||
<i class="xo-icon-network"></i>
|
||||
{{host.address}}
|
||||
</li>
|
||||
</ul>
|
||||
<!-- /Stats. -->
|
||||
</div>
|
||||
<!-- /Information about the host. -->
|
||||
|
||||
<!-- Contains all the VMs of this host. -->
|
||||
<div class="grid grid-cell vm-cell">
|
||||
|
||||
<!-- If no VMs, fill the space with a message. -->
|
||||
<div ng-if="!host.VMs.length" class="vms-notice">
|
||||
|
||||
<p ng-if="'Halted' === host.power_state">
|
||||
Host halted.
|
||||
</p>
|
||||
|
||||
<div ng-if="'Halted' !== host.power_state">
|
||||
<p ng-if="!host.enabled">
|
||||
Host disabled.
|
||||
</p>
|
||||
|
||||
<p ng-if="host.enabled">
|
||||
No VMs on this host.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- /Message if no VMs. -->
|
||||
|
||||
<!-- TODO: comment -->
|
||||
<div class="table-responsive" ng-if="host.VMs.length">
|
||||
<table class="table table-hover table-condensed">
|
||||
|
||||
<!-- Contains a VM. -->
|
||||
<tr
|
||||
ng-repeat="VM in host.VMs | resolve | orderBy:natural('name_label') track by VM.UUID"
|
||||
xo-sref="VMs_view({id: VM.UUID})"
|
||||
>
|
||||
|
||||
<!-- Handle used for drag & drop. -->
|
||||
<td class="grab"></td>
|
||||
|
||||
<!-- Checkbox used for selection. -->
|
||||
<td class="select-vm">
|
||||
<input
|
||||
type="checkbox"
|
||||
ng-model="selected_VMs[VM.UUID]"
|
||||
ng-change="updateVMSelection(VM.UUID)"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<!-- Power state -->
|
||||
<td class="vm-power-state">
|
||||
<i ng-if="isVMWorking(VM)"
|
||||
class="xo-icon-working"
|
||||
tooltip="{{VM.power_state}} and {{values(VM.current_operations)[0]}}"
|
||||
></i>
|
||||
<i ng-if="!isVMWorking(VM)"
|
||||
class="xo-icon-{{VM.power_state | lowercase}}"
|
||||
tooltip="{{VM.power_state}}"
|
||||
></i>
|
||||
</td>
|
||||
|
||||
<!-- VM name. -->
|
||||
<td class="vm-name col-md-2"><p class="vm">{{VM.name_label}}</p></td>
|
||||
|
||||
<!-- Quick actions. -->
|
||||
<td class="vm-quick-buttons col-md-2">
|
||||
<div class="quick-buttons">
|
||||
<a tooltip="Shutdown VM" xo-click="stopVM(VM.UUID)">
|
||||
<i class="fa fa-stop"></i>
|
||||
</a>
|
||||
<a tooltip="Start VM" xo-click="startVM(VM.UUID)">
|
||||
<i class="fa fa-play"></i>
|
||||
</a>
|
||||
<a tooltip="Reboot VM" xo-click="rebootVM(VM.UUID)">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</a>
|
||||
<a tooltip="VM Console" xo-sref="consoles_view({id: VM.UUID})">
|
||||
<i class="xo-icon-console"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Description. -->
|
||||
<td class="vm-description col-md-4">
|
||||
<i ng-if="VM.os_version.distro" tooltip="{{VM.os_version.name}}" class="xo-icon-{{osType(VM.os_version.distro)}}"> </i>
|
||||
<i ng-if="!VM.os_version.distro" class="fa fa-fw"></i>
|
||||
{{VM.name_description}}
|
||||
</td>
|
||||
|
||||
<!-- Metrics. -->
|
||||
|
||||
<!-- Memory -->
|
||||
<td class="vm-memory-stat col-md-2">
|
||||
<div class="cpu">
|
||||
{{VM.memory.size | bytesToSize}}
|
||||
</div>
|
||||
|
||||
<i
|
||||
ng-if="VM.PV_drivers"
|
||||
class="fa fa-fw"
|
||||
></i>
|
||||
<i
|
||||
ng-if="!VM.PV_drivers"
|
||||
class="xo-icon-info fa-fw"
|
||||
tooltip="Xen tools not installed"
|
||||
></i>
|
||||
</td>
|
||||
<!-- /Metrics. -->
|
||||
|
||||
<!-- Address. -->
|
||||
<td class="text-muted text-right col-md-2">
|
||||
{{VM.addresses['0/ip']}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- /VMs of this host. -->
|
||||
|
||||
</div>
|
||||
<!-- /Host with its children. -->
|
||||
|
||||
<!-- Contains a pseudo-host which contains all VMs not in any hosts. -->
|
||||
<div ng-if="pool.VMs.length" class="grid">
|
||||
|
||||
<!-- This is where the information about a host would be displayed. -->
|
||||
<div class="grid-cell host-cell"></div>
|
||||
|
||||
<!-- Contains all the VMs of this pool. -->
|
||||
<div class="grid grid-cell vm-cell">
|
||||
|
||||
<!-- TODO: comment -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-condensed">
|
||||
|
||||
<!-- Contains a VM. -->
|
||||
<tr
|
||||
ng-repeat="VM in pool.VMs | resolve | orderBy:natural('name_label') track by VM.UUID"
|
||||
xo-sref="VMs_view({id: VM.UUID})"
|
||||
>
|
||||
|
||||
<!-- Handle used for drag & drop. -->
|
||||
<td class="grab"></td>
|
||||
|
||||
<!-- Checkbox used for selection. -->
|
||||
<td class="select-vm">
|
||||
<input
|
||||
type="checkbox"
|
||||
ng-model="selected_VMs[VM.UUID]"
|
||||
ng-change="updateVMSelection(VM.UUID)"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<!-- Power state -->
|
||||
<td class="vm-power-state">
|
||||
<i
|
||||
class="xo-icon-{{VM.power_state | lowercase}}"
|
||||
tooltip="{{VM.power_state}}"
|
||||
></i>
|
||||
</td>
|
||||
|
||||
<!-- VM name. -->
|
||||
<td class="vm-name col-md-2">
|
||||
<p class="vm">{{VM.name_label}}</p>
|
||||
</td>
|
||||
|
||||
<!-- Quick actions. -->
|
||||
<td class="vm-quick-buttons col-md-2">
|
||||
<div class="quick-buttons">
|
||||
<a tooltip="Shutdown VM" xo-click="stopVM(VM.UUID)">
|
||||
<i class="fa fa-stop"></i>
|
||||
</a>
|
||||
<a tooltip="Start VM" xo-click="startVM(VM.UUID)">
|
||||
<i class="fa fa-play"></i>
|
||||
</a>
|
||||
<a tooltip="Reboot VM" xo-click="rebootVM(VM.UUID)">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</a>
|
||||
<a tooltip="VM Console">
|
||||
<i class="xo-icon-console"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Description. -->
|
||||
<td class="vm-description col-md-4">
|
||||
<i ng-if="VM.os_version.distro" tooltip="{{VM.os_version.name}}" class="xo-icon-{{osType(VM.os_version.distro)}}"> </i>
|
||||
<i ng-if="!VM.os_version.distro" class="fa fa-fw"></i>
|
||||
{{VM.name_description}}
|
||||
</td>
|
||||
|
||||
<!-- Metrics. -->
|
||||
|
||||
<!-- Memory -->
|
||||
<td class="vm-memory-stat col-md-2">
|
||||
<div class="cpu">
|
||||
{{VM.memory.size | bytesToSize}}
|
||||
</div>
|
||||
|
||||
<i
|
||||
ng-if="VM.PV_drivers"
|
||||
class="fa fa-fw"
|
||||
></i>
|
||||
<i
|
||||
ng-if="!VM.PV_drivers"
|
||||
class="xo-icon-info fa-fw"
|
||||
tooltip="Xen tools not installed"
|
||||
></i>
|
||||
</td>
|
||||
<!-- /Metrics. -->
|
||||
|
||||
<!-- Address. -->
|
||||
<td class="text-muted text-right col-md-2">
|
||||
{{VM.addresses['0/ip']}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- /Pseudo host containing VMs not on any hosts. -->
|
||||
|
||||
</div>
|
||||
<!-- /Hosts of this pool. -->
|
||||
|
||||
</div>
|
||||
<!-- /Pool with its children. -->
|
||||
</div>
|
||||
@@ -1,174 +0,0 @@
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
|
||||
<!-- Button used to (un)collapse on mobile display. -->
|
||||
<button
|
||||
type="button"
|
||||
class="navbar-toggle"
|
||||
ng-init="collapsed = true"
|
||||
ng-click="collapsed = !collapsed"
|
||||
>
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
|
||||
<!-- Brand name. -->
|
||||
<a class="navbar-brand" ui-sref="home">Xen Orchestra</a>
|
||||
</div>
|
||||
|
||||
<!-- All navbar items are collapsed on mobile display. -->
|
||||
<div class="collapse navbar-collapse" ng-class="!collapsed && 'in'">
|
||||
|
||||
<!-- Search form of the navbar. -->
|
||||
<form class="navbar-form navbar-left" role="search" style="width: 250px">
|
||||
<!-- Forced width due to issue with `input`s
|
||||
(https://github.com/twbs/bootstrap/issues/9950. -->
|
||||
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control inverse"
|
||||
placeholder=""
|
||||
ng-model="$root.listFilter"
|
||||
ng-change="ensureListView()"
|
||||
>
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-search" ng-click="ensureListView()">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
<!-- /Search form. -->
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="https://xen-orchestra.com/services?from=xoa" target="_blank">
|
||||
<i class="xo-icon-info"></i> Unregistered version
|
||||
</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Right items of the navbar. -->
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
|
||||
<li ng-if="'disconnected' === status" class="navbar-text">
|
||||
<i class="xo-icon-error"></i> Disconnected from XO-Server
|
||||
</li>
|
||||
|
||||
<li ng-if="'connecting' === status" class="navbar-text">
|
||||
<i class="fa fa-refresh fa-spin"></i> Connecting to XO-Server
|
||||
</li>
|
||||
|
||||
<!-- Main menu. -->
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle inverse">
|
||||
<i class="fa fa-th"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu inverse">
|
||||
<li ui-sref-active="active" ui-route="/">
|
||||
<a ui-sref="home">
|
||||
<i class="fa fa-indent"></i>
|
||||
Tree view
|
||||
</a>
|
||||
</li>
|
||||
<li ui-sref-active="active" ui-route="/list">
|
||||
<a ui-sref="list">
|
||||
<i class="fa fa-align-justify"></i>
|
||||
Flat view
|
||||
</a>
|
||||
</li>
|
||||
<li class="disabled" ui-sref-active="active" ui-route="/graph">
|
||||
<a ui-sref="graph">
|
||||
<i class="fa fa-sitemap"></i>
|
||||
Graphs view
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li class="disabled">
|
||||
<a>
|
||||
<i class="fa fa-clock-o"></i>
|
||||
Scheduler
|
||||
</a>
|
||||
</li>
|
||||
<li ui-sref-active="active" ui-route="/settings">
|
||||
<a ui-sref="settings">
|
||||
<i class="fa fa-cog"></i>
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li ui-sref-active="active" ui-route="/about">
|
||||
<a ui-sref="about">
|
||||
<i class="fa fa-info-circle" style="color:#5bc0de"></i>
|
||||
About
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!-- /Main menu. -->
|
||||
|
||||
<!-- Displayed only when the user is connected. -->
|
||||
<li ng-if="user"><a><i class="fa fa-user"></i> {{user.email}}</a></li>
|
||||
<li ng-if="user"><a ng-click="logOut()"><i class="fa fa-sign-out"></i></a></li>
|
||||
<!-- /When user is connected. -->
|
||||
|
||||
<!-- Displayed only when the user is not connected. -->
|
||||
<li ng-if="!user" class="dropdown">
|
||||
|
||||
<a class="dropdown-toggle">
|
||||
Log in <i class="fa fa-sign-in"></i>
|
||||
</a>
|
||||
|
||||
<form
|
||||
class="dropdown-menu login-form-dark"
|
||||
ng-submit="logIn(login.email, login.password, true)"
|
||||
ng-click="$event.stopPropagation()"
|
||||
>
|
||||
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
<i class="fa fa-user fa-fw"></i>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Email"
|
||||
ng-model="login.email"
|
||||
name="email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
<i class="fa fa-key fa-fw"></i>
|
||||
</span>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
placeholder="Password"
|
||||
name="password"
|
||||
ng-model="login.password"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<i class="fa fa-sign-in"></i> Log in
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
</li>
|
||||
<!-- /When user is not connected. -->
|
||||
|
||||
</ul>
|
||||
<!-- /Right items. -->
|
||||
|
||||
</div>
|
||||
<!-- /Navbar items. -->
|
||||
|
||||
</nav>
|
||||
<!-- /Navbar. -->
|
||||
@@ -1,49 +0,0 @@
|
||||
<!-- TODO: lots of stuff. -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="page-title"><i class="xo-icon-vm"></i> Add SR on {{container.name_label}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add server panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-info-circle" style="color: #e25440;"></i> SR info</p>
|
||||
<form class="form-horizontal" role="form">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Type</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control">
|
||||
<option value="">-- Choose a type of SR --</option>
|
||||
<optgroup label="VDI SR">
|
||||
<option value="NFS">NFS</option>
|
||||
<option value="ISCSI">Software iSCSI</option>
|
||||
<option value="HBA">HBA</option>
|
||||
</optgroup>
|
||||
<optgroup label="ISO SR">
|
||||
<option value="CIFSISO">CIFS</option>
|
||||
<option value="NFSISO">NFS</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Name</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" placeholder="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Description</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" placeholder="SR Created by Xen Orchestra">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<p class="text-center"><a class="btn btn-primary big"><i class="fa fa-times"></i> Clear</a> <a class="btn btn-success big"><i class="fa fa-save"></i> Save</a></p>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-link" style="color: #e25440;"></i> Connection</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,350 +0,0 @@
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="page-title">
|
||||
<i class="xo-icon-vm"></i>
|
||||
Add VM on
|
||||
<a
|
||||
ng-if="'pool' === container.type"
|
||||
ui-sref="pools_view({id: container.UUID})"
|
||||
>
|
||||
{{container.name_label}}
|
||||
</a>
|
||||
<a
|
||||
ng-if="'host' === container.type"
|
||||
ui-sref="hosts_view({id: container.UUID})"
|
||||
>
|
||||
{{container.name_label}}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add server panel -->
|
||||
<form class="form-horizontal" ng-submit="createVM()">
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title">
|
||||
<i class="fa fa-info-circle" style="color: #e25440;"></i> VM info
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Template</label>
|
||||
<div class="col-sm-9">
|
||||
<select
|
||||
class="form-control"
|
||||
ng-model="template"
|
||||
ng-options="template.name_label for template in templates | orderBy:natural('name_label') track by template.UUID"
|
||||
required
|
||||
>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Name</label>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Name of your new VM"
|
||||
required
|
||||
ng-model="name_label"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Description</label>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Optional description of you new VM"
|
||||
ng-model="name_description"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-dashboard" style="color: #e25440;"></i> Performances</p>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">vCPUs</label>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="{{template.CPUs.number}}"
|
||||
ng-model="CPUs"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">RAM</label>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="{{template.memory.size | bytesToSize}}"
|
||||
ng-model="memory"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<!-- Install panel -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-download" style="color: #e25440;"></i> Install settings</p>
|
||||
<div
|
||||
class="form-group"
|
||||
ng-show="availableMethods.cdrom"
|
||||
>
|
||||
<label class="col-sm-3 control-label">ISO/DVD</label>
|
||||
<div class="col-sm-9"><div class="input-group">
|
||||
<span class="input-group-addon"><input
|
||||
type="radio"
|
||||
value="cdrom"
|
||||
ng-model="installation_method"
|
||||
></span>
|
||||
<select
|
||||
ng-disabled="'cdrom' !== installation_method"
|
||||
class="form-control disabled"
|
||||
ng-model="installation_cdrom"
|
||||
>
|
||||
<optgroup
|
||||
ng-repeat="SR in ISO_SRs | orderBy:natural('name_label') track by SR.UUID"
|
||||
ng-if="SR.VDIs.length"
|
||||
label="{{SR.name_label}}"
|
||||
>
|
||||
<option
|
||||
ng-repeat="VDI in SR.VDIs | resolve | orderBy:natural('name_label') track by VDI.UUID"
|
||||
ng-value="VDI.UUID"
|
||||
>
|
||||
{{VDI.name_label}}
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div></div>
|
||||
</div>
|
||||
<div
|
||||
class="form-group"
|
||||
ng-show="availableMethods.http || availableMethods.ftp || availableMethods.nfs"
|
||||
>
|
||||
<label class="col-sm-3 control-label">Network</label>
|
||||
<div class="col-sm-9"><div class="input-group">
|
||||
<span class="input-group-addon"><input
|
||||
type="radio"
|
||||
ng-model="installation_method"
|
||||
value="network"
|
||||
></span>
|
||||
<input
|
||||
ng-disabled="'network' !== installation_method"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="e.g: http://ftp.debian.org/debian"
|
||||
ng-model="installation_network"
|
||||
>
|
||||
</div></div>
|
||||
</div>
|
||||
<!-- <div class="form-group"> FIXME
|
||||
<label class="col-sm-3 control-label">Home server</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control">
|
||||
<option>Default (auto)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- Interface panel -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="xo-icon-network" style="color: #e25440;"></i> Interfaces
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>MAC</th>
|
||||
<th>Network</th>
|
||||
<th class="col-md-1"> </th><!-- Buttons -->
|
||||
</tr>
|
||||
<tr ng-repeat="VIF in VIFs track by VIF.id">
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="VIF.MAC"
|
||||
ng-pattern="/^\s*[0-9a-f]{2}(:[0-9a-f]{2}){5}\s*$/i"
|
||||
placeholder="00:00:00:00:00"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="network.UUID as network.name_label for network in (networks | orderBy:natural('name_label'))"
|
||||
ng-model="VIF.network"
|
||||
required
|
||||
>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<div class="pull-right">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
ng-click="removeVIF($index)"
|
||||
title="Remove this interface"
|
||||
>
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form">
|
||||
<p class="center">
|
||||
<div class="btn-form">
|
||||
<p class="center">
|
||||
<button type="button" ng-click="addVIF()" class="btn btn-success">
|
||||
<i class="fa fa-plus"></i> Add interface
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end of misc and interface panel -->
|
||||
|
||||
<!-- Disk panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title">
|
||||
<i class="xo-icon-sr" style="color: #e25440;"></i> Disks
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th class="col-md-2">SR</th>
|
||||
<th class="col-md-1">Bootable?</th>
|
||||
<th class="col-md-2">Size</th>
|
||||
<th class="col-md-2">Name</th>
|
||||
<th class="col-md-4">Description</th>
|
||||
<th class="col-md-1"> </th><!-- Buttons -->
|
||||
</tr>
|
||||
<tr ng-repeat="VDI in VDIs track by VDI.id">
|
||||
<td>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-model="VDI.SR"
|
||||
ng-options="SR.UUID as SR.name_label for SR in (writable_SRs | orderBy:natural('name_label'))"
|
||||
>
|
||||
</select>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
ng-model="VDI.bootable"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="VDI.size"
|
||||
required
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Name of this virtual disk"
|
||||
ng-model="VDI.name_label"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Description of this virtual disk"
|
||||
ng-model="VDI.name_description"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
ng-click="moveVDI($index, -1)"
|
||||
ng-disabled="$first"
|
||||
title="Move this disk up"
|
||||
>
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
ng-click="moveVDI($index, 1)"
|
||||
ng-disabled="$last"
|
||||
title="Move this disk down"
|
||||
>
|
||||
<i class="fa fa-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
ng-click="removeVDI($index)"
|
||||
title="Remove this disk"
|
||||
>
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form">
|
||||
<p class="center">
|
||||
<div class="btn-form">
|
||||
<p class="center">
|
||||
<button type="button" ng-click="addVDI()" class="btn btn-success"><i class="fa fa-plus"></i> Add disk</button>
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirmation panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-flag-checkered" style="color: #e25440;"></i> Summary</p>
|
||||
<div class="grid">
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Name:
|
||||
<p class="center big">{{name_label}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Template:
|
||||
<p class="center">{{template.name_label}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">vCPUs</p>
|
||||
<p class="center big">{{CPUs || template.CPUs.number}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">RAM</p>
|
||||
<p class="center big">{{(memory) || (template.memory.size | bytesToSize)}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Disks</p>
|
||||
<p class="center big">{{(VDIs.length) || (template.$VBDs.length) || 0}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Interfaces</p>
|
||||
<p class="center big">{{VIFs.length}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="center"><button type="submit" class="btn btn-lg btn-primary"><i class="fa fa-play"></i> Create VM</button></p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,220 +0,0 @@
|
||||
<!-- TODO: lots of stuff. -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="page-title"><i class="xo-icon-pool"></i> {{pool.name_label}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title">
|
||||
<i class="fa fa-cogs" style="color: #e25440;"></i> General
|
||||
<span class="quick-edit" tooltip="Edit General settings" ng-click="poolSettings.$show()">
|
||||
<i class="fa fa-edit fa-fw"></i>
|
||||
</span>
|
||||
</p>
|
||||
<div class="general-properties">
|
||||
<form editable-form name="poolSettings" onbeforesave="savePool($data)">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>
|
||||
<span editable-text="pool.name_label" e-name="name_label" e-form="poolSettings">
|
||||
{{pool.name_label}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
<span editable-text="pool.name_description" e-name="name_description" e-form="poolSettings">
|
||||
{{pool.name_description}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>Master</dt>
|
||||
<dd ng-repeat="master in [pool.master] | resolve">
|
||||
<a ui-sref="hosts_view({id: master.UUID})">
|
||||
{{master.name_label}}
|
||||
</a>
|
||||
</dd>
|
||||
<dt>Tags</dt>
|
||||
<dd>
|
||||
<span ng-repeat="tag in pool.tags">
|
||||
<span class="label label-primary">{{tag}}</span>
|
||||
</span>
|
||||
</dd>
|
||||
<dt ng-if="pool.default_SR">Default SR</dt>
|
||||
<dd ng-if="pool.default_SR" ng-init="default_SR = (pool.default_SR | resolve)">
|
||||
<a ui-sref="SRs_view({id: default_SR.UUID})">{{default_SR.name_label}}</a>
|
||||
</dd>
|
||||
<dt>HA</dt>
|
||||
<dd>
|
||||
{{pool.HA_enabled}}
|
||||
</dd>
|
||||
<dt>UUID</dt>
|
||||
<dd>{{pool.UUID}}</dd>
|
||||
</dl>
|
||||
<div class="btn-form" ng-show="poolSettings.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="poolSettings.$waiting" ng-click="poolSettings.$cancel()" class="btn btn-default">
|
||||
<i class="fa fa-times"></i> Cancel
|
||||
</button>
|
||||
<button type="submit" ng-disabled="poolSettings.$waiting" class="btn btn-primary">
|
||||
<i class="fa fa-save"></i> Save
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-bar-chart-o" style="color: #e25440;"></i> Stats</p>
|
||||
<div class="grid">
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Hosts:</p>
|
||||
<p class="center big">{{pool.hosts.length}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Running:</p>
|
||||
<p class="center big">{{pool.$running_hosts.length}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-flash" style="color: #e25440;"></i> Actions</p>
|
||||
<div class="grid">
|
||||
<div class="grid-cell text-center grid-button" tooltip="Add SR" xo-sref="SRs_new({container: pool.UUID})">
|
||||
<i class="xo-icon-sr fa-2x fa-fw"></i>
|
||||
</div>
|
||||
<div class="grid-cell text-center grid-button" tooltip="Add VM" xo-sref="VMs_new({container: pool.UUID})">
|
||||
<i class="xo-icon-vm fa-2x fa-fw"></i>
|
||||
</div>
|
||||
<div class="grid-cell text-center grid-button" tooltip="Add Host">
|
||||
<i class="xo-icon-host fa-2x fa-fw"></i>
|
||||
</div>
|
||||
<div class="grid-cell text-center grid-button" tooltip="Disconnect">
|
||||
<i class="fa fa-unlink fa-2x fa-fw"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hosts panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="xo-icon-host" style="color: #e25440;"></i> Hosts</p>
|
||||
<table class="table table-hover table-condensed">
|
||||
<th>Name</th>
|
||||
<th class="col-md-4">Description</th>
|
||||
<th class="col-md-6">Memory</th>
|
||||
<tr
|
||||
xo-sref="hosts_view({id: host.UUID})"
|
||||
ng-repeat="host in pool.hosts | resolve | orderBy:natural('name_label') track by host.UUID"
|
||||
>
|
||||
<td>{{host.name_label}}</td>
|
||||
<td>{{host.name_description}}</td>
|
||||
<td>
|
||||
<div class="progress-condensed">
|
||||
<div
|
||||
class="progress-bar"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{host.memory.usage}}"
|
||||
aria-valuemax="{{host.memory.size}}"
|
||||
style="width: {{[host.memory.usage, host.memory.size] | %}}"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Shared SR panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="xo-icon-sr" style="color: #e25440;"></i> Shared SR
|
||||
<table class="table table-hover">
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Type</th>
|
||||
<th>Size</th>
|
||||
<th class="col-md-4">Physical/Allocated usage</th>
|
||||
<tr
|
||||
xo-sref="SRs_view({id: SR.UUID})"
|
||||
ng-repeat="SR in pool.SRs | resolve | orderBy:natural('name_label') track by SR.UUID"
|
||||
>
|
||||
<td>{{SR.name_label}}</td>
|
||||
<td>{{SR.name_description}}</td>
|
||||
<td>{{SR.SR_type}}</td>
|
||||
<td>{{SR.size | bytesToSize}}</td>
|
||||
<td>
|
||||
<div class="progress-condensed">
|
||||
<div
|
||||
class="progress-bar"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{SR.usage}}"
|
||||
aria-valuemax="{{SR.size}}"
|
||||
style="width: {{[SR.physical_usage, SR.size] | %}}"
|
||||
tooltip="Physical usage: {{[SR.physical_usage, SR.size] | %}}"
|
||||
></div>
|
||||
<div
|
||||
class="progress-bar progress-bar-info"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{SR.physical_usage}}"
|
||||
aria-valuemax="{{SR.size}}"
|
||||
style="width: {{[(SR.usage-SR.physical_usage), SR.size] | %}}"
|
||||
tooltip="Allocated: {{[(SR.usage), SR.size] | %}}"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: CPU and Logs panels -->
|
||||
<div class="grid">
|
||||
<!-- Logs panel -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="poolLogs" onaftersave="saveTable()" oncancel="cancel()">
|
||||
<p class="flat-panel-title"><i class="fa fa-comments" style="color: #e25440;"></i> Logs
|
||||
<span class="quick-edit" tooltip="Edit logs" ng-click="poolLogs.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<p class="center" ng-if="!pool.messages.length">No recent logs</p>
|
||||
<table class="table table-hover" ng-if="pool.messages.length">
|
||||
<th>Date</th>
|
||||
<th>Name</th>
|
||||
<tr ng-repeat="message in pool.messages | resolve | orderBy:'-time' track by message.UUID">
|
||||
<td>{{message.time*1e3 | date:'medium'}}</td>
|
||||
<td>
|
||||
{{message.name}}
|
||||
<button type="button" ng-show="poolLogs.$visible" ng-click="deleteLog(message.UUID)" class="btn btn-sm btn-danger pull-right"><i class="fa fa-trash-o fa-lg" tooltip="Remove this log entry"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="poolLogs.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="poolLogs.$waiting" ng-click="poolLogs.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Template panel -->
|
||||
<!-- FIXME: error on accordion display, DOES NOT WORK SO FAR -->
|
||||
<!-- <div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-copy" style="color: #e25440;"></i> Templates</p>
|
||||
<p class="center" ng-if="!pool.templates.length">No templates</p>
|
||||
<accordion close-others="true">
|
||||
<accordion-group heading="{{template.name_label}}" ng-repeat="template in pool.templates | resolve | orderBy:natural('name_label') track by template.UUID">
|
||||
{{template.name_description}}
|
||||
</accordion-group>
|
||||
</accordion>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
@@ -1,104 +0,0 @@
|
||||
<!-- TODO: lots of stuff. -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="page-title"><i class="fa fa-cog"></i> XO Settings</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add server panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-link" style="color: #e25440;"></i> Connected servers</p>
|
||||
<form ng-submit="saveServers()" autocomplete="off">
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>Host</th>
|
||||
<th>User</th>
|
||||
<th>Password</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
<tr ng-repeat="server in servers | orderBy:natural('host') track by server.id">
|
||||
<td><input type="text" ng-model="server.host" class="form-control" /></td>
|
||||
<td><input type="text" ng-model="server.username" class="form-control"/></td>
|
||||
<td>
|
||||
<input type="password" ng-model="server.password" class="form-control" placeholder="Fill to change the password"/>
|
||||
</td>
|
||||
<td><input type="checkbox" ng-model="selectedServers[server.id]"/></td>
|
||||
</tr>
|
||||
<tr ng-repeat="server in newServers">
|
||||
<td>
|
||||
<input type="text" ng-model="server.host" class="form-control" placeholder="address[:port]"/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" ng-model="server.username" class="form-control" placeholder="user"/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="password" ng-model="server.password" class="form-control" placeholder="password"/>
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="text-center">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa fa-save"></i> Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" ng-click="addServer()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-users" style="color: #e25440;"></i> Users</p>
|
||||
<form ng-submit="saveUsers()" autocomplete="off">
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<th>Permissions</th>
|
||||
<th>Password</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
<tr ng-repeat="user in users | orderBy:natural('email') track by user.id">
|
||||
<td><input type="text" ng-model="user.email" class="form-control"/></td>
|
||||
<td>
|
||||
<select
|
||||
ng-options="p.value as p.label for p in permissions"
|
||||
ng-model="user.permission"
|
||||
class="form-control"
|
||||
>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input type="password" ng-model="user.password" class="form-control" placeholder="Fill to change the password"/>
|
||||
</td>
|
||||
<td><input type="checkbox" ng-model="selectedUsers[user.id]" /></td>
|
||||
</tr>
|
||||
<tr ng-repeat="user in newUsers">
|
||||
<td>
|
||||
<input type="text" ng-model="user.email" class="form-control" placeholder="email"/>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
ng-options="p.value as p.label for p in permissions"
|
||||
ng-model="user.permission"
|
||||
class="form-control"
|
||||
>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input type="password" ng-model="user.password" class="form-control" placeholder="password"/>
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="text-center">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa fa-save"></i> Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" ng-click="addUser()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,228 +0,0 @@
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="page-title"><i class="xo-icon-sr"></i> {{SR.name_label}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title">
|
||||
<i class="fa fa-cogs" style="color: #e25440;"></i> General
|
||||
<span class="quick-edit" tooltip="Edit General settings" ng-click="srSettings.$show()">
|
||||
<i class="fa fa-edit fa-fw"></i>
|
||||
</span>
|
||||
</p>
|
||||
<div class="general-properties">
|
||||
<form editable-form name="srSettings" onbeforesave="saveSR($data)">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>
|
||||
<span editable-text="SR.name_label" e-name="name_label" e-form="srSettings">
|
||||
{{SR.name_label}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
<span editable-text="SR.name_description" e-name="name_description" e-form="srSettings">
|
||||
{{SR.name_description}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>Content type:</dt>
|
||||
<dd>{{SR.SR_type}}</dd>
|
||||
<dt>Tags</dt>
|
||||
<dd ng-if="SR.tags.length">
|
||||
<span ng-repeat="tag in SR.tags">
|
||||
<span class="label label-primary">{{tag}}</span>
|
||||
</span>
|
||||
</dd>
|
||||
<dd ng-if="!SR.tags.length">
|
||||
<em>No tags.</em>
|
||||
</dd>
|
||||
<dt>Shared</dt>
|
||||
<div ng-repeat="container in [SR.$container] | resolve">
|
||||
<dd ng-if="'pool' === container.type">Yes (<a ui-sref="pools_view({id: container.UUID})">{{container.name_label}}</a>)</dd>
|
||||
<dd ng-if="'host' === container.type">No</dd>
|
||||
</div>
|
||||
<dt>Size</dt>
|
||||
<dd>{{SR.size | bytesToSize}}</dd>
|
||||
<dt>UUID</dt>
|
||||
<dd>{{SR.UUID}}</dd>
|
||||
</dl>
|
||||
<div class="btn-form" ng-show="srSettings.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="srSettings.$waiting" ng-click="srSettings.$cancel()" class="btn btn-default">
|
||||
<i class="fa fa-times"></i> Cancel
|
||||
</button>
|
||||
<button type="submit" ng-disabled="srSettings.$waiting" class="btn btn-primary">
|
||||
<i class="fa fa-save"></i> Save
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-bar-chart-o" style="color: #e25440;"></i> Stats</p>
|
||||
<div class="grid">
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Physical Alloc:</p>
|
||||
<p class="center big">{{[SR.physical_usage, SR.size] | %}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Virtual Alloc:</p>
|
||||
<p class="center big">{{[SR.usage, SR.size] | %}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">VDIs:</p>
|
||||
<p class="center big">{{SR.VDIs.length}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Space panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title">
|
||||
<i class="xo-icon-memory" style="color: #e25440;"></i> VDI Map
|
||||
</p>
|
||||
<div class="progress">
|
||||
<div
|
||||
ng-if="((VDI.size/SR.size)*100) > 0.5"
|
||||
ng-repeat="VDI in SR.VDIs | resolve | orderBy:natural('name_label') track by VDI.UUID"
|
||||
class="progress-bar progress-bar-vm"
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="{{VDI.size}}"
|
||||
aria-valuemax="{{SR.size}}"
|
||||
style="width: {{[VDI.size, SR.size] | %}}"
|
||||
xo-sref="VDIs_view({id: VDI.UUID})"
|
||||
tooltip="{{VDI.name_label}} ({{[VDI.size, SR.size] | %}})"
|
||||
>
|
||||
<!-- display the name only if it fits in its progress bar -->
|
||||
<span ng-if="VDI.name_label.length < ((VDI.size/SR.size)*100)">{{VDI.name_label}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-inline text-center">
|
||||
<li>Total: {{SR.size | bytesToSize}}</li>
|
||||
<li>Currently used: {{SR.usage | bytesToSize}}</li>
|
||||
<li>Available: {{SR.size-SR.usage | bytesToSize}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: VDIs. -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="srDisks" onbeforesave="saveVDI()" oncancel="cancel()">
|
||||
<p class="flat-panel-title">
|
||||
<i class="fa fa-hdd-o" style="color: #e25440;"></i> Virtual disks
|
||||
<span class="quick-edit" tooltip="Edit disks" ng-click="srDisks.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
<span class="quick-edit" tooltip="Rescan" ng-click="rescanSr(SR.UUID)">
|
||||
<i class="fa fa-refresh fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Size</th>
|
||||
<th>Attached to:</th>
|
||||
</tr>
|
||||
<tr
|
||||
ng-repeat="VDI in SR.VDIs | resolve | orderBy:natural('name_label')"
|
||||
xo-sref="VDIs_view({id: VDI.UUID})"
|
||||
>
|
||||
<td>
|
||||
<span editable-text="VDI.name_label" e-name="name_description" e-form="srDisks">
|
||||
{{VDI.name_label}} <span ng-if="VDI.$snapshot_of" class="label label-info">snapshot</span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span editable-text="VDI.name_description" e-name="name_description" e-form="srDisks">
|
||||
{{VDI.name_description}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span editable-text="VDI.size | bytesToSize" e-name="size" e-form="srDisks">
|
||||
{{VDI.size | bytesToSize}}
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
ng-if="((VDI.$VBD | resolve).VM)"
|
||||
ng-init="VBD = (VDI.$VBD | resolve)"
|
||||
>
|
||||
{{(((VDI.$VBD | resolve).VM) | resolve).name_label}}
|
||||
<button type="button" ng-show="srDisks.$visible" ng-click="disconnectVBD(VBD.UUID)" class="btn btn-sm btn-danger pull-right" tooltip="Disconnect this disk"><i class="fa fa-unlink fa-lg"></i></button>
|
||||
</td>
|
||||
<td ng-if="!((VDI.$VBD | resolve).VM)">
|
||||
<button type="button" ng-show="srDisks.$visible" ng-click="deleteVDI(VDI.UUID)" class="btn btn-sm btn-danger pull-right" tooltip="Destroy this disk"><i class="fa fa-trash-o fa-lg"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="srDisks.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="srDisks.$waiting" ng-click="srDisks.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="submit" ng-disabled="srDisks.$waiting" class="btn btn-primary"><i class="fa fa-save"></i> Save</button>
|
||||
<button type="button" ng-disabled="srDisks.$waiting" ng-click="addVDI()" class="btn btn-success pull-right"><i class="fa fa-plus"></i> Add Disk</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /VDIs. -->
|
||||
|
||||
<!-- Hosts. -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="srHosts" onaftersave="saveTable()" oncancel="cancel()">
|
||||
<p class="flat-panel-title">
|
||||
<i class="fa fa-link" style="color: #e25440;"></i> Connected hosts
|
||||
<span class="quick-edit" tooltip="Edit connected hosts" ng-click="srHosts.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
<span class="quick-edit" tooltip="Reconnect all hosts" ng-click="reconnectAllHosts()">
|
||||
<i class="fa fa-plus-square fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<tr
|
||||
ng-repeat="PBD in SR.$PBDs | resolve"
|
||||
ng-init="host = (PBD.host | resolve)"
|
||||
xo-sref="hosts_view({id: host.UUID})"
|
||||
>
|
||||
<td>{{host.name_label}}</td>
|
||||
<td ng-if="PBD.attached">
|
||||
<span class="label label-success">Connected</span>
|
||||
<button type="button" ng-show="srHosts.$visible" ng-click="disconnectPBD(host.UUID)" class="btn btn-sm btn-danger pull-right" tooltip="Disconnect this host"><i class="fa fa-unlink fa-lg"></i></button>
|
||||
</td>
|
||||
<td ng-if="!PBD.attached">
|
||||
<span class="label label-default">Disabled</span>
|
||||
<button type="button" ng-show="srHosts.$visible" ng-click="connectPBD(host.UUID)" class="btn btn-sm btn-primary pull-right" tooltip="Connect this host"><i class="fa fa-link fa-lg"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-comments" style="color: #e25440;"></i> Logs</p>
|
||||
<p class="center" ng-if="!SR.messages.length">No recent logs</p>
|
||||
<table class="table table-hover" ng-if="SR.messages.length">
|
||||
<th class="col-md-1">Date</th>
|
||||
<th class="col-md-1">Name</th>
|
||||
<tr ng-repeat="message in SR.messages | resolve | orderBy:'-time' track by message.UUID">
|
||||
<td>{{message.time*1e3 | date:'medium'}}</td>
|
||||
<td>
|
||||
{{message.name}}
|
||||
<a class="quick-remove" tooltip="Remove log">
|
||||
<i class="fa fa-trash-o fa-fw"> </i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Hosts. -->
|
||||
@@ -1,349 +0,0 @@
|
||||
<!-- TODO: lots of stuff. -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="page-title"><i class="xo-icon-vm xo-color-{{VM.power_state | lowercase}}"></i> {{VM.name_label}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-cogs" style="color: #e25440;"></i> General
|
||||
<span class="quick-edit" tooltip="Edit General settings" ng-click="vmSettings.$show()">
|
||||
<i class="fa fa-edit fa-fw"></i>
|
||||
</span>
|
||||
</p>
|
||||
<div class="general-properties">
|
||||
<form editable-form name="vmSettings" onbeforesave="saveVM($data)">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name</dt>
|
||||
<dd>
|
||||
<span editable-text="VM.name_label" e-name="name_label" e-form="vmSettings">
|
||||
{{VM.name_label}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
<span editable-text="VM.name_description" e-name="name_description" e-form="vmSettings">
|
||||
{{VM.name_description}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt ng-if="VM.power_state == ('Running' || 'Paused')">Running on:</dt>
|
||||
<dd ng-repeat="container in [VM.$container] | resolve">
|
||||
<a xo-sref="hosts_view({id: container.UUID})">
|
||||
{{container.name_label}}
|
||||
</a>
|
||||
</dd>
|
||||
<dt>Address</dt>
|
||||
<dd ng-repeat="IP in VM.addresses">{{IP}}</dd>
|
||||
<dt>Tags</dt>
|
||||
<dd>
|
||||
<!-- TODO angular select2 in angular xeditable
|
||||
<input type="text" ui-select2="select2Options" ng-model="VM.tags" data-placeholder="tags">
|
||||
-->
|
||||
</dd>
|
||||
<dt>vCPUs</dt>
|
||||
<dd>
|
||||
<span editable-text="VM.CPUs.number" e-name="CPUs" e-form="vmSettings">
|
||||
{{VM.CPUs.number}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>RAM</dt>
|
||||
<dd>
|
||||
<span editable-text="memorySize" e-name="memory" e-form="vmSettings">
|
||||
{{memorySize}}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>UUID</dt>
|
||||
<dd>{{VM.UUID}}</dd>
|
||||
</dl>
|
||||
<div class="btn-form" ng-show="vmSettings.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="vmSettings.$waiting" ng-click="vmSettings.$cancel()" class="btn btn-default">
|
||||
<i class="fa fa-times"></i> Cancel
|
||||
</button>
|
||||
<button type="submit" ng-disabled="vmSettings.$waiting" class="btn btn-primary">
|
||||
<i class="fa fa-save"></i> Save
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-bar-chart-o" style="color: #e25440;"></i> Stats</p>
|
||||
<div class="grid">
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">vCPUs</p>
|
||||
<p class="center big">{{VM.CPUs.number}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">RAM</p>
|
||||
<p class="center big">{{VM.memory.size | bytesToSize}}</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Disks</p>
|
||||
<p class="center big">{{VM.$VBDs.length || 0}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="grid">
|
||||
<div ng-if="VM.os_version.distro" class="grid-cell">
|
||||
<p class="stat-name">OS:</p>
|
||||
<p class="center big">
|
||||
<i tooltip="{{VM.os_version.name}}" style="color: black;" class="xo-icon-{{osType(VM.os_version.distro)}}"></i>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid-cell">
|
||||
<p class="stat-name">Xen tools:</p>
|
||||
<p class="center">
|
||||
<span ng-if="VM.PV_drivers" style="color:green;">Installed</span>
|
||||
<span ng-if="!VM.PV_drivers">NOT installed</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<p class="flat-panel-title"><i class="fa fa-flash" style="color: #e25440;"></i> Actions</p>
|
||||
<div class="grid-cell text-center">
|
||||
<div class="grid">
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Stop VM" type="button" style="width: 90%" class="btn" xo-click="stopVM(VM.UUID)">
|
||||
<i class="fa fa-stop fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Start VM" type="button" style="width: 90%" class="btn" xo-click="startVM(VM.UUID)">
|
||||
<i class="fa fa-play fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Reboot VM" type="button" style="width: 90%" class="btn" xo-click="rebootVM(VM.UUID)">
|
||||
<i class="fa fa-refresh fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Migrate VM" type="button" style="width: 90%" class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-share fa-2x fa-fw"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu left" role="menu">
|
||||
<li ng-repeat="host in hosts | orderBy:natural('name_label') track by host.UUID">
|
||||
<a ng-click="migrateVM(VM.UUID, host.UUID)">
|
||||
<i class="xo-icon-host fa-fw"></i>
|
||||
To {{host.name_label}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Force Reboot" type="button" style="width: 90%" class="btn" xo-click="force_rebootVM(VM.UUID)">
|
||||
<i class="fa fa-flash fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Force Shutdown" type="button" style="width: 90%" class="btn" xo-click="force_stopVM(VM.UUID)">
|
||||
<i class="fa fa-power-off fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Delete VM" type="button" style="width: 90%" class="btn" xo-click="destroyVM(VM.UUID)">
|
||||
<i class="fa fa-trash-o fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group">
|
||||
<button tooltip="Create a snapshot" style="width: 90%" type="button" class="btn" xo-click="snapshotVM(VM.UUID,VM.name_label)">
|
||||
<i class="xo-icon-snapshot fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid-cell btn-group" style="margin-bottom: 0.5em">
|
||||
<button tooltip="VM Console" type="button" style="width: 90%" class="btn" xo-sref="consoles_view({id: VM.UUID})">
|
||||
<i class="xo-icon-console fa-2x fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Disk panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="vmDisks" onaftersave="saveTable()" oncancel="cancel()">
|
||||
<p class="flat-panel-title">
|
||||
<i class="xo-icon-sr" style="color: #e25440;"></i> Disk
|
||||
<span class="quick-edit" tooltip="Edit disks" ng-click="vmDisks.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Size</th>
|
||||
<th>SR</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
<tr
|
||||
ng-repeat="VDI in VDIs | orderBy:natural('name_label') track by VDI.UUID"
|
||||
ng-init="SR = (VDI.$SR | resolve); VBD = (VDI.$VBD | resolve)"
|
||||
xo-sref="SRs_view({id: SR.UUID})"
|
||||
>
|
||||
<td>
|
||||
<span editable-text="VDI.name_label" e-name="name_label" e-form="vmDisks" e-required>
|
||||
{{VDI.name_label}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span editable-text="VDI.name_description" e-name="name_description" e-form="vmDisks">
|
||||
{{VDI.name_description}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span editable-text="VDI.size | bytesToSize" e-name="size" e-form="vmDisks" e-required>
|
||||
{{VDI.size | bytesToSize}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Are SR editable? will trigger moving VDI to the new SR -->
|
||||
{{SR.name_label}}
|
||||
</td>
|
||||
<td ng-if="VBD.attached">
|
||||
<span editable-checkbox="VBD.attached" e-title="Connected?" class="label label-success" e-form="vmDisks" e-required>Connected</span>
|
||||
<button type="button" ng-show="vmDisks.$visible" ng-click="disconnectVBD(VBD.UUID)" class="btn btn-sm btn-danger pull-right"><i class="fa fa-unlink fa-lg" tooltip="Disconnect this disk"></i></button>
|
||||
</td>
|
||||
<td ng-if="!VBD.attached">
|
||||
<span editable-checkbox="VBD.attached" e-title="Connected?" class="label label-default" e-form="vmDisks" e-required>Disconnected</span>
|
||||
<button type="button" ng-show="vmDisks.$visible" ng-click="deleteVDI(VDI.UUID)" class="btn btn-sm btn-danger pull-right"><i class="fa fa-trash-o fa-lg" tooltip="Remove this disk"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="VDI in newVDIs | resolve | orderBy:natural('name_label') track by VDI.UUID">
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="vmDisks.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="vmDisks.$waiting" ng-click="vmDisks.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="submit" ng-disabled="vmDisks.$waiting" ng-click="saveChanges()" class="btn btn-primary"><i class="fa fa-save"></i> Save</button>
|
||||
<button type="button" ng-disabled="vmDisks.$waiting" ng-click="addVDI()" class="btn btn-success pull-right"><i class="fa fa-plus"></i> Add Disk</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- TODO: Interface panel -->
|
||||
<div class="grid">
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="vmInterfaces" onaftersave="saveTable()" oncancel="cancel()">
|
||||
<p class="flat-panel-title"><i class="xo-icon-network" style="color: #e25440;"></i> Interface
|
||||
<span class="quick-edit" tooltip="Edit interfaces" ng-click="vmInterfaces.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<table class="table table-hover">
|
||||
<th>Device</th>
|
||||
<th>MAC</th>
|
||||
<th>MTU</th>
|
||||
<th>Network</th>
|
||||
<th>Link status</th>
|
||||
<tr ng-repeat="VIF in VM.VIFs | resolve | orderBy:natural('name_label') track by VIF.UUID">
|
||||
<td>VIF #{{VIF.device}}</td>
|
||||
<td>
|
||||
{{VIF.MAC}}
|
||||
</td>
|
||||
<td>
|
||||
<span editable-text="VIF.MTU" e-name="mtu" e-form="vmInterfaces" e-required>
|
||||
{{VIF.MTU}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{{(VIF.$network | resolve).name_label}}
|
||||
</td>
|
||||
<td ng-if="VIF.attached">
|
||||
<span editable-checkbox="VIF.attached" class="label label-success">Connected</span>
|
||||
<button type="button" ng-show="vmInterfaces.$visible" ng-click="disconnectVIF(VIF.UUID)" class="btn btn-sm btn-danger pull-right"><i class="fa fa-ban fa-lg" tooltip="Disconnect this interface"></i></button>
|
||||
</td>
|
||||
<td ng-if="!VIF.attached">
|
||||
<span editable-checkbox="VIF.attached" class="label label-default">Disconnected</span>
|
||||
<button type="button" ng-show="vmInterfaces.$visible" ng-click="deleteVIF(VIF.UUID)" class="btn btn-sm btn-danger pull-right"><i class="fa fa-trash-o fa-lg" tooltip="Remove this interface"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="vmInterfaces.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="vmInterfaces.$waiting" ng-click="vmInterfaces.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="button" ng-disabled="vmInterfaces.$waiting" ng-click="saveChanges()" class="btn btn-primary"><i class="fa fa-save"></i> Save</button>
|
||||
<button type="button" ng-disabled="vmInterfaces.$waiting" ng-click="addNetwork()" class="btn btn-success pull-right"><i class="fa fa-plus"></i> Add Interface</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Snapshot and Logs panels -->
|
||||
<div class="grid">
|
||||
|
||||
<!-- Snap panel -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="vmSnap" oncancel="cancel()">
|
||||
<p class="flat-panel-title"><i class="xo-icon-snapshot" style="color: #e25440;"></i> Snapshots
|
||||
<span class="quick-edit" tooltip="Edit snapshots" ng-click="vmSnap.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<p class="center" ng-if="!VM.snapshots.length">No snapshots</p>
|
||||
<table class="table table-hover" ng-if="VM.snapshots.length">
|
||||
<th>Date</th>
|
||||
<th>Name</th>
|
||||
<tr ng-repeat="snapshot in VM.snapshots | resolve | orderBy:'-snapshot_time' track by snapshot.UUID">
|
||||
<td>{{snapshot.snapshot_time*1e3 | date:'medium'}}</td>
|
||||
<td>
|
||||
<span editable-text="snapshot.name_label" e-name="name_label" e-form="vmSnap" onbeforesave="saveSnapshot(snapshot.UUID, $data)" >
|
||||
{{snapshot.name_label}}
|
||||
</span>
|
||||
<span class="pull-right">
|
||||
<button type="button" ng-show="vmSnap.$visible" ng-click="deleteSnapshot(snapshot.UUID)" class="btn btn-sm btn-danger"><i class="fa fa-trash-o fa-lg" tooltip="Remove this snapshot"></i></button>
|
||||
<button type="button" ng-show="vmSnap.$visible" ng-click="revertSnapshot(snapshot.UUID)" class="btn btn-sm btn-danger"><i class="fa fa-undo fa-lg" tooltip="Revert VM to this snapshot"></i></button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="vmSnap.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="vmSnap.$waiting" ng-click="vmSnap.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="submit" ng-disabled="vmSnap.$waiting" ng-click="saveChanges()" class="btn btn-primary"><i class="fa fa-save"></i> Save</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Logs panel -->
|
||||
<div class="grid-cell flat-panel">
|
||||
<form editable-form name="vmLogs" onaftersave="saveTable()" oncancel="cancel()">
|
||||
<p class="flat-panel-title"><i class="fa fa-comments" style="color: #e25440;"></i> Logs
|
||||
<span class="quick-edit" tooltip="Edit logs" ng-click="vmLogs.$show()">
|
||||
<i class="fa fa-edit fa-fw"> </i>
|
||||
</span>
|
||||
</p>
|
||||
<p class="center" ng-if="!VM.messages.length">No recent logs</p>
|
||||
<table class="table table-hover" ng-if="VM.messages.length">
|
||||
<th>Date</th>
|
||||
<th>Name</th>
|
||||
<tr ng-repeat="message in VM.messages | resolve | orderBy:'-time' track by message.UUID">
|
||||
<td>{{message.time*1e3 | date:'medium'}}</td>
|
||||
<td>
|
||||
{{message.name}}
|
||||
<button type="button" ng-show="vmLogs.$visible" ng-click="deleteLog(message.UUID)" class="btn btn-sm btn-danger pull-right"><i class="fa fa-trash-o fa-lg" tooltip="Remove this log entry"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="btn-form" ng-show="vmLogs.$visible">
|
||||
<p class="center">
|
||||
<button type="button" ng-disabled="vmLogs.$waiting" ng-click="vmLogs.$cancel()" class="btn btn-default"><i class="fa fa-times"></i> Cancel</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,9 +4,9 @@
|
||||
"dependencies": {
|
||||
"angular": "1.2.13",
|
||||
"angular-animate": "1.2.13",
|
||||
"angular-bootstrap": "~0.11.0",
|
||||
"angular-cookies": "1.2.13",
|
||||
"angular-notify-toaster": "0.4.3",
|
||||
"angular-ui-bootstrap-bower": "0.10.0",
|
||||
"angular-ui-router": "0.2.8-bowratic-tedium",
|
||||
"angular-ui-select2": "0.0.5",
|
||||
"angular-ui-utils": "0.1.1",
|
||||
@@ -18,8 +18,5 @@
|
||||
"json3": "3.3.0",
|
||||
"noVNC": "https://github.com/kanaka/noVNC.git",
|
||||
"sass-bootstrap": "3.0.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "1.2.13"
|
||||
}
|
||||
}
|
||||
|
||||
89
doc/README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Documentation
|
||||
|
||||

|
||||
|
||||
## Introduction
|
||||
|
||||
Welcome on the Xen Orchestra documentation. This document has multiple purposes, it explains:
|
||||
- what is Xen Orchestra
|
||||
- how to install it
|
||||
- how to use it
|
||||
|
||||
## [Architecture](./architecture/README.md)
|
||||
|
||||
## [Installation](./installation/README.md)
|
||||
|
||||
## [Administration](./administration/README.md)
|
||||
|
||||
## [Layout](./layout/README.md)
|
||||
|
||||
## [VM Usage](./vm/README.md)
|
||||
|
||||
## [Recipes](./recipes/README.md)
|
||||
|
||||
<!--
|
||||
|
||||
This is what's left to do
|
||||
|
||||
### Hosts
|
||||
|
||||
#### Life-cycle
|
||||
|
||||
#### Memory Map
|
||||
|
||||
#### Edit host characteristics
|
||||
|
||||
#### Restart tool-stack
|
||||
|
||||
#### Remove from pool
|
||||
|
||||
#### Host console
|
||||
|
||||
#### Attached storage repository
|
||||
|
||||
#### Network (interface) management
|
||||
|
||||
#### Pending tasks
|
||||
|
||||
#### Logs
|
||||
|
||||
### Storage repositories
|
||||
|
||||
#### Edit SR characteristics
|
||||
|
||||
#### VDI Map
|
||||
|
||||
#### Virtual disks management
|
||||
|
||||
##### Rescan the repository
|
||||
|
||||
#### Connected hosts
|
||||
|
||||
#### Logs
|
||||
|
||||
### Pools
|
||||
|
||||
#### Edit pool characteristics
|
||||
|
||||
#### Hosts list
|
||||
|
||||
#### Shared SR list
|
||||
|
||||
#### Logs
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection between XO-server and XO-web
|
||||
|
||||
### Consoles
|
||||
|
||||
#### With HTTPS
|
||||
|
||||
#### Behind a NAT or a firewall
|
||||
|
||||
## How to contribute
|
||||
|
||||
### Report bugs
|
||||
|
||||
### Fork us!
|
||||
-->
|
||||
101
doc/administration/README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Administration
|
||||
|
||||
Once Xen Orchestra is installed, you can configure some parameters in the configuration file. Let's see how to do that.
|
||||
|
||||
## Configuration
|
||||
|
||||
The configuration file is in XO-server folder (for XOA users, it's in `/root/xo-server/config/local.yaml.dist`). If it's not already done, copy this file to `local.yaml` in the same folder. Now, you can edit the configuration safely (if you destroy it, you can reuse the dist file).
|
||||
|
||||
WARNING: YAML is very strict with indentation: use spaces for it, not tabs.
|
||||
|
||||
### User to run XO-server as
|
||||
|
||||
By default, XO-server is running with 'nobody' user and 'nogroup' group. You can change it by uncommenting these lines and choose whatever user/group you want:
|
||||
|
||||
```yaml
|
||||
user: 'nobody'
|
||||
group: 'nogroup'
|
||||
```
|
||||
|
||||
### HTTP listen address and port
|
||||
|
||||
By default, XO-server listens to all addresses (0.0.0.0) and runs on port 80. You can change this if you want in the `# Basic HTTP` section:
|
||||
|
||||
```yaml
|
||||
host: '0.0.0.0'
|
||||
port: 80
|
||||
```
|
||||
|
||||
### HTTPS
|
||||
|
||||
XO-server can also run in HTTPS (both HTTP and HTTPS can cohabit), just modify what's needed in the `# Basic HTTPS` section, this time with certificates/keys you want and their path:
|
||||
|
||||
```yaml
|
||||
host: '0.0.0.0'
|
||||
port: 443
|
||||
certificate: './certificate.pem'
|
||||
key: './key.pem'
|
||||
```
|
||||
|
||||
### Link to XO-web
|
||||
|
||||
On XOA, you shouldn't have to change this. On manual install, you need to link files served by XO-server for XO-web. That's the mount section. In this example, "xo-web" folder is in the same folder than "xo-server":
|
||||
|
||||
```yaml
|
||||
mounts:
|
||||
'/':
|
||||
- '../xo-web/dist/'
|
||||
```
|
||||
|
||||
### Redis server
|
||||
|
||||
By default, XO-server will try to contact Redis server on `localhost`, with the port `6379`. But you can define anything else you want:
|
||||
|
||||
```yaml
|
||||
uri: 'tcp://db:password@hostname:port'
|
||||
```
|
||||
|
||||
### Log file
|
||||
|
||||
On XOA, the log file for XO-server is in `/var/log/xo`: it has all of the server informations. Can be a real help when you have trouble.
|
||||
|
||||
## First connection
|
||||
|
||||
### Login screen
|
||||
|
||||
This is the login screen:
|
||||
|
||||

|
||||
|
||||
Note the green *check* icons: it indicates that you are correctly connected to XO-server. If you see this icon: , that's not good. Please check the Troubleshooting section if it's the case.
|
||||
|
||||
The default user login/password is `admin@admin.net` with `admin` password. This is what you should see after been logged:
|
||||
|
||||

|
||||
|
||||
You should change your password now.
|
||||
|
||||
## Users and passwords
|
||||
|
||||
You can access users ans servers management in the Setting view. It's accessible from the main menu:
|
||||
|
||||

|
||||
|
||||
From there, you can modify your current password, then Save:
|
||||
|
||||

|
||||
|
||||
You can add new users with limited rights. So far, **read** permission allow to see everything but not to interact with any objects. It's pretty basic for now, but [check how it will evolve soon](https://xen-orchestra.com/users-roles-in-xen-orchestra/) on our website.
|
||||
|
||||
## Add Xen hosts
|
||||
|
||||
Adding Xen hosts is in the same view (Settings) as users:
|
||||
|
||||

|
||||
|
||||
When you add a server, you just have to wait to be displayed (e.g: in the main view. Removing is less trivial, you need to restart XO-server (or it will not disappear).
|
||||
|
||||
|
||||
|
||||
|
||||
**Congrats! You've reached the end of this doc. See the next part, [about how the interface works](../layout/README.md).**
|
||||
BIN
doc/administration/assets/gosettings.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
doc/administration/assets/loginbad.png
Normal file
|
After Width: | Height: | Size: 776 B |
BIN
doc/administration/assets/loginok.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
doc/administration/assets/servers.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
doc/administration/assets/users.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
doc/administration/assets/welcome.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
60
doc/architecture/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Architecture
|
||||
|
||||
This part is dedicated to the architecture of Xen Orchestra. It will give you hints about how we build this software.
|
||||
|
||||
## Overview
|
||||
|
||||
Here is a diagram giving an overview of what is Xen Orchestra:
|
||||

|
||||
|
||||
Xen Orchestra is split in modules:
|
||||
- the core is "[xo-server](https://github.com/vatesfr/xo-server)", a daemon dealing directly with XenServer or XAPI capable hosts. This is where are stored users, and it's the center point for talking to your whole Xen infrastructure.
|
||||
- the Web interface is in "[xo-web](https://github.com/vatesfr/xo-web)": you are running it directly in your browser. The connection with "xo-server" is done via *WebSockets*
|
||||
- "[xo-cli](https://github.com/vatesfr/xo-cli)" is a new module allowing to send order directly in command line
|
||||
|
||||
We will use this modular architecture to add further parts later. It's completely flexible, allowing us to adapt Xen Orchestra in every existing work-flow.
|
||||
|
||||
## XO-server
|
||||
|
||||
XO-Server is the core of Xen Orchestra. It's central role opens a lot of possibilities versus other solutions. Let's see why.
|
||||
|
||||
### Daemon mode
|
||||
|
||||
As a daemon, XO-server is always up. In this way, it can listen and record every events occurring on your whole Xen infrastructure. Connections are always open and it can cache informations before serve it to another client (CLI, Web or anything else).
|
||||
|
||||
### Central point
|
||||
|
||||
Contrary to XenCenter, each Xen Orchestra's client is connected to one XO-Server, and not all the Xen servers. With a traditional architecture:
|
||||
|
||||

|
||||
|
||||
You can see how we avoid a lost of resources and bandwidth waste with a central point:
|
||||
|
||||

|
||||
|
||||
### Pluggable
|
||||
|
||||
It's really easy to plug other modules to XO-server, thus extend or adapt the solution to your needs (see XO-web and XO-cli for real examples).
|
||||
|
||||
### NodeJS under the hood
|
||||
|
||||
[NodeJS](https://en.wikipedia.org/wiki/Nodejs) is a software platform for scalable server-side and networking applications. It's famous for its efficiency, scalability and its asynchronous capabilities. Exactly what we need! Thus, XO-server is written in JavaScript.
|
||||
|
||||
## XO-web
|
||||
|
||||
This is probably the first thing you'll see of Xen Orchestra. It's the Web interface, allowing to interact with your virtual infrastructure. As a module for XO-web, it facilitates the everyday Xen administrator work, but also provide a solution to delegate some part of your infrastructure to other people.
|
||||
|
||||
### JavaScript
|
||||
|
||||
We are also using JavaScript for XO-web: we stay consistent from the back-end to the front-end with one main language. [AngularJS](https://en.wikipedia.org/wiki/Angularjs) and [Twitter Bootstrap](https://en.wikipedia.org/wiki/Bootstrap_%28front-end_framework%29) are also powerful allies to our everyday development.
|
||||
|
||||
## XO-cli
|
||||
|
||||
After [a request from someone on our Github repositoryy](https://github.com/vatesfr/xo-server/issues/23), we decided to add the possibility to do some actions with CLI. Just few hours after the request, [we created XO-cli](https://github.com/vatesfr/xo-cli). It's a real example of the Xen Orchestra modularity, and how we can be flexible.
|
||||
|
||||
## Other modules
|
||||
|
||||
In our vision, we think Xen Orchestra can be more than "just an Web GUI for Xen". With modularity, we can imagine a lot of things: because XO-server is a daemon, it can record any activity and thus create performance reports. The next step is auto-migrate VM for adapting the need to the demand (reducing the costs to only what you need). Everything is possible!
|
||||
|
||||
|
||||
**Congrats! You've reached the end of this doc. See the next part, [about how to install Xen Orchestra](../installation/README.md).**
|
||||
BIN
doc/architecture/assets/with-xo.jpg
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
doc/architecture/assets/without-xo.jpg
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
doc/architecture/assets/xo-arch.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
doc/assets/xo.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
166
doc/installation/README.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Installation
|
||||
|
||||
There is two ways to install Xen Orchestra. If you are just a user and not a developer, please consider using the easier way (XOA).
|
||||
|
||||
## Default credentials
|
||||
|
||||
Be advised that our default user and password for a fresh install are **admin@admin.net** and **admin**. Do not forget to change it to avoid troubles.
|
||||
|
||||
## Xen Orchestra Appliance (XOA)
|
||||
|
||||
The fastest way to install Xen Orchestra is to use our Appliance. You can [download it from here](https://xen-orchestra.com/install-and-update-xo-from-git/). Basically, it's a Debian VM with all the stuff needed to run Xen Orchestra and a update script. No more, no less.
|
||||
|
||||
Once you got it, you can import it with `xe vm-import filename=xoa_version_number.xva` or via XenCenter.
|
||||
|
||||
After the VM is imported, you just need to start it with a `xe vm-start vm=XOA` or with XenCenter.
|
||||
|
||||
XOA is in **DHCP** by default, so if you need to configure the IP, you need to edit `/etc/network/interfaces` as explain in the [Debian documentation](https://wiki.debian.org/NetworkConfiguration#Configuring_the_interface_manually). You can access the VM console through XenCenter or directly on your Xen hosts the `xe console vm=XOA` command.
|
||||
|
||||
Xen Orchestra is now accessible in your browser on ` http://your-vm-ip` or in HTTPS on the same URL.
|
||||
|
||||
### XOA credentials
|
||||
|
||||
So far, system/SSH user and password are **root**/**root**. Be smart, change the root password as soon as possible!
|
||||
|
||||
### Restart and update process in XOA
|
||||
|
||||
You can restart XOA by going in XOA on SSH (or console) and type `service xo restart`. If it fails, try it twice.
|
||||
|
||||
You can also update XOA with latest version with the integrated update script. This time, a `service xo update` do the job.
|
||||
|
||||
## Manual installation
|
||||
|
||||
This installation is validated on a fresh Debian 7 (Wheezy) 64 bits. It should be almost the same on others dpkg systems. For RPMs based OS, it should be close, because most of our dependencies came from NPM and not the OS itself.
|
||||
|
||||
As you may have seen, XO is composed of two parts: [XO-Server](https://github.com/vatesfr/xo-server/) and [XO-Web](https://github.com/vatesfr/xo-web/). They can be installed separately, even on different machines, but for the sake of simplicity we will set them up together.
|
||||
|
||||
## Packages and Pre-requisites
|
||||
|
||||
### NodeJS
|
||||
|
||||
XO needs Node.js. You can install it:
|
||||
- by [following this procedure](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager).
|
||||
- on Wheezy, the build from source was tested and working well.
|
||||
- by using *n*, documented just below.
|
||||
|
||||
We'll use `n` because it's powerful and flexible. First, you need `wget` and `curl`. Then, install it as root:
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/visionmedia/n/master/bin/n -O /usr/local/bin/n
|
||||
chmod +x /usr/local/bin/n
|
||||
n stable
|
||||
```
|
||||
We'll consider at this point that you've got a working node on your box. E.g:
|
||||
|
||||
```
|
||||
$ node -v
|
||||
v0.10.30
|
||||
```
|
||||
|
||||
### Packages
|
||||
|
||||
```
|
||||
apt-get install build-essential redis-server libpng-dev ruby git
|
||||
```
|
||||
|
||||
We also need compass in Ruby (we want to [remove this dependency as soon as possible](https://github.com/vatesfr/xo-web/issues/44))
|
||||
|
||||
```
|
||||
gem install compass
|
||||
```
|
||||
|
||||
## Fetching the Code
|
||||
|
||||
You may either download them [here](https://github.com/vatesfr/xo-server/archive/master.zip) and [here](https://github.com/vatesfr/xo-web/archive/master.zip) or use `git` with these repositories from `http://github.com/vatesfr/xo-server` and `http://github.com/vatesfr/xo-web`:
|
||||
|
||||
```
|
||||
git clone http://github.com/vatesfr/xo-server
|
||||
git clone http://github.com/vatesfr/xo-web
|
||||
```
|
||||
|
||||
## Installing dependencies
|
||||
|
||||
### XO-Server
|
||||
|
||||
Once you have it, you can use `npm` to install the other dependencies: go into XO-Server directory and launch the following command:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
Then, you have to create a config file for XO-Server:
|
||||
|
||||
```
|
||||
cp config/local.yaml.dist config/local.yaml
|
||||
```
|
||||
|
||||
Edit it to have the right path to deliver XO-Web, because XO-Server embeds an HTTP server (we assume that XO-Server and XO-Web are on the same directory). It's near the end of the file:
|
||||
|
||||
```yaml
|
||||
mounts:
|
||||
'/':
|
||||
- '../xo-web/dist/'
|
||||
```
|
||||
WARNING: YAML is very strict with indentation: use spaces for it, not tabs.
|
||||
|
||||
In this config file, you can also change default ports (80 and 443) for XO-Server.
|
||||
|
||||
You can try to start XO-Server to see if it works. You should have something like that:
|
||||
|
||||
```
|
||||
$ ./xo-server
|
||||
WebServer listening on 0.0.0.0:80
|
||||
[INFO] Default user: "admin@admin.net" with password "admin"
|
||||
```
|
||||
|
||||
### XO-Web
|
||||
|
||||
First, we'll also install dependencies:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
You can now install `bower` dependencies and build the application:
|
||||
|
||||
```
|
||||
./gulp --production
|
||||
```
|
||||
|
||||
## Running XO
|
||||
|
||||
The sole part you have to launch is XO-Server which is quite easy to do, just launch the `xo-server` script, which is in the root of XO-Server's directory':
|
||||
|
||||
```
|
||||
$ ./xo-server
|
||||
```
|
||||
That's it! Go on your browser to the XO-Server IP address, and it works :)
|
||||
|
||||
## Misc
|
||||
|
||||
- You can also consider using [forever](https://github.com/nodejitsu/forever) to have always the process running.
|
||||
|
||||
```
|
||||
npm install -g forever
|
||||
forever start -c ./node_modules/.bin/coffee src/main.coffee
|
||||
```
|
||||
|
||||
- Our stable branch is "master" and the beta branch is "next-release". You can change it if you want to test our latest features (on both XO-Server and XO-Web, do NOT mix them):
|
||||
|
||||
```
|
||||
git checkout next-release
|
||||
```
|
||||
- If you want to update your current version, do this on both repositories:
|
||||
|
||||
```
|
||||
git pull --ff-only
|
||||
npm install
|
||||
```
|
||||
|
||||
And this in XO-Web:
|
||||
|
||||
```
|
||||
./gulp --production
|
||||
```
|
||||
|
||||
**Congrats! You've reached the end of this doc. See the next part, [about administrate Xen Orchestra](../administration/README.md).**
|
||||
62
doc/layout/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Layout
|
||||
|
||||
In this section, we'll make a tour of the global layout of XO-web.
|
||||
|
||||
## Navigation bar
|
||||
|
||||
The navigation bar, on the top, is displayed on every views/pages. You can go to the main view by clicking on "Xen Orchestra", use the search bar, access to the main menu and disconnect your session from there.
|
||||
|
||||

|
||||
|
||||
### Main menu
|
||||
|
||||
It's represented by the . It gives you access to the main zones of XO-web.
|
||||
|
||||

|
||||
|
||||
### Search bar
|
||||
|
||||
The search bar is very useful to filter information, to quickly find what you need. The live filter works with every data on every object. Check the result in the Flat view section.
|
||||
|
||||
## Main view
|
||||
|
||||
The main view is a global view of all your pools, servers and VMs. The concept is to display all the important information with an horizontal hierarchy.
|
||||
|
||||
### Extended navigation bar
|
||||
|
||||
The first change is an extended navigation bar, with statistics and the "master checkbox":
|
||||
|
||||

|
||||
|
||||
This master checkbox can be use to select multiple VM in one action: all VM running, halted, or VM on one host.
|
||||
|
||||
The bar change when you select a VM with its checkbox (or *via* the master checkbox):
|
||||
|
||||

|
||||
|
||||
From this bar, you've got also Migrate VM display.
|
||||
|
||||
### Quick actions
|
||||
|
||||
We hovering a VM, you can see buttons for stop, start, reboot VM, and also access its console:
|
||||
|
||||

|
||||
|
||||
### Host and pools submenu
|
||||
|
||||
Don't forget to expand Pool and hosts submenus to explore what you can do:
|
||||
|
||||

|
||||
|
||||
## Flat view
|
||||
|
||||
Flat view is non hierarchical view, with the main goal to serve the filtered results of the search field.
|
||||
|
||||
Here is a example with an IP address:
|
||||
|
||||

|
||||
|
||||
It also work for names, descriptions, everything else!
|
||||
|
||||
|
||||
**Congrats! You've reached the end of this doc. See the next part, [about how to manage VMs](../vm/README.md).**
|
||||
BIN
doc/layout/assets/filter.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
doc/layout/assets/iconmain.png
Normal file
|
After Width: | Height: | Size: 197 B |
BIN
doc/layout/assets/navbar.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
doc/layout/assets/navbarhome.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
doc/layout/assets/navbarselected.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
doc/layout/assets/quickactions.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
doc/layout/assets/submenus.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
5
doc/recipes/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Recipes
|
||||
|
||||
The place to find how to do cool things with Xen Orchestra :)
|
||||
|
||||
- [Xen Orchestra behind an Apache reverse proxy](apache-reverse.md) (thanks to *schn0052* [on our forum](https://xen-orchestra.com/forum/93-apache-reverse-proxy)!)
|
||||
14
doc/recipes/apache-reverse.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Xen Orchestra behind an Apache reverse proxy
|
||||
|
||||
As XO-web and XO-server communicates with *WebSockets*, you need to have the `mod_proxy_tunnel` in Apache (please [check the Apache documentation](http://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html) about it). It's available for Apache 2.4.5 and later.
|
||||
|
||||
|
||||
Please use this configuration in this order or it will not work:
|
||||
|
||||
```
|
||||
ProxyPass /api/ ws://x.x.x.x/api/
|
||||
ProxyPassReverse /api/ ws://x.x.x.x/api/
|
||||
|
||||
ProxyPass / http://x.x.x.x/
|
||||
ProxyPassReverse / http://x.x.x.x/
|
||||
```
|
||||
153
doc/vm/README.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Virtual machines
|
||||
|
||||
VM are the core interest of virtualization. That's why they are first in this documentation. The VM view contains all you need to interact with a virtual machine:
|
||||
|
||||

|
||||
|
||||
## Life-cycle (stop, start, reboot)
|
||||
|
||||
This is trivial, it can be done in the main view:
|
||||
|
||||

|
||||
|
||||
But also in the VM view, in the **Actions** panel:
|
||||
|
||||

|
||||
|
||||
## Live migration
|
||||
|
||||
Live migration allows to move your VM from an host to another without interrupting service. You can do it in the main view, by checking the needed VM, and choose the Migrate button in the extended navigation bar:
|
||||
|
||||

|
||||
|
||||
Or in the VM view in the **Actions** panel:
|
||||
|
||||

|
||||
|
||||
## Console
|
||||
|
||||
Xen Orchestra allows console access from the web. For that, we use the NoVNC project. Check the troubleshooting section if you have any problem for display it (it relies on a direct network connection, but it will be fixed soon).
|
||||
|
||||
Consoles are accessible from different views, but always with the same symbol: 
|
||||
|
||||
You can send *Ctrl+Alt+Del* key with the dedicated button. And you can also insert a virtual CD (from an ISO repository or a local DVD drive) in any VM you need:
|
||||
|
||||

|
||||
|
||||
## Edit VM characteristics
|
||||
|
||||
On the VM view, you can edit any characteristics using the edit icon: 
|
||||
|
||||
E.g with **General** panel:
|
||||
|
||||

|
||||
|
||||
## Create a VM
|
||||
|
||||
You can create a VM from the pool submenu or the host submenu:
|
||||
|
||||

|
||||
|
||||
Then, here you go, on the create vm view:
|
||||
|
||||

|
||||
|
||||
Fill the required fields:
|
||||
- choose a template
|
||||
- pick a name
|
||||
- choose install settings
|
||||
- choose an interface and a Network
|
||||
- you can modify disk size and add disks if you want
|
||||
|
||||
The summary is here to check if you are sure about your settings. Let's create a VM by clicking on "Create VM" button!
|
||||
|
||||
**WARNING**: if you create a VM from a special template (a previously existing VM converted in a template), you should remove all interfaces and disks! Because they are already existing in the template.
|
||||
|
||||
## Copy/clone a VM
|
||||
|
||||
Copying (or cloning, same thing) a VM allows you to "fork" an existing VM. You have 2 choices:
|
||||
- Fast clone: it uses a snapshot on your storage back-end. Pro: it's very fast to create a fast clone. Cons: snapshots are less suitable for a long-term usage (important performance drawback).
|
||||
- Full disk copy: it creates a new disk and copy the whole content of the VM in it. Pro: it's very reliable, and there is no link between this disk and its parent. Cons: disk creation is longer.
|
||||
|
||||

|
||||
|
||||
When to use *Fast clone*:
|
||||
- create a temporary VM, e.g for development or test purpose
|
||||
- disk performance is not needed
|
||||
- you storage system is occupied at less than 80% (performance impact is very high after this average limit)
|
||||
|
||||
For production use, please consider *Full disk copy*.
|
||||
|
||||
## Create a template
|
||||
|
||||
When you create a template from an existing VM, it will **convert** your current VM to a template. **There is no turning back!**
|
||||
|
||||
After the conversion, you can create a new VM, and see in the template list, a new template with the name of the converted VM. The VM creation process is slightly different from a "classical" template because:
|
||||
- your converted VM contains already a network interface
|
||||
- and a disk
|
||||
|
||||
Thus, you don't need to create both of them (remove the network interface and let disks section empty).
|
||||
|
||||
This is very powerful for creating the same VM basis for a type of usage (e.g: a development stack pre-configured in the templated VM).
|
||||
|
||||
You can use this feature in the VM view, in the "Actions panel", using this icon: 
|
||||
|
||||
## Delete
|
||||
|
||||
Remove a VM can be done from the main view but also the VM view.
|
||||
|
||||
In the main view, you need to check the VM you want to delete (can be multiple VM), then in the "More" menu, select the Delete action:
|
||||
|
||||

|
||||
|
||||
It will ask you if you are sure to delete the VM, and also the possibility to delete its associated disks:
|
||||
|
||||

|
||||
|
||||
In the VM view, the process is the same with the trash icon: 
|
||||
|
||||
## Snapshots management
|
||||
|
||||
Snapshotting is a very powerful feature. It allows the possibility to quickly save a VM state (e.g: for backups) but also to rollback to a previous state if needed.
|
||||
|
||||
The classical example is to create a snapshot before a VM OS update, check if it works, and revert/rollback if not. As snapshots are really fast, you can avoid a too long service interruption.
|
||||
|
||||
You can create a snapshot from the main view, with the "More" menu on each VM selected (can be multiples VM at one time):
|
||||
|
||||

|
||||
|
||||
Or in the VM view with the snapshot icon: 
|
||||
|
||||
Created snapshots are visible in the VM view in the "Snapshots" panel:
|
||||
|
||||

|
||||
|
||||
As you can see, the snapshot name is auto-generated (with the snapshot date). You can change the name, revert of delete snapshot by editing the panel:
|
||||
|
||||

|
||||
|
||||
Snapshots operations are possible without power state distinction.
|
||||
|
||||
<!--
|
||||
|
||||
## Disk management
|
||||
|
||||
## Network (interface) management
|
||||
|
||||
-->
|
||||
|
||||
## Logs
|
||||
|
||||
Logs are XAPI events, like when a VM is started or shutdown:
|
||||
|
||||

|
||||
|
||||
You can remove a log by editing the "Logs" panel and use this icon: 
|
||||
|
||||
## Group actions
|
||||
|
||||
You can make "group actions" in the main view, e.g migrate a bunch of VM in another host. For that, you have to use the check box for each VM you want or use the "master checkbox" for select VM in the same host or power state:
|
||||
|
||||

|
||||
|
||||
The, choose the button you need in the [extended navigation bar](#extended-navigation-bar).
|
||||
BIN
doc/vm/assets/actionbar.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
doc/vm/assets/clone.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
doc/vm/assets/confirmdeletevm.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
doc/vm/assets/consoleicon.png
Normal file
|
After Width: | Height: | Size: 341 B |
BIN
doc/vm/assets/consoleview.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
doc/vm/assets/createvm.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
doc/vm/assets/edit.png
Normal file
|
After Width: | Height: | Size: 718 B |
BIN
doc/vm/assets/edit_snap.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
doc/vm/assets/editpanel.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
doc/vm/assets/log.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
doc/vm/assets/mastercheckbox.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |