1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-03-21 18:14:51 +00:00

Merge pull request #3 from retspen/master

add
This commit is contained in:
aiminick 2021-04-12 02:10:40 +08:00 committed by GitHub
commit cd798eb9b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
94 changed files with 1874 additions and 1697 deletions

View file

@ -22,7 +22,7 @@
How to update <code>gstfsd</code> daemon on hypervisor:
```bash
wget -O - https://clck.ru/9VMRH | sudo tee -a /usr/local/bin/gstfsd
wget -O - https://bit.ly/2NAaWXG | sudo tee -a /usr/local/bin/gstfsd
sudo service supervisor restart
```
@ -86,7 +86,7 @@ sudo service supervisor restart
Setup libvirt and KVM on server
```bash
wget -O - https://clck.ru/9V9fH | sudo sh
wget -O - https://bit.ly/36baWUu | sudo sh
```
Done!!

View file

@ -4,8 +4,10 @@ from instances.models import Instance
def refresh_instance_database(compute):
domains = compute.proxy.wvm.listAllDomains()
domain_names = [d.name() for d in domains]
domain_uuids = [d.UUIDString() for d in domains]
# Delete instances that're not on host from DB
Instance.objects.filter(compute=compute).exclude(name__in=domain_names).delete()
Instance.objects.filter(compute=compute).exclude(uuid__in=domain_uuids).delete()
# Create instances that're on host but not in DB
names = Instance.objects.filter(compute=compute).values_list('name', flat=True)
for domain in domains:

View file

@ -1,14 +1,13 @@
Django==2.2.17
Django==2.2.19
django-bootstrap4==2.3.1
django-icons==2.2.0
django-icons==2.2.1
django-login-required-middleware==0.5.0
django-otp==1.0.2
django-qr-code==1.3.1
gunicorn==20.0.4
importlib-metadata==2.0.0
libsass==0.20.1
libvirt-python==6.9.0
lxml==4.6.1
libvirt-python==7.1.0
lxml==4.6.3
qrcode==6.1
rwlock==0.0.7
websockify==0.9.0

View file

@ -1,5 +1,5 @@
[program:gstfsd]
command=/srv/webvirtcloud/venv/bin/python3 /usr/local/bin/gstfsd
command=/usr/bin/python3 /usr/local/bin/gstfsd
directory=/usr/local/bin
user=root
autostart=true

View file

@ -45,7 +45,7 @@ echowarn() {
# DESCRIPTION: Echo debug information to stdout.
#-------------------------------------------------------------------------------
echodebug() {
if [ "$_ECHO_DEBUG" -eq "$BS_TRUE" ]; then
if [ $_ECHO_DEBUG -eq $BS_TRUE ]; then
printf "${BC} * DEBUG${EC}: %s\n" "$@";
fi
}
@ -342,7 +342,7 @@ __check_end_of_life_versions() {
;;
centos)
# CentOS versions lower than 5 are no longer supported
# CentOS versions lower than 6 are no longer supported
if { [ "$DISTRO_MAJOR_VERSION" -eq 6 ] && [ "$DISTRO_MINOR_VERSION" -lt 3 ]; } || [ "$DISTRO_MAJOR_VERSION" -lt 5 ]; then
echoerror "End of life distributions are not supported."
echoerror "Please consider upgrading to the next stable. See:"
@ -374,26 +374,32 @@ __check_end_of_life_versions
# CentOS Install Functions
#
install_centos() {
if [ "$DISTRO_MAJOR_VERSION" -ge 6 ]; then
yum -y install qemu-kvm libvirt bridge-utils python-libguestfs libguestfs-tools supervisor cyrus-sasl-md5 epel-release || return 1
yum -y install epel-release || return 1
if [ "$DISTRO_MAJOR_VERSION" -lt 8 ]; then
yum -y install qemu-kvm libvirt bridge-utils python-libguestfs libguestfs-tools supervisor cyrus-sasl-md5 || return 1
else
yum -y install qemu-kvm libvirt python3-libguestfs libguestfs-tools cyrus-sasl-md5 supervisor || return 1
fi
return 0
}
install_centos_post() {
if [ -f /etc/sysconfig/libvirtd ]; then
sed -i 's/#LIBVIRTD_ARGS/LIBVIRTD_ARGS/g' /etc/sysconfig/libvirtd
else
echoerror "/etc/sysconfig/libvirtd not found. Exiting..."
exit 1
fi
if [ -f /etc/libvirt/libvirtd.conf ]; then
sed -i 's/#listen_tls/listen_tls/g' /etc/libvirt/libvirtd.conf
sed -i 's/#listen_tcp/listen_tcp/g' /etc/libvirt/libvirtd.conf
sed -i 's/#auth_tcp/auth_tcp/g' /etc/libvirt/libvirtd.conf
else
echoerror "/etc/libvirt/libvirtd.conf not found. Exiting..."
exit 1
if [ "$DISTRO_MAJOR_VERSION" -lt 8 ]; then
if [ -f /etc/sysconfig/libvirtd ]; then
sed -i 's/#LIBVIRTD_ARGS/LIBVIRTD_ARGS/g' /etc/sysconfig/libvirtd
else
echoerror "/etc/sysconfig/libvirtd not found. Exiting..."
exit 1
fi
if [ -f /etc/libvirt/libvirtd.conf ]; then
sed -i 's/#listen_tls/listen_tls/g' /etc/libvirt/libvirtd.conf
sed -i 's/#listen_tcp/listen_tcp/g' /etc/libvirt/libvirtd.conf
sed -i 's/#auth_tcp/auth_tcp/g' /etc/libvirt/libvirtd.conf
else
echoerror "/etc/libvirt/libvirtd.conf not found. Exiting..."
exit 1
fi
fi
if [ -f /etc/libvirt/qemu.conf ]; then
sed -i 's/#[ ]*vnc_listen.*/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf
@ -440,14 +446,24 @@ daemons_running_centos() {
service libvirt-guests stop > /dev/null 2>&1
service libvirt-guests start
fi
if [ -f /usr/lib/systemd/system/libvirtd.service ]; then
systemctl stop libvirtd.service > /dev/null 2>&1
systemctl start libvirtd.service
if [ "$DISTRO_MAJOR_VERSION" -lt 8 ]; then
if [ -f /usr/lib/systemd/system/libvirtd.service ]; then
systemctl stop libvirtd.service > /dev/null 2>&1
systemctl start libvirtd.service
fi
else
if [ -f /usr/lib/systemd/system/libvirtd-tcp.socket ]; then
systemctl stop libvirtd-tcp.socket > /dev/null 2>&1
systemctl start libvirtd-tcp.socket
fi
fi
if [ -f /usr/lib/systemd/system/libvirt-guests.service ]; then
systemctl stop libvirt-guests.service > /dev/null 2>&1
systemctl start libvirt-guests.service
fi
if [ -f /etc/init.d/supervisord ]; then
service supervisord stop > /dev/null 2>&1
service supervisord start
@ -599,7 +615,6 @@ install_ubuntu() {
apt install -y qemu-kvm libvirt-bin bridge-utils virt-manager sasl2-bin python3-guestfs supervisor || return 1
fi
return 0
}

View file

@ -1,7 +1,7 @@
-r ../conf/requirements.txt
coverage==5.3
django-debug-toolbar==3.1.1
pycodestyle==2.6.0
pyflakes==2.2.0
pylint==2.6.0
yapf==0.30.0
coverage==5.5
django-debug-toolbar==3.2
pycodestyle==2.7.0
pyflakes==2.3.1
pylint==2.7.2
yapf==0.31.0

View file

@ -10,14 +10,12 @@
}
.breadcrumb-item {
display: flex;
// The separator between breadcrumbs (by default, a forward-slash: "/")
+ .breadcrumb-item {
padding-left: $breadcrumb-item-padding;
&::before {
display: inline-block; // Suppress underlining of the separator in modern browsers
float: left; // Suppress inline spacings and underlining of the separator
padding-right: $breadcrumb-item-padding;
color: $breadcrumb-divider-color;
content: escape-svg($breadcrumb-divider);

View file

@ -127,7 +127,7 @@
display: inline-block;
width: $carousel-control-icon-width;
height: $carousel-control-icon-width;
background: no-repeat 50% / 100% 100%;
background: 50% / 100% 100% no-repeat;
}
.carousel-control-prev-icon {
background-image: escape-svg($carousel-control-prev-icon-bg);

View file

@ -104,7 +104,7 @@
width: $custom-control-indicator-size;
height: $custom-control-indicator-size;
content: "";
background: no-repeat 50% / #{$custom-control-indicator-bg-size};
background: 50% / #{$custom-control-indicator-bg-size} no-repeat;
}
}
@ -315,6 +315,7 @@
width: 100%;
height: $custom-file-height;
margin: 0;
overflow: hidden;
opacity: 0;
&:focus ~ .custom-file-label {
@ -347,6 +348,7 @@
z-index: 1;
height: $custom-file-height;
padding: $custom-file-padding-y $custom-file-padding-x;
overflow: hidden;
font-family: $custom-file-font-family;
font-weight: $custom-file-font-weight;
line-height: $custom-file-line-height;
@ -388,7 +390,7 @@
appearance: none;
&:focus {
outline: none;
outline: 0;
// Pseudo-elements must be split across multiple rulesets to have an effect.
// No box-shadow() mixin for focus accessibility.

View file

@ -100,7 +100,7 @@
}
}
// When enabled Popper.js, reset basic dropdown position
// When Popper is enabled, reset the basic dropdown position
// stylelint-disable-next-line no-duplicate-selectors
.dropdown-menu {
&[x-placement^="top"],

View file

@ -42,7 +42,6 @@
> .form-control,
> .custom-select {
&:not(:last-child) { @include border-right-radius(0); }
&:not(:first-child) { @include border-left-radius(0); }
}
@ -53,9 +52,24 @@
align-items: center;
&:not(:last-child) .custom-file-label,
&:not(:last-child) .custom-file-label::after { @include border-right-radius(0); }
&:not(:first-child) .custom-file-label { @include border-left-radius(0); }
}
&:not(.has-validation) {
> .form-control:not(:last-child),
> .custom-select:not(:last-child),
> .custom-file:not(:last-child) .custom-file-label::after {
@include border-right-radius(0);
}
}
&.has-validation {
> .form-control:nth-last-child(n + 3),
> .custom-select:nth-last-child(n + 3),
> .custom-file:nth-last-child(n + 3) .custom-file-label::after {
@include border-right-radius(0);
}
}
}
@ -175,8 +189,10 @@
.input-group > .input-group-prepend > .btn,
.input-group > .input-group-prepend > .input-group-text,
.input-group > .input-group-append:not(:last-child) > .btn,
.input-group > .input-group-append:not(:last-child) > .input-group-text,
.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .btn,
.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .input-group-text,
.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .btn,
.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .input-group-text,
.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
@include border-right-radius(0);

View file

@ -35,11 +35,8 @@
.nav-tabs {
border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
.nav-item {
margin-bottom: -$nav-tabs-border-width;
}
.nav-link {
margin-bottom: -$nav-tabs-border-width;
border: $nav-tabs-border-width solid transparent;
@include border-top-radius($nav-tabs-border-radius);

View file

@ -136,8 +136,12 @@
height: 1.5em;
vertical-align: middle;
content: "";
background: no-repeat center center;
background-size: 100% 100%;
background: 50% / 100% 100% no-repeat;
}
.navbar-nav-scroll {
max-height: $navbar-nav-scroll-max-height;
overflow-y: auto;
}
// Generate series of `.navbar-expand-*` responsive classes for configuring
@ -199,6 +203,10 @@
}
}
.navbar-nav-scroll {
overflow: visible;
}
.navbar-collapse {
display: flex !important; // stylelint-disable-line declaration-no-important

View file

@ -66,9 +66,9 @@
//
.pagination-lg {
@include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $line-height-lg, $border-radius-lg);
@include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $line-height-lg, $pagination-border-radius-lg);
}
.pagination-sm {
@include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $line-height-sm, $border-radius-sm);
@include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $line-height-sm, $pagination-border-radius-sm);
}

View file

@ -36,7 +36,7 @@
@if $enable-transitions {
.progress-bar-animated {
animation: progress-bar-stripes $progress-bar-animation-timing;
animation: $progress-bar-animation-timing progress-bar-stripes;
@if $enable-prefers-reduced-motion-media-query {
@media (prefers-reduced-motion: reduce) {

View file

@ -1,4 +1,4 @@
// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
// Reboot
//
@ -307,13 +307,13 @@ button {
border-radius: 0;
}
// Work around a Firefox/IE bug where the transparent `button` background
// results in a loss of the default `button` focus styles.
//
// Credit: https://github.com/suitcss/base/
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
// Explicitly remove focus outline in Chromium when it shouldn't be
// visible (e.g. as result of mouse click or touch tap). It already
// should be doing this automatically, but seems to currently be
// confused and applies its very visible two-tone outline anyway.
button:focus:not(:focus-visible) {
outline: 0;
}
input,

View file

@ -1,4 +1,3 @@
// Do not forget to update getting-started/theming.md!
:root {
// Custom variable values only support SassScript inside `#{}`.
@each $color, $value in $colors {

View file

@ -15,7 +15,7 @@
border-right-color: transparent;
// stylelint-disable-next-line property-disallowed-list
border-radius: 50%;
animation: spinner-border .75s linear infinite;
animation: .75s linear infinite spinner-border;
}
.spinner-border-sm {
@ -47,10 +47,19 @@
// stylelint-disable-next-line property-disallowed-list
border-radius: 50%;
opacity: 0;
animation: spinner-grow .75s linear infinite;
animation: .75s linear infinite spinner-grow;
}
.spinner-grow-sm {
width: $spinner-width-sm;
height: $spinner-height-sm;
}
@if $enable-prefers-reduced-motion-media-query {
@media (prefers-reduced-motion: reduce) {
.spinner-border,
.spinner-grow {
animation-duration: 1.5s;
}
}
}

View file

@ -1,4 +1,4 @@
// stylelint-disable declaration-no-important, selector-list-comma-newline-after
// stylelint-disable selector-list-comma-newline-after
//
// Headings

View file

@ -274,7 +274,7 @@ $embed-responsive-aspect-ratios: join(
// Font, line-height, and color for body text, headings, and more.
// stylelint-disable value-keyword-case
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
$font-family-base: $font-family-sans-serif !default;
// stylelint-enable value-keyword-case
@ -583,7 +583,7 @@ $custom-select-disabled-bg: $gray-200 !default;
$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions
$custom-select-indicator-color: $gray-800 !default;
$custom-select-indicator: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'><path fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/></svg>") !default;
$custom-select-background: escape-svg($custom-select-indicator) no-repeat right $custom-select-padding-x center / $custom-select-bg-size !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon)
$custom-select-background: escape-svg($custom-select-indicator) right $custom-select-padding-x center / $custom-select-bg-size no-repeat !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon)
$custom-select-feedback-icon-padding-right: add(1em * .75, (2 * $custom-select-padding-y * .75) + $custom-select-padding-x + $custom-select-indicator-padding) !default;
$custom-select-feedback-icon-position: center right ($custom-select-padding-x + $custom-select-indicator-padding) !default;
@ -694,7 +694,7 @@ $zindex-tooltip: 1070 !default;
// Navs
$nav-link-padding-y: .5rem !default;
$nav-link-padding-x: 1rem !default;
$nav-link-padding-x: 1.25rem !default;
$nav-link-disabled-color: $gray-600 !default;
$nav-tabs-border-color: $gray-300 !default;
@ -731,6 +731,8 @@ $navbar-toggler-padding-x: .75rem !default;
$navbar-toggler-font-size: $font-size-lg !default;
$navbar-toggler-border-radius: $btn-border-radius !default;
$navbar-nav-scroll-max-height: 75vh !default;
$navbar-dark-color: rgba($white, .5) !default;
$navbar-dark-hover-color: rgba($white, .75) !default;
$navbar-dark-active-color: $white !default;
@ -772,12 +774,12 @@ $dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default;
$dropdown-link-color: $gray-900 !default;
$dropdown-link-hover-color: darken($gray-900, 5%) !default;
$dropdown-link-hover-bg: $gray-100 !default;
$dropdown-link-hover-bg: $gray-200 !default;
$dropdown-link-active-color: $component-active-color !default;
$dropdown-link-active-bg: $component-active-bg !default;
$dropdown-link-disabled-color: $gray-600 !default;
$dropdown-link-disabled-color: $gray-500 !default;
$dropdown-item-padding-y: .25rem !default;
$dropdown-item-padding-x: 1.5rem !default;
@ -816,6 +818,8 @@ $pagination-disabled-color: $gray-600 !default;
$pagination-disabled-bg: $white !default;
$pagination-disabled-border-color: $gray-300 !default;
$pagination-border-radius-sm: $border-radius-sm !default;
$pagination-border-radius-lg: $border-radius-lg !default;
// Jumbotron
@ -1054,7 +1058,7 @@ $figure-caption-color: $gray-600 !default;
$breadcrumb-font-size: null !default;
$breadcrumb-padding-y: .75rem !default;
$breadcrumb-padding-x: 1rem !default;
$breadcrumb-padding-x: .5rem !default;
$breadcrumb-item-padding: .5rem !default;
$breadcrumb-margin-bottom: 1rem !default;

View file

@ -1,7 +1,7 @@
/*!
* Bootstrap Grid v4.5.3 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors
* Copyright 2011-2020 Twitter, Inc.
* Bootstrap Grid v4.6.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
@ -19,6 +19,7 @@ html {
@import "functions";
@import "variables";
@import "mixins/deprecate";
@import "mixins/breakpoints";
@import "mixins/grid-framework";
@import "mixins/grid";

View file

@ -1,7 +1,7 @@
/*!
* Bootstrap Reboot v4.5.3 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors
* Copyright 2011-2020 Twitter, Inc.
* Bootstrap Reboot v4.6.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/

View file

@ -1,7 +1,7 @@
/*!
* Bootstrap v4.5.3 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors
* Copyright 2011-2020 Twitter, Inc.
* Bootstrap v4.6.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/

View file

@ -64,6 +64,13 @@
color: color-yiq($color);
background-color: rgba($color, $form-feedback-tooltip-opacity);
@include border-radius($form-feedback-tooltip-border-radius);
// See https://github.com/twbs/bootstrap/pull/31557
// Align tooltip to form elements
.form-row > .col > &,
.form-row > [class*="col-"] > & {
left: $form-grid-gutter-width / 2;
}
}
@include form-validation-state-selector($state) {
@ -108,7 +115,7 @@
@if $enable-validation-icons {
padding-right: $custom-select-feedback-icon-padding-right;
background: $custom-select-background, escape-svg($icon) $custom-select-bg no-repeat $custom-select-feedback-icon-position / $custom-select-feedback-icon-size;
background: $custom-select-background, $custom-select-bg escape-svg($icon) $custom-select-feedback-icon-position / $custom-select-feedback-icon-size no-repeat;
}
&:focus {

View file

@ -26,7 +26,7 @@
// Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,
// but doesn't convert dppx=>dpi.
// There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.
// Compatibility info: https://caniuse.com/#feat=css-media-resolution
// Compatibility info: https://caniuse.com/css-media-resolution
@media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx
only screen and (min-resolution: 2dppx) { // Standardized
background-image: url($file-2x);

View file

@ -1,3 +1,3 @@
@import 'dev/scss/wvc-theme/flatly/variables';
@import 'dev/scss/bootstrap-overrides.scss';
@import 'dev/scss/wvc-theme/flatly/bootswatch';
@import 'dev/scss//wvc-theme/flatly/variables';
@import 'dev/scss//bootstrap-overrides.scss';
@import 'dev/scss//wvc-theme/flatly/bootswatch';

View file

@ -1,4 +1,4 @@
// Cerulean 4.5.3
// Cerulean 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Cerulean 4.5.3
// Cerulean 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Cosmo 4.5.3
// Cosmo 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Cosmo 4.5.3
// Cosmo 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Cyborg 4.5.3
// Cyborg 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Cyborg 4.5.3
// Cyborg 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Darkly 4.5.3
// Darkly 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Darkly 4.5.3
// Darkly 4.6.0
// Bootswatch
//
@ -83,7 +83,7 @@ $dropdown-link-hover-bg: $primary !default;
// Navs
$nav-link-padding-x: 1.5rem !default;
$nav-link-padding-x: 1.25rem !default;
$nav-link-disabled-color: $gray-500 !default;
$nav-tabs-border-color: $gray-700 !default;
$nav-tabs-link-hover-border-color: $nav-tabs-border-color $nav-tabs-border-color transparent !default;

View file

@ -1,4 +1,4 @@
// Flatly 4.5.3
// Flatly 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Flatly 4.5.3
// Flatly 4.6.0
// Bootswatch
//
@ -66,7 +66,7 @@ $dropdown-link-hover-bg: $primary !default;
// Navs
$nav-link-padding-y: .5rem !default !default;
$nav-link-padding-x: 1.5rem !default;
$nav-link-padding-x: 1.25rem !default;
$nav-link-disabled-color: $gray-600 !default !default;
$nav-tabs-border-color: $gray-200 !default;

View file

@ -1,4 +1,4 @@
// Journal 4.5.3
// Journal 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Journal 4.5.3
// Journal 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Litera 4.5.3
// Litera 4.6.0
// Bootswatch
// Navbar ======================================================================
@ -24,20 +24,6 @@
}
}
// Buttons =====================================================================
.btn {
border-radius: 1.078em;
&-lg {
border-radius: 2.688em;
}
&-sm {
border-radius: .844em;
}
}
// Typography ==================================================================
p {

View file

@ -1,4 +1,4 @@
// Litera 4.5.3
// Litera 4.6.0
// Bootswatch
//
@ -65,15 +65,17 @@ $btn-font-family: $font-family-sans-serif !default;
$btn-font-size: .875rem !default;
$btn-font-size-sm: .688rem !default;
$btn-border-radius: 1.078em !default;
$btn-border-radius-lg: 2.688em !default;
$btn-border-radius-sm: .844em !default;
// Forms
$input-border-color: rgba(0, 0, 0, .1) !default;
$input-group-addon-bg: $gray-200 !default !default;
$input-border-color: rgba(0, 0, 0, .1) !default;
$input-group-addon-bg: $gray-200 !default !default;
// Navbar
$navbar-padding-y: .7rem !default;
$navbar-dark-color: rgba($white, .6) !default;
$navbar-dark-hover-color: $white !default;
$navbar-light-hover-color: $body-color !default;
@ -86,8 +88,8 @@ $tooltip-font-size: 11px !default;
// Badges
$badge-font-weight: 400 !default;
$badge-padding-y: .3em !default;
$badge-padding-x: .6em !default;
$badge-padding-y: .6em !default;
$badge-padding-x: 1.2em !default;
// Alerts

View file

@ -1,4 +1,4 @@
// Lumen 4.5.3
// Lumen 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Lumen 4.5.3
// Lumen 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Lux 4.5.3
// Lux 4.6.0
// Bootswatch
@ -22,7 +22,7 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;6
}
&-brand {
margin-right: 1rem;
margin-right: 2rem;
}
}
@ -43,7 +43,7 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;6
}
.nav-item {
margin-right: 1rem;
margin-right: .5rem;
}
// Buttons =====================================================================

View file

@ -1,4 +1,4 @@
// Lux 4.5.3
// Lux 4.6.0
// Bootswatch
//
@ -72,12 +72,12 @@ $input-btn-border-width: 0 !default;
// Buttons
$btn-line-height: 1.5rem !default;
$input-btn-padding-y: .6rem !default;
$input-btn-padding-x: 1.2rem !default;
$input-btn-padding-y-sm: .4rem !default;
$input-btn-padding-x-sm: .8rem !default;
$input-btn-padding-y-lg: 2rem !default;
$input-btn-padding-x-lg: 2rem !default;
$input-btn-padding-y: .5rem !default;
$input-btn-padding-x: 1rem !default;
$input-btn-padding-y-sm: .25rem !default;
$input-btn-padding-x-sm: .75rem !default;
$input-btn-padding-y-lg: 1rem !default;
$input-btn-padding-x-lg: 1rem !default;
$btn-font-weight: 600 !default;
// Forms
@ -95,6 +95,7 @@ $navbar-light-color: rgba($black, .3) !default;
$navbar-light-hover-color: $gray-900 !default;
$navbar-light-active-color: $gray-900 !default;
// Pagination
$pagination-border-color: transparent !default;

View file

@ -1,4 +1,4 @@
// Materia 4.5.3
// Materia 4.6.0
// Bootswatch
@ -287,7 +287,7 @@ select,
select.form-control {
appearance: none;
padding: .5rem 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4' viewBox='0 0 8 4'%3E%3Cpolygon fill='%23666' points='8 0 4 4 0 0'/%3E%3C/svg%3E%0A");
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4' viewBox='0 0 8 4'%3e%3cpolygon fill='%23666' points='8 0 4 4 0 0'/%3e%3c/svg%3e%0a");
background-size: 8px 4px;
background-repeat: no-repeat;
background-position: right center;
@ -309,7 +309,7 @@ select.form-control {
&:focus {
box-shadow: inset 0 -2px 0 $primary;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4' viewBox='0 0 8 4'%3E%3Cpolygon fill='%23212121' points='8 0 4 4 0 0'/%3E%3C/svg%3E%0A");
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4' viewBox='0 0 8 4'%3e%3cpolygon fill='%23212121' points='8 0 4 4 0 0'/%3e%3c/svg%3e%0a");
}
&[multiple] {

View file

@ -1,4 +1,4 @@
// Materia 4.5.3
// Materia 4.6.0
// Bootswatch
//
@ -54,12 +54,12 @@ $font-weight-base: 400 !default;
// Buttons
$input-btn-padding-y: .8rem !default;
$input-btn-padding-y: .5rem !default;
$input-btn-padding-x: 1rem !default;
// Forms
$input-padding-y: 1rem !default;
$input-padding-y: .5rem !default;
$input-padding-x: 0 !default;
$input-padding-y-sm: 0 !default;
$input-padding-x-sm: 0 !default;

View file

@ -1,4 +1,4 @@
// Minty 4.5.3
// Minty 4.6.0
// Bootswatch
@ -33,7 +33,7 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Montserrat:wght@400;50
&-light,
&-light:hover {
color: $gray-700;
color: $gray-900;
}
&-link,

View file

@ -1,4 +1,4 @@
// Minty 4.5.3
// Minty 4.6.0
// Bootswatch
//
@ -34,7 +34,7 @@ $success: $green !default;
$info: $cyan !default;
$warning: $yellow !default;
$danger: $red !default;
$light: $gray-100 !default;
$light: $primary !default;
$dark: $gray-800 !default;
$yiq-contrasted-threshold: 250 !default;
@ -54,7 +54,6 @@ $border-radius-sm: .3rem !default;
// stylelint-disable-next-line value-keyword-case
$headings-font-family: Montserrat, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default;
$headings-color: $gray-700 !default;
$font-size-base: .875rem !default;
// Tables
@ -67,8 +66,6 @@ $dropdown-link-hover-bg: $secondary !default;
// Navbar
$navbar-padding-y: .7rem !default;
$navbar-dark-color: rgba($white, .6) !default;
$navbar-dark-hover-color: $white !default;
$navbar-light-color: rgba($black, .3) !default;

View file

@ -1,4 +1,4 @@
// Pulse 4.5.3
// Pulse 4.6.0
// Bootswatch
// Buttons =====================================================================

View file

@ -1,4 +1,4 @@
// Pulse 4.5.3
// Pulse 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Sandstone 4.5.3
// Sandstone 4.6.0
// Bootswatch
@ -22,7 +22,7 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;70
}
.sandstone {
font-size: 11px;
font-size: 12px;
line-height: 22px;
font-weight: 500;
text-transform: uppercase;

View file

@ -1,4 +1,4 @@
// Sandstone 4.5.3
// Sandstone 4.6.0
// Bootswatch
//
@ -51,7 +51,7 @@ $link-color: $success !default;
// stylelint-disable-next-line value-keyword-case
$font-family-sans-serif: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
$font-size-base: .875rem !default;
$font-size-base: .975rem !default;
$headings-font-weight: 400 !default;
// Dropdowns
@ -64,7 +64,7 @@ $dropdown-link-active-bg: $dropdown-link-hover-bg !default;
// Navs
$nav-link-padding-x: .9rem !default;
$nav-link-padding-x: 1.25rem !default;
$nav-link-disabled-color: $gray-300 !default;
$nav-tabs-border-color: $gray-300 !default;
$nav-tabs-link-hover-border-color: $gray-300 !default;

View file

@ -1,4 +1,4 @@
// Simplex 4.5.3
// Simplex 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Simplex 4.5.3
// Simplex 4.6.0
// Bootswatch
//
@ -54,7 +54,7 @@ $dropdown-link-hover-bg: $primary !default;
// Navs
$nav-link-padding-y: .7rem !default;
$nav-link-padding-y: .9rem !default;
$nav-link-disabled-color: $gray-400 !default;
$nav-tabs-border-color: darken(#fff, 6.5%) !default;

View file

@ -1,4 +1,4 @@
// Sketchy 4.5.3
// Sketchy 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Sketchy 4.5.3
// Sketchy 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Slate 4.5.3
// Slate 4.6.0
// Bootswatch
@ -525,7 +525,7 @@ legend {
}
.list-group {
&-item:hover {
&-item-action:hover {
background-color: darken($gray-900, 5%);
}
}

View file

@ -1,4 +1,4 @@
// Slate 4.5.3
// Slate 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Solar 4.5.3
// Solar 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Solar 4.5.3
// Solar 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// Spacelab 4.5.3
// Spacelab 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Spacelab 4.5.3
// Spacelab 4.6.0
// Bootswatch
//
@ -51,14 +51,16 @@ $link-color: $info !default;
// stylelint-disable-next-line value-keyword-case
$font-family-sans-serif: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
$font-size-base: .9375rem !default;
$font-size-sm: $font-size-base * .88 !default;
$headings-font-weight: 300 !default;
$headings-color: $gray-900 !default;
// Navbar
$navbar-padding-y: .7rem !default;
$navbar-dark-color: rgba($white, .75) !default;
$navbar-dark-hover-color: $white !default;
$navbar-light-color: rgba($black, .4) !default;
$navbar-light-hover-color: $info !default;
$navbar-light-active-color: $info !default;
$navbar-padding-y: .7rem !default;

View file

@ -1,4 +1,4 @@
// Superhero 4.5.3
// Superhero 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Superhero 4.5.3
// Superhero 4.6.0
// Bootswatch
//

View file

@ -1,4 +1,4 @@
// United 4.5.3
// United 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// United 4.5.3
// United 4.6.0
// Bootswatch
//
@ -34,7 +34,7 @@ $success: $green !default;
$info: $cyan !default;
$warning: $yellow !default;
$danger: $red !default;
$light: $gray-200 !default;
$light: $gray-100 !default;
$dark: $purple !default;
$yiq-contrasted-threshold: 200 !default;
@ -53,6 +53,13 @@ $font-family-sans-serif: Ubuntu, -apple-system, BlinkMacSystemFont, "Segoe
$table-dark-bg: $dark !default;
$table-dark-border-color: darken($dark, 5%) !default;
// Navbar
$navbar-padding-y: .7rem !default;
$navbar-padding-y: .7rem !default;
// Breadcrumb
$breadcrumb-padding-y: .75rem !default;
$breadcrumb-padding-x: .25rem !default;
$breadcrumb-item-padding: .25rem !default;

View file

@ -1,4 +1,4 @@
// Yeti 4.5.3
// Yeti 4.6.0
// Bootswatch

View file

@ -1,4 +1,4 @@
// Yeti 4.5.3
// Yeti 4.6.0
// Bootswatch
//

View file

@ -204,10 +204,6 @@ class Instance(models.Model):
def formats(self):
return self.proxy.get_image_formats()
@cached_property
def interfaces(self):
return self.proxy.get_ifaces()
class PermissionSet(models.Model):
"""

View file

@ -27,7 +27,7 @@
{% for c_net in networks_host %}
<option value="net:{{ c_net }}">Network {{ c_net }}</option>
{% endfor %}
{% for c_iface in instance.interfaces %}
{% for c_iface in interfaces_host %}
<option value="iface:{{ c_iface }}">Interface {{ c_iface }}</option>
{% endfor %}
</select>

View file

@ -54,7 +54,14 @@
{{ ipv4 }} |
{% endfor %}
{% endfor %}
<a class="text-secondary" href="{% url 'instances:instance' instance.id %}" title="{% trans 'Refresh instance info' %}"><span class="fa fa-refresh"></span></a>
{% if instance.guest_agent_ready %}
<a class="text-secondary" title="{% trans 'Show Instance OS details' %}" onclick="get_osinfo()">
<span class="fa fa-info-circle"></span>
</a>
{% endif %}
<a class="text-secondary" href="{% url 'instances:instance' instance.id %}" title="{% trans 'Refresh instance info' %}">
<span class="fa fa-refresh"></span>
</a>
</div>
{% if user_quota_msg %}
<div class="alert alert-warning fade show">
@ -71,13 +78,13 @@
<ul class="nav nav-pills" id="navbtn" aria-label="Instance actions">
<li class="nav-item">
<a href="#power" class="nav-link action-button active" aria-controls="power" role="tab" data-toggle="tab">
<i id="action-block" class="fa fa-power-off fa-2x" aria-hidden="true"></i>
<span id="action-block" class="fa fa-power-off fa-2x" aria-hidden="true"></span>
{% trans "Power" %}
</a>
</li>
<li class="nav-item">
<a href="#access" class="nav-link action-button" aria-controls="access" role="tab" data-toggle="tab">
<i id="action-block" class="fa fa-lock" aria-hidden="true"></i>
<span id="action-block" class="fa fa-lock" aria-hidden="true"></span>
{% trans "Access" %}
</a>
</li>
@ -684,7 +691,7 @@
}
});
}
if (~$.inArray(hash, ['#boot_opt', "#disks", '#network', '#clone', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) {
if (~$.inArray(hash, ['#osinfo', '#boot_opt', "#disks", '#network', '#clone', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) {
var btnsect = $('#navbtn>li>a');
$(btnsect).each(function () {
if ($(this).attr('href') === '#settings') {
@ -715,4 +722,22 @@
});
}
</script>
<script>
function get_osinfo() {
$('#navbtn a[href="#settings"]').tab('show');
$('#settings a[href="#osinfo"]').tab('show');
$.getJSON('{% url 'instances:osinfo' instance.id %}', function (data) {
$.each(data, function() {
$('#oshostname').text(data['host-name']);
$('#osname').text(data.id);
$('#osprettyname').text(data['pretty-name']);
$('#oskernelrelease').text(data['kernel-release']);
$('#oskernelversion').text(data['kernel-version']);
$('#osversion').text(data.version);
$('#ostimezone').text(data.zone + " / " + data.offset);
})
});
}
</script>
{% endblock %}

View file

@ -1,168 +1,166 @@
{% load i18n %}
<div role="tabpanel" class="tab-pane" id="access" aria-label="Instance access options">
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link text-secondary active" href="#vnconsole" aria-controls="vnconsole" role="tab"
data-toggle="tab">
{% trans "Console" %}
</a>
</li>
{% if app_settings.SHOW_ACCESS_ROOT_PASSWORD == 'True' %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#rootpasswd" aria-controls="rootpasswd" role="tab"
data-toggle="tab">
{% trans "Root Password" %}
</a>
</li>
{% endif %}
{% if app_settings.SHOW_ACCESS_SSH_KEYS == 'True' %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#sshkeys" aria-controls="sshkeys" role="tab" data-toggle="tab">
{% trans "SSH Keys" %}
</a>
</li>
{% endif %}
{% if instance.status == 1 %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#vdiconsole" aria-controls="vdiconsole" role="tab"
data-toggle="tab">
{% trans "VDI" %}
</a>
</li>
{% endif %}
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="vnconsole">
<p>{% blocktrans with type=instance.console_type|upper %} This action opens a new window with a {{ type }} connection to the console of the instance.{% endblocktrans %}
</p>
{% if instance.console_type == 'vnc' %}
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="scale"
{% if app_settings.CONSOLE_SCALE == 'True' %} checked {% endif %}
id="scale">
<label class="custom-control-label font-weight-bold" for="scale">{% trans "Scale" %}</label>
</div>
</div>
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="view_only"
{% if app_settings.CONSOLE_VIEW_ONLY == 'True' %} checked {% endif %}
id="view_only">
<label class="custom-control-label font-weight-bold" for="view_only">{% trans "View Only" %}</label>
</div>
</div>
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="resize_session"
{% if app_settings.CONSOLE_RESIZE_SESSION == 'True' %} checked {% endif %}
id="resize_session">
<label class="custom-control-label font-weight-bold" for="resize_session">{% trans "Resize Session" %}</label>
</div>
</div>
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="clip_viewport"
{% if app_settings.CONSOLE_CLIP_VIEWPORT == 'True' %} checked {% endif %}
id="clip_viewport">
<label class="custom-control-label font-weight-bold" for="clip_viewport">{% trans "View Clipboard" %}</label>
</div>
</div>
{% endif %}
{% if instance.status == 1 %}
<!-- Split button -->
<div class="btn-group float-right">
<button type="button" id="consoleBtnGroup" class="btn btn-lg btn-success"
onclick="open_console('lite')">{% trans 'Console' %}</button>
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">{% trans 'Toggle Dropdown' %}</span>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"
title="{% trans "Console port" %}: {{ instance.console_port }}"
onclick="open_console('lite')">{% trans "Console" %} - {% trans "Lite" %}</a>
<a class="dropdown-item" href="#"
title="{% trans "Console port" %}: {{ instance.console_port }}"
onclick="open_console('full')">{% trans "Console" %} - {% trans "Full" %}</a>
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link text-secondary active" href="#vnconsole" aria-controls="vnconsole" role="tab"
data-toggle="tab">
{% trans "Console" %}
</a>
</li>
{% if app_settings.SHOW_ACCESS_ROOT_PASSWORD == 'True' %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#rootpasswd" aria-controls="rootpasswd" role="tab"
data-toggle="tab">
{% trans "Root Password" %}
</a>
</li>
{% endif %}
{% if app_settings.SHOW_ACCESS_SSH_KEYS == 'True' %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#sshkeys" aria-controls="sshkeys" role="tab" data-toggle="tab">
{% trans "SSH Keys" %}
</a>
</li>
{% endif %}
{% if instance.status == 1 %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#vdiconsole" aria-controls="vdiconsole" role="tab"
data-toggle="tab">
{% trans "VDI" %}
</a>
</li>
{% endif %}
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="vnconsole">
<p>{% blocktrans with type=instance.console_type|upper %} This action opens a new window with a {{ type }} connection to the console of the instance.{% endblocktrans %}
</p>
{% if instance.console_type == 'vnc' %}
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="scale"
{% if app_settings.CONSOLE_SCALE == 'True' %} checked {% endif %}
id="scale">
<label class="custom-control-label font-weight-bold" for="scale">{% trans "Scale" %}</label>
</div>
</div>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Console" %}</button>
{% endif %}
<div class="clearfix"></div>
</div>
{% if app_settings.SHOW_ACCESS_ROOT_PASSWORD == 'True' %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="rootpasswd">
<p>{% trans "You need shut down your instance and enter a new root password." %}</p>
<form action="{% url 'instances:rootpasswd' instance.id %}" class="form-inline" method="post"
role="form" aria-label="Add root password to instance form">
{% csrf_token %}
<div class="form-group row">
<div class="col-sm-12">
<input type="text" class="form-control-lg" name="passwd"
placeholder="{% trans "Enter Password" %}" maxlength="24">
</div>
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="view_only"
{% if app_settings.CONSOLE_VIEW_ONLY == 'True' %} checked {% endif %}
id="view_only">
<label class="custom-control-label font-weight-bold" for="view_only">{% trans "View Only" %}</label>
</div>
{% if instance.status == 5 %}
<input type="submit" class="btn btn-lg btn-success float-right" name="rootpasswd"
value="{% trans "Reset Root Password" %}">
{% else %}
<button
class="btn btn-lg btn-success float-right disabled">{% trans "Reset Root Password" %}</button>
{% endif %}
</form>
<div class="clearfix"></div>
</div>
{% endif %}
{% if app_settings.SHOW_ACCESS_SSH_KEYS == 'True' %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="sshkeys">
<p>{% trans "You need shut down your instance and choose your public key." %}</p>
<form action="{% url 'instances:add_public_key' instance.id %}" class="form-inline" method="post"
role="form" aria-label="Add public key to instance form">
{% csrf_token %}
<div class="form-group row">
<div class="col-sm-12">
<select name="sshkeyid" class="form-control-lg keyselect">
{% if publickeys %}
{% for key in publickeys %}
<option value="{{ key.id }}">{{ key.keyname }}</option>
{% endfor %}
{% else %}
<option value="None">{% trans "None" %}</option>
{% endif %}
</select>
</div>
</div>
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="resize_session"
{% if app_settings.CONSOLE_RESIZE_SESSION == 'True' %} checked {% endif %}
id="resize_session">
<label class="custom-control-label font-weight-bold" for="resize_session">{% trans "Resize Session" %}</label>
</div>
{% if instance.status == 5 %}
<input type="submit" class="btn btn-lg btn-success float-right" name="addpublickey"
value="{% trans "Add Public Key" %}">
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Add Public Key" %}</button>
{% endif %}
</form>
<div class="clearfix"></div>
</div>
</div>
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="clip_viewport"
{% if app_settings.CONSOLE_CLIP_VIEWPORT == 'True' %} checked {% endif %}
id="clip_viewport">
<label class="custom-control-label font-weight-bold" for="clip_viewport">{% trans "View Clipboard" %}</label>
</div>
</div>
{% endif %}
{% if instance.status == 1 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vdiconsole">
<p>{% trans "This action opens a remote viewer with a connection to the console of the instance." %}</p>
<div class="input-group">
<input type="text" class="input-lg disabled form-control" disabled id="vdi_url_input" />
<span class="input-group-append">
<a href="#" class="btn btn-success" id="vdi_url">{% trans "VDI" %}</a>
</span>
<!-- Split button -->
<div class="btn-group float-right">
<button type="button" id="consoleBtnGroup" class="btn btn-lg btn-success"
onclick="open_console('lite')">{% trans 'Console' %}</button>
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">{% trans 'Toggle Dropdown' %}</span>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"
title="{% trans "Console port" %}: {{ instance.console_port }}"
onclick="open_console('lite')">{% trans "Console" %} - {% trans "Lite" %}</a>
<a class="dropdown-item" href="#"
title="{% trans "Console port" %}: {{ instance.console_port }}"
onclick="open_console('full')">{% trans "Console" %} - {% trans "Full" %}</a>
</div>
<p>{% trans "To download console.vv file for virt-viewer." %}</p>
<a href="{% url 'instances:getvvfile' instance.id %}" class="btn btn-lg btn-success float-right">{% trans "Get console.vv" %}</a>
<div class="clearfix"></div>
</div>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Console" %}</button>
{% endif %}
<div class="clearfix"></div>
</div>
{% if app_settings.SHOW_ACCESS_ROOT_PASSWORD == 'True' %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="rootpasswd">
<p>{% trans "You need shut down your instance and enter a new root password." %}</p>
<form action="{% url 'instances:rootpasswd' instance.id %}" class="form-inline" method="post"
role="form" aria-label="Add root password to instance form">
{% csrf_token %}
<div class="form-group row">
<div class="col-sm-12">
<input type="text" class="form-control-lg" name="passwd"
placeholder="{% trans "Enter Password" %}" maxlength="24">
</div>
</div>
{% if instance.status == 5 %}
<input type="submit" class="btn btn-lg btn-success float-right" name="rootpasswd"
value="{% trans "Reset Root Password" %}">
{% else %}
<button
class="btn btn-lg btn-success float-right disabled">{% trans "Reset Root Password" %}</button>
{% endif %}
</form>
<div class="clearfix"></div>
</div>
{% endif %}
{% if app_settings.SHOW_ACCESS_SSH_KEYS == 'True' %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="sshkeys">
<p>{% trans "You need shut down your instance and choose your public key." %}</p>
<form action="{% url 'instances:add_public_key' instance.id %}" class="form-inline" method="post"
role="form" aria-label="Add public key to instance form">
{% csrf_token %}
<div class="form-group row">
<div class="col-sm-12">
<select name="sshkeyid" class="form-control-lg keyselect">
{% if publickeys %}
{% for key in publickeys %}
<option value="{{ key.id }}">{{ key.keyname }}</option>
{% endfor %}
{% else %}
<option value="None">{% trans "None" %}</option>
{% endif %}
</select>
</div>
</div>
{% if instance.status == 5 %}
<input type="submit" class="btn btn-lg btn-success float-right" name="addpublickey"
value="{% trans "Add Public Key" %}">
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Add Public Key" %}</button>
{% endif %}
</form>
<div class="clearfix"></div>
</div>
{% endif %}
{% if instance.status == 1 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vdiconsole">
<p>{% trans "This action opens a remote viewer with a connection to the console of the instance." %}</p>
<div class="input-group">
<input type="text" class="input-lg disabled form-control" disabled id="vdi_url_input" />
<span class="input-group-append">
<a href="#" class="btn btn-success" id="vdi_url">{% trans "VDI" %}</a>
</span>
</div>
<p>{% trans "To download console.vv file for virt-viewer." %}</p>
<a href="{% url 'instances:getvvfile' instance.id %}" class="btn btn-lg btn-success float-right">{% trans "Get console.vv" %}</a>
<div class="clearfix"></div>
</div>
{% endif %}
</div>
</div>
{% block script %}

View file

@ -1,29 +1,27 @@
{% load i18n %}
<div role="tabpanel" class="tab-pane" id="undefine">
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance destroy menu">
<li class="nav-item">
<a class="nav-link active" href="#destroy" aria-controls="destroy" role="tab" data-toggle="tab">
{% trans "Destroy Instance" %}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy">
<p>{% trans 'This action starts remove instance process' %}</p>
{% if request.user.is_superuser or userinstance.is_delete %}
{% if instance.status == 3 %}
<a class="btn btn-lg btn-success disabled float-right">{% trans "Destroy" %}</a>
{% else %}
<a href="{% url 'instances:destroy' instance.id %}" class="btn btn-lg btn-success float-right">{% trans "Destroy" %}</a>
{% endif %}
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance destroy menu">
<li class="nav-item">
<a class="nav-link active" href="#destroy" aria-controls="destroy" role="tab" data-toggle="tab">
{% trans "Destroy Instance" %}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy">
<p>{% trans 'This action starts remove instance process' %}</p>
{% if request.user.is_superuser or userinstance.is_delete %}
{% if instance.status == 3 %}
<a class="btn btn-lg btn-success disabled float-right">{% trans "Destroy" %}</a>
{% else %}
<button class="btn btn-lg btn-success disabled float-right" name="delete">{% trans "Destroy" %}</button>
<a href="{% url 'instances:destroy' instance.id %}" class="btn btn-lg btn-success float-right">{% trans "Destroy" %}</a>
{% endif %}
<div class="clearfix"></div>
</div>
{% else %}
<button class="btn btn-lg btn-success disabled float-right" name="delete">{% trans "Destroy" %}</button>
{% endif %}
<div class="clearfix"></div>
</div>
</div>
</div>

View file

@ -0,0 +1,20 @@
{% load i18n %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="osinfo">
<h3 class="page-header">{% trans "Guest Info" %}</h3>
<dl class="mx-3 row">
<dt class="col-3">{% trans "Hostname" %}</dt>
<dd class="col-9" id="oshostname"></dd>
<dt class="col-3">{% trans "OS Name" %}</dt>
<dd class="col-9" id="osname"></dd>
<dt class="col-3">{% trans "OS Pretty-Name" %}</dt>
<dd class="col-9" id="osprettyname"></dd>
<dt class="col-3">{% trans "Version" %}</dt>
<dd class="col-9" id="osversion"></dd>
<dt class="col-3">{% trans "Kernel Release" %}</dt>
<dd class="col-9" id="oskernelrelease"></dd>
<dt class="col-3">{% trans "Kernel Version" %}</dt>
<dd class="col-9" id="oskernelversion"></dd>
<dt class="col-3">{% trans "Timezone / Offset" %}</dt>
<dd class="col-9" id="ostimezone"></dd>
</dl>
</div>

View file

@ -1,131 +1,129 @@
{% load i18n %}
<div role="tabpanel" class="tab-pane active" id="power">
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance power actions">
{% if instance.status == 1 %}
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance power actions">
{% if instance.status == 1 %}
<li class="nav-item">
<a class="nav-link text-secondary active" href="#poweroff" aria-controls="poweroff" role="tab" data-toggle="tab">
{% trans "Power Off" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#powercycle" aria-controls="powercycle" role="tab" data-toggle="tab">
{% trans "Power Cycle" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#powerforce" aria-controls="powerforce" role="tab" data-toggle="tab">
{% trans "Force Off" %}
</a>
</li>
{% if request.user.is_superuser %}
<li class="nav-item">
<a class="nav-link text-secondary active" href="#poweroff" aria-controls="poweroff" role="tab" data-toggle="tab">
{% trans "Power Off" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#powercycle" aria-controls="powercycle" role="tab" data-toggle="tab">
{% trans "Power Cycle" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#powerforce" aria-controls="powerforce" role="tab" data-toggle="tab">
{% trans "Force Off" %}
</a>
</li>
{% if request.user.is_superuser %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#suspend" aria-controls="suspend" role="tab" data-toggle="tab">
{% trans "Suspend" %}
</a>
</li>
{% endif %}
{% endif %}
{% if instance.status == 3 %}
{% if request.user.is_superuser %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#resume" aria-controls="resume" role="tab" data-toggle="tab">
{% trans "Resume" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#powerforce" aria-controls="powerforce" role="tab" data-toggle="tab">
{% trans "Force Off" %}
</a>
</li>
{% endif %}
{% endif %}
{% if instance.status == 5 %}
<li class="nav-item">
<a class="nav-link text-secondary active" href="#boot" aria-controls="boot" role="tab" data-toggle="tab">
{% trans "Power On" %}
<a class="nav-link text-secondary" href="#suspend" aria-controls="suspend" role="tab" data-toggle="tab">
{% trans "Suspend" %}
</a>
</li>
{% endif %}
</ul>
<!-- Tab panes -->
<div class="tab-content">
{% if instance.status == 1 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="poweroff">
<p>{% trans "This action sends an ACPI shutdown signal to the instance." %}</p>
<form action="{% url 'instances:poweroff' instance.id %}" method="post" role="form" aria-label0="Power off instance form">
{% csrf_token %}
<input type="submit" name="poweroff" class="btn btn-lg btn-success float-right" value="{% trans "Power Off" %}">
{% endif %}
{% if instance.status == 3 %}
{% if request.user.is_superuser %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#resume" aria-controls="resume" role="tab" data-toggle="tab">
{% trans "Resume" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#powerforce" aria-controls="powerforce" role="tab" data-toggle="tab">
{% trans "Force Off" %}
</a>
</li>
{% endif %}
{% endif %}
{% if instance.status == 5 %}
<li class="nav-item">
<a class="nav-link text-secondary active" href="#boot" aria-controls="boot" role="tab" data-toggle="tab">
{% trans "Power On" %}
</a>
</li>
{% endif %}
</ul>
<!-- Tab panes -->
<div class="tab-content">
{% if instance.status == 1 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="poweroff">
<p>{% trans "This action sends an ACPI shutdown signal to the instance." %}</p>
<form action="{% url 'instances:poweroff' instance.id %}" method="post" role="form" aria-label0="Power off instance form">
{% csrf_token %}
<input type="submit" name="poweroff" class="btn btn-lg btn-success float-right" value="{% trans "Power Off" %}">
<div class="clearfix"></div>
</form>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="powercycle">
<p>{% trans "This action forcibly powers off and start the instance and may cause data corruption." %}</p>
<form action="{% url 'instances:powercycle' instance.id %}" method="post" role="form" aria-label="Power cycle instance form">{% csrf_token %}
<input type="submit" name="powercycle" class="btn btn-lg btn-success float-right" value="{% trans "Power Cycle" %}">
<div class="clearfix"></div>
</form>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="powerforce">
<p>{% trans "This action forcibly powers off the instance and may cause data corruption." %}</p>
<form action="{% url 'instances:force_off' instance.id %}" method="post" role="form" aria-label="Force to shotdown instance form">
{% csrf_token %}
<input type="submit" name="powerforce" class="btn btn-lg btn-success float-right" value="{% trans "Force Off" %}">
<div class="clearfix"></div>
</form>
</div>
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="suspend">
<p>{% trans "This action suspends the instance." %}</p>
<form action="{% url 'instances:suspend' instance.id %}" method="post" role="form" aria-label="Suspend instance form">{% csrf_token %}
<input type="submit" name="suspend" class="btn btn-lg btn-success float-right" value="{% trans "Suspend" %}">
<div class="clearfix"></div>
</form>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="powercycle">
<p>{% trans "This action forcibly powers off and start the instance and may cause data corruption." %}</p>
<form action="{% url 'instances:powercycle' instance.id %}" method="post" role="form" aria-label="Power cycle instance form">{% csrf_token %}
<input type="submit" name="powercycle" class="btn btn-lg btn-success float-right" value="{% trans "Power Cycle" %}">
{% endif %}
{% endif %}
{% if instance.status == 3 %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<p>{% trans "This action restore the instance after suspend." %}</p>
<form action="{% url 'instances:resume' instance.id %}" method="post" role="form" aria-label="Resume instance from suspension form">{% csrf_token %}
<input type="submit" name="resume" class="btn btn-lg btn-success float-right" value="{% trans "Resume" %}">
<div class="clearfix"></div>
</form>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="powerforce">
<p>{% trans "This action forcibly powers off the instance and may cause data corruption." %}</p>
<form action="{% url 'instances:force_off' instance.id %}" method="post" role="form" aria-label="Force to shotdown instance form">
{% csrf_token %}
<form action="{% url 'instances:force_off' instance.id %}" method="post" role="form" aria-label="Force to shutdown form">{% csrf_token %}
<input type="submit" name="powerforce" class="btn btn-lg btn-success float-right" value="{% trans "Force Off" %}">
<div class="clearfix"></div>
</form>
</div>
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="suspend">
<p>{% trans "This action suspends the instance." %}</p>
<form action="{% url 'instances:suspend' instance.id %}" method="post" role="form" aria-label="Suspend instance form">{% csrf_token %}
<input type="submit" name="suspend" class="btn btn-lg btn-success float-right" value="{% trans "Suspend" %}">
<div class="clearfix"></div>
</form>
</div>
{% endif %}
{% endif %}
{% if instance.status == 3 %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<p>{% trans "This action restore the instance after suspend." %}</p>
<form action="{% url 'instances:resume' instance.id %}" method="post" role="form" aria-label="Resume instance from suspension form">{% csrf_token %}
<input type="submit" name="resume" class="btn btn-lg btn-success float-right" value="{% trans "Resume" %}">
<div class="clearfix"></div>
</form>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="powerforce">
<p>{% trans "This action forcibly powers off the instance and may cause data corruption." %}</p>
<form action="{% url 'instances:force_off' instance.id %}" method="post" role="form" aria-label="Force to shutdown form">{% csrf_token %}
<input type="submit" name="powerforce" class="btn btn-lg btn-success float-right" value="{% trans "Force Off" %}">
<div class="clearfix"></div>
</form>
</div>
{% else %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<p>{% trans "Administrator blocked your instance." %}</p>
<form action="{% url 'instances:resume' instance.id %}" method="post" role="form" aria-label="Resume instance form">{% csrf_token %}
<button class="btn btn-lg btn-success disabled float-right">{% trans "Resume" %}</button>
<div class="clearfix"></div>
</form>
</div>
{% endif %}
{% endif %}
{% if instance.status == 5 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot">
<p>{% trans "Click on Power On button to start this instance." %}</p>
<form action="{% url 'instances:poweron' instance.id %}" method="post" role="form" aria-label="Start instance form">
{% csrf_token %}
{% if instance.is_template %}
<p>{% trans "Template instance cannot be started." %}</p>
<input type="submit" name="poweron" class="btn btn-lg btn-success float-right disabled" value="{% trans "Power On" %}">
{% else %}
<input type="submit" name="poweron" class="btn btn-lg btn-success float-right" value="{% trans "Power On" %}">
{% endif %}
{% else %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<p>{% trans "Administrator blocked your instance." %}</p>
<form action="{% url 'instances:resume' instance.id %}" method="post" role="form" aria-label="Resume instance form">{% csrf_token %}
<button class="btn btn-lg btn-success disabled float-right">{% trans "Resume" %}</button>
<div class="clearfix"></div>
</form>
</div>
{% endif %}
</div>
{% endif %}
{% if instance.status == 5 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot">
<p>{% trans "Click on Power On button to start this instance." %}</p>
<form action="{% url 'instances:poweron' instance.id %}" method="post" role="form" aria-label="Start instance form">
{% csrf_token %}
{% if instance.is_template %}
<p>{% trans "Template instance cannot be started." %}</p>
<input type="submit" name="poweron" class="btn btn-lg btn-success float-right disabled" value="{% trans "Power On" %}">
{% else %}
<input type="submit" name="poweron" class="btn btn-lg btn-success float-right" value="{% trans "Power On" %}">
{% endif %}
<div class="clearfix"></div>
</form>
</div>
{% endif %}
</div>
</div>

View file

@ -1,164 +1,162 @@
{% load i18n %}
<div role="tabpanel" class="tab-pane" id="resize">
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance resize options">
<li class="nav-item">
<a class="nav-link text-secondary active" href="#resizevm_cpu" aria-controls="resizevm_cpu" role="tab" data-toggle="tab">
{% trans "CPU" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#resizevm_mem" aria-controls="resizevm_mem" role="tab" data-toggle="tab">
{% trans "Memory" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#resizevm_disk" aria-controls="resizevm_disk" role="tab" data-toggle="tab">
{% trans "Disk" %}
</a>
</li>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resizevm_cpu">
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
{% if instance.status == 5 or not instance.vcpus %}
<form action="{% url 'instances:resizevm_cpu' instance.id %}" method="post" role="form" aria-label="Resize instance cpu form">{% csrf_token %}
<p class="font-weight-bold">{% trans "Logical host CPUs" %} : {{ vcpu_host }}</p>
<div class="form-group row">
<label class="col-sm-4 col-form-label"> {% trans "Current Allocation" %}</label>
<div class="col-sm-4">
<select name="cur_vcpu" class="custom-select">
{% for cpu in instance.vcpu_range %}
{% if instance.cur_vcpu %}
<option value="{{ cpu }}" {% if cpu == instance.cur_vcpu %}selected{% endif %}>{{ cpu }}</option>
{% else %}
<option value="{{ cpu }}" {% if cpu == instance.vcpu %}selected{% endif %}>{{ cpu }}</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">{% trans "Maximum Allocation" %}</label>
<div class="col-sm-4">
<select name="vcpu" class="custom-select">
{% for cpu in instance.vcpu_range %}
<option value="{{ cpu }}" {% if cpu == instance.vcpu %}selected{% endif %}>{{ cpu }}</option>
{% endfor %}
</select>
</div>
</div>
{% if instance.status == 5 %}
<button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_cpu">{% trans "Resize" %}</button>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
</form>
<div class="clearfix"></div>
{% else %}
<p class="font-weight-bold">{% trans "Logical Instance Active/Maximum CPUs" %} : {{ instance.cur_vcpu }} / {{ instance.vcpu }} </p>
<div class="col-sm-3"></div>
<div class="col-sm-6">
{% for id, vcpu in instance.vcpus.items %}
<form action="{% url 'instances:set_vcpu' instance.id %}" method="post" role="form" aria-label="Resize instance cpu form">{% csrf_token %}
<div class="col-sm-3">
<input name="id" value="{{ id }}" hidden/>
{% if vcpu.enabled == 'yes' and vcpu.hotpluggable == "yes" %}
<button type="submit" class="btn btn-block btn-success" value="False" name="set_vcpu" title="{% trans "Disable" %}">{{ id }}</button>
{% elif vcpu.enabled == 'yes' and vcpu.hotpluggable == "no" %}
<button type="button" class="btn btn btn-block btn-info" title="{% trans "Constant" %}">{{ id }}</button>
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance resize options">
<li class="nav-item">
<a class="nav-link text-secondary active" href="#resizevm_cpu" aria-controls="resizevm_cpu" role="tab" data-toggle="tab">
{% trans "CPU" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#resizevm_mem" aria-controls="resizevm_mem" role="tab" data-toggle="tab">
{% trans "Memory" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#resizevm_disk" aria-controls="resizevm_disk" role="tab" data-toggle="tab">
{% trans "Disk" %}
</a>
</li>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resizevm_cpu">
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
{% if instance.status == 5 or not instance.vcpus %}
<form action="{% url 'instances:resizevm_cpu' instance.id %}" method="post" role="form" aria-label="Resize instance cpu form">{% csrf_token %}
<p class="font-weight-bold">{% trans "Logical host CPUs" %} : {{ vcpu_host }}</p>
<div class="form-group row">
<label class="col-sm-4 col-form-label"> {% trans "Current Allocation" %}</label>
<div class="col-sm-4">
<select name="cur_vcpu" class="custom-select">
{% for cpu in instance.vcpu_range %}
{% if instance.cur_vcpu %}
<option value="{{ cpu }}" {% if cpu == instance.cur_vcpu %}selected{% endif %}>{{ cpu }}</option>
{% else %}
<button type="submit" class="btn btn btn-block btn-secondary" value="True" name="set_vcpu" title="{% trans "Enable" %}">{{ id }}</button>
<option value="{{ cpu }}" {% if cpu == instance.vcpu %}selected{% endif %}>{{ cpu }}</option>
{% endif %}
</div>
</form>
{% endfor %}
{% endfor %}
</select>
</div>
<div class="col-sm-3"></div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">{% trans "Maximum Allocation" %}</label>
<div class="col-sm-4">
<select name="vcpu" class="custom-select">
{% for cpu in instance.vcpu_range %}
<option value="{{ cpu }}" {% if cpu == instance.vcpu %}selected{% endif %}>{{ cpu }}</option>
{% endfor %}
</select>
</div>
</div>
{% if instance.status == 5 %}
<button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_cpu">{% trans "Resize" %}</button>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
{% else %}
{% trans "You don't have permission for resizing instance" %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
</form>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="resizevm_mem">
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
<form action="{% url 'instances:resize_memory' instance.id %}" method="post" role="form" aria-label="Resize instance memory form">
{% csrf_token %}
<p class="font-weight-bold">{% trans "Total host memory" %}: {{ memory_host|filesizeformat }}</p>
<div class="form-group row">
<label class="col-sm-4 col-form-label">{% trans "Current Allocation" %} ({% trans "MB" %})</label>
<div class="col-sm-4 js-custom__container">
<select name="cur_memory" class="custom-select js-custom__toggle">
{% for mem in memory_range %}
<option value="{{ mem }}" {% if mem == instance.cur_memory %}selected{% endif %}>{{ mem }}</option>
{% endfor %}
</select>
<input type="text" name="cur_memory_custom" class="custom-select js-custom__toggle" style="display: none" />
<small><input type="checkbox" class="js-custom__checkbox" /> {% trans "Custom value" %}</small>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">
{% trans "Maximum Allocation" %} ({% trans "MB" %})
</label>
<div class="col-sm-4 js-custom__container">
<select name="memory" class="form-control js-custom__toggle">
{% for mem in memory_range %}
<option value="{{ mem }}"
{% if mem == instance.memory %}selected{% endif %}>{{ mem }}</option>
{% endfor %}
</select>
<input type="text" name="memory_custom" class="form-control js-custom__toggle" style="display: none" />
<small><input type="checkbox" class="js-custom__checkbox" /> {% trans "Custom value" %}</small>
</div>
</div>
<button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_mem">{% trans "Resize" %}</button>
</form>
{% else %}
{% trans "You don't have permission for resizing instance" %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="resizevm_disk">
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
<form action="{% url 'instances:resize_disk' instance.id %}" method="post" role="form" aria-label="Resize instance disk form">
{% csrf_token %}
<p class="font-weight-bold">{% trans "Disk allocation (GB)" %}:</p>
{% for disk in instance.disks %}
<div class="form-group row">
<label class="col-sm-4 col-form-label">{% trans "Current Allocation" %} ({{ disk.dev }})</label>
{% if disk.storage is None %}
<div class="col-sm-4 js-custom__container">
<div class="alert alert-danger">
{% trans "Error getting disk info" %}
</div>
<p class="font-weight-bold">{% trans "Logical Instance Active/Maximum CPUs" %} : {{ instance.cur_vcpu }} / {{ instance.vcpu }} </p>
<div class="col-sm-3"></div>
<div class="col-sm-6">
{% for id, vcpu in instance.vcpus.items %}
<form action="{% url 'instances:set_vcpu' instance.id %}" method="post" role="form" aria-label="Resize instance cpu form">{% csrf_token %}
<div class="col-sm-3">
<input name="id" value="{{ id }}" hidden/>
{% if vcpu.enabled == 'yes' and vcpu.hotpluggable == "yes" %}
<button type="submit" class="btn btn-block btn-success" value="False" name="set_vcpu" title="{% trans "Disable" %}">{{ id }}</button>
{% elif vcpu.enabled == 'yes' and vcpu.hotpluggable == "no" %}
<button type="button" class="btn btn btn-block btn-info" title="{% trans "Constant" %}">{{ id }}</button>
{% else %}
<button type="submit" class="btn btn btn-block btn-secondary" value="True" name="set_vcpu" title="{% trans "Enable" %}">{{ id }}</button>
{% endif %}
</div>
{% else %}
<div class="col-sm-4 js-custom__container">
<input type="number" name="disk_size_{{ disk.dev }}" class="form-control" value="{% widthratio disk.size 1073741824 1 %}" />
</div>
{% endif %}
</div>
</form>
{% endfor %}
{% if instance.status == 5 %}
<button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_disk">{% trans "Resize" %}</button>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
</form>
{% else %}
{% trans "You don't have permission for resizing instance" %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
</div>
<div class="col-sm-3"></div>
{% endif %}
<div class="clearfix"></div>
</div>
{% else %}
{% trans "You don't have permission for resizing instance" %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="resizevm_mem">
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
<form action="{% url 'instances:resize_memory' instance.id %}" method="post" role="form" aria-label="Resize instance memory form">
{% csrf_token %}
<p class="font-weight-bold">{% trans "Total host memory" %}: {{ memory_host|filesizeformat }}</p>
<div class="form-group row">
<label class="col-sm-4 col-form-label">{% trans "Current Allocation" %} ({% trans "MB" %})</label>
<div class="col-sm-4 js-custom__container">
<select name="cur_memory" class="custom-select js-custom__toggle">
{% for mem in memory_range %}
<option value="{{ mem }}" {% if mem == instance.cur_memory %}selected{% endif %}>{{ mem }}</option>
{% endfor %}
</select>
<input type="text" name="cur_memory_custom" class="custom-select js-custom__toggle" style="display: none" />
<small><input type="checkbox" class="js-custom__checkbox" /> {% trans "Custom value" %}</small>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">
{% trans "Maximum Allocation" %} ({% trans "MB" %})
</label>
<div class="col-sm-4 js-custom__container">
<select name="memory" class="form-control js-custom__toggle">
{% for mem in memory_range %}
<option value="{{ mem }}"
{% if mem == instance.memory %}selected{% endif %}>{{ mem }}</option>
{% endfor %}
</select>
<input type="text" name="memory_custom" class="form-control js-custom__toggle" style="display: none" />
<small><input type="checkbox" class="js-custom__checkbox" /> {% trans "Custom value" %}</small>
</div>
</div>
<button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_mem">{% trans "Resize" %}</button>
</form>
{% else %}
{% trans "You don't have permission for resizing instance" %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="resizevm_disk">
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
<form action="{% url 'instances:resize_disk' instance.id %}" method="post" role="form" aria-label="Resize instance disk form">
{% csrf_token %}
<p class="font-weight-bold">{% trans "Disk allocation (GB)" %}:</p>
{% for disk in instance.disks %}
<div class="form-group row">
<label class="col-sm-4 col-form-label">{% trans "Current Allocation" %} ({{ disk.dev }})</label>
{% if disk.storage is None %}
<div class="col-sm-4 js-custom__container">
<div class="alert alert-danger">
{% trans "Error getting disk info" %}
</div>
</div>
{% else %}
<div class="col-sm-4 js-custom__container">
<input type="number" name="disk_size_{{ disk.dev }}" class="form-control" value="{% widthratio disk.size 1073741824 1 %}" />
</div>
{% endif %}
</div>
{% endfor %}
{% if instance.status == 5 %}
<button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_disk">{% trans "Resize" %}</button>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
</form>
{% else %}
{% trans "You don't have permission for resizing instance" %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endif %}
<div class="clearfix"></div>
</div>
</div>
</div>

File diff suppressed because it is too large Load diff

View file

@ -1,83 +1,81 @@
{% load i18n %}
{% load icons %}
<div role="tabpanel" class="tab-pane" id="snapshots">
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance snapshot menu">
<li class="nav-item">
<a class="nav-link text-secondary active" href="#takesnapshot" aria-controls="takesnapshot" role="tab" data-toggle="tab">
{% trans "Take Snapshot" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#managesnapshot" aria-controls="managesnapshot" role="tab" data-toggle="tab">
{% trans "Manage Snapshots" %}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="takesnapshot">
<p>{% trans "This may take more than an hour, depending on how much content is on your instance and how large the disk is. It could cause web server timeout.." %}</p>
<form action="{% url 'instances:snapshot' instance.id %}" class="form-inline" method="post" role="form" aria-label="Create snapshot form">
{% csrf_token %}
<div class="form-group row">
<div class="col-sm-12">
<input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Enter Snapshot Name" %}" maxlength="14">
</div>
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance snapshot menu">
<li class="nav-item">
<a class="nav-link text-secondary active" href="#takesnapshot" aria-controls="takesnapshot" role="tab" data-toggle="tab">
{% trans "Take Snapshot" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#managesnapshot" aria-controls="managesnapshot" role="tab" data-toggle="tab">
{% trans "Manage Snapshots" %}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="takesnapshot">
<p>{% trans "This may take more than an hour, depending on how much content is on your instance and how large the disk is. It could cause web server timeout.." %}</p>
<form action="{% url 'instances:snapshot' instance.id %}" class="form-inline" method="post" role="form" aria-label="Create snapshot form">
{% csrf_token %}
<div class="form-group row">
<div class="col-sm-12">
<input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Enter Snapshot Name" %}" maxlength="14">
</div>
<input type="submit" class="btn btn-lg btn-success float-right" name="snapshot" value="{% trans "Take Snapshot" %}" onclick="showPleaseWaitDialog();">
</form>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="managesnapshot">
{% if instance.snapshots %}
<p>{% trans "Choose a snapshot for restore/delete" %}</p>
<div class="table-responsive">
<table class="table">
<thead>
<th scope="col">{% trans "Name" %}</th>
<th scope="col">{% trans "Date" %}</th>
<th scope="colgroup" colspan="2">{% trans "Action" %}</th>
</thead>
<tbody>
{% for snap in instance.snapshots %}
<tr>
<td><strong>{{ snap.name }}</strong></td>
<td>{{ snap.date|date:"M d H:i:s" }}</td>
<td style="width:30px;">
<form action="{% url 'instances:revert_snapshot' instance.id %}" method="post" role="form" aria-label="Restore snapshot form">
{% csrf_token %}
<input type="hidden" name="name" value="{{ snap.name }}">
{% if instance.status == 5 %}
<button type="submit" class="btn btn-sm btn-secondary" name="revert_snapshot" title="{% trans 'Revert to this Snapshot' %}" onclick="return confirm('Are you sure?')">
<span class="fa fa-download"></span>
</button>
{% else %}
<button type="button" class="btn btn-sm btn-secondary disabled"
title="{% trans "To restore snapshots you need Power Off the instance." %}">
<span class="fa fa-download"></span>
</button>
{% endif %}
</form>
</td>
<td style="width:30px;">
<form action="{% url 'instances:delete_snapshot' instance.id %}" method="post" role="form" aria-label="Delete snapshot form">{% csrf_token %}
<input type="hidden" name="name" value="{{ snap.name }}">
<button type="submit" class="btn btn-sm btn-danger" title="{% trans 'Delete Snapshot' %}" onclick="return confirm('{% trans "Are you sure?" %}')">
{% icon 'trash' %}
</div>
<input type="submit" class="btn btn-lg btn-success float-right" name="snapshot" value="{% trans "Take Snapshot" %}" onclick="showPleaseWaitDialog();">
</form>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="managesnapshot">
{% if instance.snapshots %}
<p>{% trans "Choose a snapshot for restore/delete" %}</p>
<div class="table-responsive">
<table class="table">
<thead>
<th scope="col">{% trans "Name" %}</th>
<th scope="col">{% trans "Date" %}</th>
<th scope="colgroup" colspan="2">{% trans "Action" %}</th>
</thead>
<tbody>
{% for snap in instance.snapshots %}
<tr>
<td><strong>{{ snap.name }}</strong></td>
<td>{{ snap.date|date:"M d H:i:s" }}</td>
<td style="width:30px;">
<form action="{% url 'instances:revert_snapshot' instance.id %}" method="post" role="form" aria-label="Restore snapshot form">
{% csrf_token %}
<input type="hidden" name="name" value="{{ snap.name }}">
{% if instance.status == 5 %}
<button type="submit" class="btn btn-sm btn-secondary" name="revert_snapshot" title="{% trans 'Revert to this Snapshot' %}" onclick="return confirm('Are you sure?')">
<span class="fa fa-download"></span>
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p>{% trans "You do not have any snapshots" %}</p>
{% endif %}
</div>
{% else %}
<button type="button" class="btn btn-sm btn-secondary disabled"
title="{% trans "To restore snapshots you need Power Off the instance." %}">
<span class="fa fa-download"></span>
</button>
{% endif %}
</form>
</td>
<td style="width:30px;">
<form action="{% url 'instances:delete_snapshot' instance.id %}" method="post" role="form" aria-label="Delete snapshot form">{% csrf_token %}
<input type="hidden" name="name" value="{{ snap.name }}">
<button type="submit" class="btn btn-sm btn-danger" title="{% trans 'Delete Snapshot' %}" onclick="return confirm('{% trans "Are you sure?" %}')">
{% icon 'trash' %}
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p>{% trans "You do not have any snapshots" %}</p>
{% endif %}
</div>
</div>
</div>

View file

@ -1,99 +1,97 @@
{% load i18n %}
<div role="tabpanel" class="tab-pane" id="graphics">
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance graphs and logs menu">
<li class="nav-item">
<a class="nav-link text-secondary active" href="#graphs" id="graphtab" aria-controls="graphs" role="tab" data-toggle="tab" aria-controls="graphs" aria-selected="true">
{% trans "Real Time" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#logs" id="logtab" aria-controls="logs" role="tab" data-toggle="tab" aria-controls="logs" onclick='update_logs_table("{{ instance.name }}");'>
{% trans "Logs" %}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="graphs">
<div class="mb-1 card border-success">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
{% trans "CPU Usage" %}</h5>
</div>
<div class="card-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart">
<canvas id="cpuChart" width="735" height="160"></canvas>
</div>
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance graphs and logs menu">
<li class="nav-item">
<a class="nav-link text-secondary active" href="#graphs" id="graphtab" aria-controls="graphs" role="tab" data-toggle="tab" aria-controls="graphs" aria-selected="true">
{% trans "Real Time" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="#logs" id="logtab" aria-controls="logs" role="tab" data-toggle="tab" aria-controls="logs" onclick='update_logs_table("{{ instance.name }}");'>
{% trans "Logs" %}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="graphs">
<div class="mb-1 card border-success">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
{% trans "CPU Usage" %}</h5>
</div>
<div class="card-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart">
<canvas id="cpuChart" width="735" height="160"></canvas>
</div>
</div>
</div>
<div class="mb-1 card border-danger">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
{% trans "Memory Usage" %}</h5>
</div>
<div class="card-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart">
<canvas id="memChart" width="735" height="160"></canvas>
</div>
</div>
</div>
</div>
{% for net in instance.networks %}
<div class="mb-1 card border-info">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
{% trans "Bandwidth Device" %}: eth{{ forloop.counter0 }}</h5>
</div>
<div class="card-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart">
<canvas id="netEth{{ forloop.counter0 }}Chart" width="735" height="160"></canvas>
</div>
</div>
</div>
</div>
{% endfor %}
{% for disk in instance.disks %}
<div class="mb-1 card border-warning">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
{% trans "Disk I/O device" %}: {{ disk.dev }}</h5>
</div>
<div class="card-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart">
<canvas id="blk{{ disk.dev }}Chart" width="735" height="160"></canvas>
</div>
</div>
</div>
</div>
{% endfor %}
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="logs">
<div class="table-responsive">
<table class="table table-striped sortable-theme-bootstrap" id="logs_table" data-sortable>
<thead>
<tr>
<th scope="col">{% trans "Date" %}</th>
<th scope="col">{% trans "User" %}</th>
<th scope="col">{% trans "Message" %}</th>
</tr>
</thead>
<tbody class="searchable">
<tr>
<td colspan="3"><i>{% trans 'None' %}...</i></td>
</tr>
</tbody>
</table>
<div class="mb-1 card border-danger">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
{% trans "Memory Usage" %}</h5>
</div>
<div class="card-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart">
<canvas id="memChart" width="735" height="160"></canvas>
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
{% for net in instance.networks %}
<div class="mb-1 card border-info">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
{% trans "Bandwidth Device" %}: eth{{ forloop.counter0 }}</h5>
</div>
<div class="card-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart">
<canvas id="netEth{{ forloop.counter0 }}Chart" width="735" height="160"></canvas>
</div>
</div>
</div>
</div>
{% endfor %}
{% for disk in instance.disks %}
<div class="mb-1 card border-warning">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
{% trans "Disk I/O device" %}: {{ disk.dev }}</h5>
</div>
<div class="card-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart">
<canvas id="blk{{ disk.dev }}Chart" width="735" height="160"></canvas>
</div>
</div>
</div>
</div>
{% endfor %}
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="logs">
<div class="table-responsive">
<table class="table table-striped sortable-theme-bootstrap" id="logs_table" data-sortable>
<thead>
<tr>
<th scope="col">{% trans "Date" %}</th>
<th scope="col">{% trans "User" %}</th>
<th scope="col">{% trans "Message" %}</th>
</tr>
</thead>
<tbody class="searchable">
<tr>
<td colspan="3"><i>{% trans 'None' %}...</i></td>
</tr>
</tbody>
</table>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>

View file

@ -20,6 +20,7 @@ urlpatterns = [
path('<int:pk>/migrate/', views.migrate, name='migrate'),
path('<int:pk>/status/', views.status, name='status'),
path('<int:pk>/stats/', views.stats, name='stats'),
path('<int:pk>/osinfo/', views.osinfo, name='osinfo'),
path('<int:pk>/rootpasswd/', views.set_root_pass, name='rootpasswd'),
path('<int:pk>/add_public_key/', views.add_public_key, name='add_public_key'),
path('<int:pk>/resizevm_cpu/', views.resizevm_cpu, name='resizevm_cpu'),

View file

@ -169,17 +169,15 @@ def refr(compute):
if compute.status is True:
domains = compute.proxy.wvm.listAllDomains()
domain_names = [d.name() for d in domains]
domain_uuids = [d.UUIDString() for d in domains]
# Delete instances that're not on host
Instance.objects.filter(compute=compute).exclude(name__in=domain_names).delete()
Instance.objects.filter(compute=compute).exclude(uuid__in=domain_uuids).delete()
# Create instances that're not in DB
names = Instance.objects.filter(compute=compute).values_list('name', flat=True)
uuids = Instance.objects.filter(compute=compute).values_list('uuid', flat=True)
for domain in domains:
if domain.name() not in names:
Instance(compute=compute, name=domain.name(), uuid=domain.UUIDString()).save()
continue
if domain.UUIDString() not in uuids:
Instance(compute=compute, name=domain.name(), uuid=domain.UUIDString()).save()
def get_dhcp_mac_address(vname):

View file

@ -25,6 +25,7 @@ from logs.views import addlogmsg
from vrtManager import util
from vrtManager.create import wvmCreate
from vrtManager.instance import wvmInstances
from vrtManager.interface import wvmInterface
from vrtManager.storage import wvmStorage
from vrtManager.util import randomPasswd
@ -121,6 +122,7 @@ def instance(request, pk):
memory_host = instance.proxy.get_max_memory()
bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine)
networks_host = sorted(instance.proxy.get_networks())
interfaces_host = sorted(instance.proxy.get_ifaces())
nwfilters_host = instance.proxy.get_nwfilters()
storages_host = sorted(instance.proxy.get_storages(True))
net_models_host = instance.proxy.get_network_models()
@ -161,6 +163,11 @@ def stats(request, pk):
}
)
def osinfo(request, pk):
instance = get_instance(request.user, pk)
results = instance.proxy.osinfo()
return JsonResponse(results)
def guess_mac_address(request, vname):
data = {"vname": vname}
@ -764,7 +771,7 @@ def revert_snapshot(request, pk):
msg = _("Successful revert snapshot: ")
msg += snap_name
messages.success(request, msg)
msg = _("Revert snapshot: %(snap)") % {"snap": snap_name}
msg = _("Revert snapshot: %(snap)s") % {"snap": snap_name}
addlogmsg(request.user.username, instance.name, msg)
return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
@ -882,7 +889,7 @@ def set_video_model(request, pk):
instance = get_instance(request.user, pk)
video_model = request.POST.get("video_model", "vga")
instance.proxy.set_video_model(video_model)
msg = _("Set Video Model: %(model)") % {"model": video_model}
msg = _("Set Video Model: %(model)s") % {"model": video_model}
addlogmsg(request.user.username, instance.name, msg)
return redirect(request.META.get("HTTP_REFERER") + "#options")
@ -899,6 +906,16 @@ def change_network(request, pk):
(source, source_type) = utils.get_network_tuple(request.POST.get(post))
network_data[post] = source
network_data[post + "-type"] = source_type
if source_type == 'iface':
iface = wvmInterface(
instance.compute.hostname,
instance.compute.login,
instance.compute.password,
instance.compute.type,
source,
)
network_data[post + "-type"] = iface.get_type()
elif post.startswith("net-"):
network_data[post] = request.POST.get(post, "")
@ -918,6 +935,16 @@ def add_network(request, pk):
nwfilter = request.POST.get("add-net-nwfilter")
(source, source_type) = utils.get_network_tuple(request.POST.get("add-net-network"))
if source_type == 'iface':
iface = wvmInterface(
instance.compute.hostname,
instance.compute.login,
instance.compute.password,
instance.compute.type,
source,
)
source_type = iface.get_type()
instance.proxy.add_network(mac, source, source_type, nwfilter=nwfilter)
msg = _("Add network: %(mac)s") % {"mac": mac}
addlogmsg(request.user.username, instance.name, msg)

View file

@ -29,7 +29,8 @@ def interfaces(request, compute_id):
netdevs = ["eth0", "eth1"]
for iface in ifaces:
ifaces_all.append(conn.get_iface_info(iface))
interf = wvmInterface(compute.hostname, compute.login, compute.password, compute.type, iface)
ifaces_all.append(interf.get_details())
if request.method == "POST":
if "create" in request.POST:

View file

@ -296,12 +296,12 @@
<button type="submit" class="btn btn-sm btn-primary"
name="modify_fixed_address"
title="{% trans "Edit entry" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
<i class="fa fa-save"></i>
<span class="fa fa-save"></span>
</button>
<button type="submit" class="btn btn-sm btn-danger"
name="delete_fixed_address"
title="{% trans "Delete entry" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
<i class="fa fa-trash"></i>
<span class="fa fa-trash"></span>
</button>
</td>
</form>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
static/js/jquery.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,3 @@
/*! js-cookie v2.2.0 | MIT */
/*! js-cookie v2.2.1 | MIT */
!function(e){var n=!1;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var o=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=o,t}}}(function(){function e(){for(var e=0,n={};e<arguments.length;e++){var o=arguments[e];for(var t in o)n[t]=o[t]}return n}function n(o){function t(n,r,i){var c;if("undefined"!=typeof document){if(arguments.length>1){if("number"==typeof(i=e({path:"/"},t.defaults,i)).expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*i.expires),i.expires=a}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(r),/^[\{\[]/.test(c)&&(r=c)}catch(e){}r=o.write?o.write(r,n):encodeURIComponent(r+"").replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=(n=(n=encodeURIComponent(n+"")).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent)).replace(/[\(\)]/g,escape);var s="";for(var f in i)i[f]&&(s+="; "+f,!0!==i[f]&&(s+="="+i[f]));return document.cookie=n+"="+r+s}n||(c={});for(var p=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,u=0;u<p.length;u++){var l=p[u].split("="),C=l.slice(1).join("=");this.json||'"'!==C.charAt(0)||(C=C.slice(1,-1));try{var m=l[0].replace(d,decodeURIComponent);if(C=o.read?o.read(C,m):o(C,m)||C.replace(d,decodeURIComponent),this.json)try{C=JSON.parse(C)}catch(e){}if(n===m){c=C;break}n||(c[m]=C)}catch(e){}}return c}}return t.set=t,t.get=function(e){return t.call(t,e)},t.getJSON=function(){return t.apply({json:!0},[].slice.call(arguments))},t.defaults={},t.remove=function(n,o){t(n,"",e(o,{expires:-1}))},t.withConverter=n,t}return n(function(){})});
!function(a){var b;if("function"==typeof define&&define.amd&&(define(a),b=!0),"object"==typeof exports&&(module.exports=a(),b=!0),!b){var c=window.Cookies,d=window.Cookies=a();d.noConflict=function(){return window.Cookies=c,d}}}(function(){function a(){for(var a=0,b={};a<arguments.length;a++){var c=arguments[a];for(var d in c)b[d]=c[d]}return b}function b(a){return a.replace(/(%[0-9A-Z]{2})+/g,decodeURIComponent)}function c(d){function e(){}function f(b,c,f){if("undefined"!=typeof document){f=a({path:"/"},e.defaults,f),"number"==typeof f.expires&&(f.expires=new Date(1*new Date+864e5*f.expires)),f.expires=f.expires?f.expires.toUTCString():"";try{var g=JSON.stringify(c);/^[\{\[]/.test(g)&&(c=g)}catch(j){}c=d.write?d.write(c,b):encodeURIComponent(c+"").replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),b=encodeURIComponent(b+"").replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent).replace(/[\(\)]/g,escape);var h="";for(var i in f)f[i]&&(h+="; "+i,!0!==f[i]&&(h+="="+f[i].split(";")[0]));return document.cookie=b+"="+c+h}}function g(a,c){if("undefined"!=typeof document){for(var e={},f=document.cookie?document.cookie.split("; "):[],g=0;g<f.length;g++){var h=f[g].split("="),i=h.slice(1).join("=");c||'"'!==i.charAt(0)||(i=i.slice(1,-1));try{var j=b(h[0]);if(i=(d.read||d)(i,j)||b(i),c)try{i=JSON.parse(i)}catch(k){}if(e[j]=i,a===j)break}catch(k){}}return a?e[a]:e}}return e.set=f,e.get=function(a){return g(a,!1)},e.getJSON=function(a){return g(a,!0)},e.remove=function(b,c){f(b,"",a(c,{expires:-1}))},e.defaults={},e.withConverter=c,e}return c(function(){})});

View file

@ -482,7 +482,7 @@ class wvmConnect(object):
def get_networks(self):
"""
:return: list of networks
:return: list of host networks
"""
virtnet = []
for net in self.wvm.listNetworks():
@ -493,7 +493,7 @@ class wvmConnect(object):
def get_ifaces(self):
"""
:return: list of network interfaces
:return: list of host interfaces
"""
interface = []
for inface in self.wvm.listInterfaces():

View file

@ -1,3 +1,4 @@
import json
import os.path
import time
@ -20,6 +21,10 @@ try:
VIR_MIGRATE_POSTCOPY,
)
from libvirt import VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT
from libvirt_qemu import (
qemuAgentCommand,
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT
)
except:
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE
@ -160,6 +165,35 @@ class wvmInstance(wvmConnect):
self._ip_cache = None
self.instance = self.get_instance(vname)
def osinfo(self):
info_results = qemuAgentCommand(
self.instance,
'{"execute":"guest-get-osinfo"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, 0
)
timezone_results = qemuAgentCommand(
self.instance,
'{"execute":"guest-get-timezone"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, 0
)
hostname_results = qemuAgentCommand(
self.instance,
'{"execute":"guest-get-host-name"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, 0
)
info_results = json.loads(info_results).get('return')
timezone_results = json.loads(timezone_results).get('return')
hostname_results = json.loads(hostname_results).get('return')
info_results.update(timezone_results)
info_results.update(hostname_results)
return info_results
def start(self):
self.instance.create()
@ -1332,19 +1366,15 @@ class wvmInstance(wvmConnect):
return bridge_name
def add_network(self, mac_address, source, source_type="net", model="virtio", nwfilter=None):
forward_mode = ""
if source_type != "iface":
forward_mode = self.get_network_forward(source)
if forward_mode in ["nat", "isolated", "routed"]:
if source_type == "net":
interface_type = "network"
elif forward_mode == "":
interface_type = "direct"
elif source_type == "bridge":
interface_type = "bridge"
else:
if self.get_bridge_name(source, source_type) is None:
interface_type = "network"
else:
interface_type = "bridge"
interface_type = "direct"
# network modes not handled: default is bridge
xml_iface = f"""
<interface type='{interface_type}'>
@ -1352,13 +1382,11 @@ class wvmInstance(wvmConnect):
if interface_type == "network":
xml_iface += f"""<source network='{source}'/>"""
elif interface_type == "direct":
if source_type == "net":
xml_iface += f"""<source network='{source}' mode='bridge'/>"""
else:
xml_iface += f"""<source dev='{source}' mode='bridge'/>"""
xml_iface += f"""<source dev='{source}' mode='bridge'/>"""
elif interface_type == "bridge":
xml_iface += f"""<source bridge='{source}'/>"""
else:
bridge_name = self.get_bridge_name(source, source_type)
xml_iface += f"""<source bridge='{bridge_name}'/>"""
raise libvirtError(f"'{interface_type}' is an unexpected interface type.")
xml_iface += f"""<model type='{model}'/>"""
if nwfilter:
xml_iface += f"""<filterref filter='{nwfilter}'/>"""
@ -1382,8 +1410,35 @@ class wvmInstance(wvmConnect):
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
return new_xml
return None
def change_network(self, network_data):
net_mac = network_data.get("net-mac-0")
net_source = network_data.get("net-source-0")
net_source_type = network_data.get("net-source-0-type")
net_filter = network_data.get("net-nwfilter-0")
net_model = network_data.get("net-model-0")
# Remove interface first, but keep network interface XML definition
# If there is an error happened while adding changed one, then add removed one to back.
status = self.delete_network(net_mac)
try:
self.add_network(net_mac, net_source, net_source_type, net_model, net_filter)
except libvirtError:
if status is not None:
if self.get_status() == 1:
self.instance.attachDeviceFlags(status, VIR_DOMAIN_AFFECT_LIVE)
self.instance.attachDeviceFlags(status, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.attachDeviceFlags(status, VIR_DOMAIN_AFFECT_CONFIG)
def change_network_oldway(self, network_data):
'''
change network firsh version...
will be removed if new one works as expected for all scenarios
'''
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = ElementTree.fromstring(xml)
for num, interface in enumerate(tree.findall("devices/interface")):

View file

@ -6,13 +6,6 @@ from vrtManager.connection import wvmConnect
class wvmInterfaces(wvmConnect):
def get_iface_info(self, name):
iface = self.get_iface(name)
xml = iface.XMLDesc(0)
mac = iface.MACString()
itype = util.get_xml_path(xml, "/interface/@type")
state = iface.isActive()
return {"name": name, "type": itype, "state": state, "mac": mac}
def define_iface(self, xml, flag=0):
self.wvm.interfaceDefineXML(xml, flag)
@ -153,6 +146,12 @@ class wvmInterface(wvmConnect):
else:
return None
def get_details(self):
mac = self.get_mac()
itype = self.get_type()
state = self.is_active()
return {"name": self.iface.name(), "type": itype, "state": state, "mac": mac}
def stop_iface(self):
self.iface.destroy()

View file

@ -146,10 +146,12 @@ configure_nginx () {
cp "$APP_PATH"/conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
if [ -n "$fqdn" ]; then
sed -i "s|\\(#server_name\\).*|server_name = $fqdn|" "$nginxfile"
fqdn_escape="$(echo -n "$fqdn"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
sed -i "s|\\(#server_name\\).*|server_name $fqdn_escape;|" "$nginxfile"
fi
sed -i "s|\\(server 127.0.0.1:\\).*|\\1$novncd_port;|" "$nginxfile"
novncd_port_escape="$(echo -n "$novncd_port"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
sed -i "s|\\(server 127.0.0.1:\\).*|\\1$novncd_port_escape;|" "$nginxfile"
}
@ -157,7 +159,8 @@ configure_supervisor () {
# Copy template supervisor service for gunicorn and novnc
echo " * Copying supervisor configuration"
cp "$APP_PATH"/conf/supervisor/webvirtcloud.conf "$supervisor_conf_path"/"$supervisor_file_name"
sed -i "s|^\\(user=\\).*|\\1$nginx_group|" "$supervisor_conf_path/$supervisor_file_name"
nginx_group_escape="$(echo -n "$nginx_group"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
sed -i "s|^\\(user=\\).*|\\1$nginx_group_escape|" "$supervisor_conf_path/$supervisor_file_name"
}
create_user () {
@ -206,13 +209,18 @@ install_webvirtcloud () {
secret_key=$(generate_secret_key)
echo "* Secret for Django generated: $secret_key"
tzone_escape="$(echo -n "$tzone"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
secret_key_escape="$(echo -n "$secret_key"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
novncd_port_escape="$(echo -n "$novncd_port"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
novncd_public_port_escape="$(echo -n "$novncd_public_port"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
novncd_host_escape="$(echo -n "$novncd_host"|sed -e 's/[](){}<>=:\!\?\+\|\/\&$*.^[]/\\&/g')"
#TODO escape SED delimiter in variables
sed -i "s|^\\(TIME_ZONE = \\).*|\\1$tzone|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(SECRET_KEY = \\).*|\\1\'$secret_key\'|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(WS_PORT = \\).*|\\1$novncd_port|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(WS_PUBLIC_PORT = \\).*|\\1$novncd_public_port|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(WS_HOST = \\).*|\\1\'$novncd_host\'|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(TIME_ZONE = \\).*|\\1$tzone_escape|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(SECRET_KEY = \\).*|\\1\'$secret_key_escape\'|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(WS_PORT = \\).*|\\1$novncd_port_escape|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(WS_PUBLIC_PORT = \\).*|\\1$novncd_public_port_escape|" "$APP_PATH/webvirtcloud/settings.py"
sed -i "s|^\\(WS_HOST = \\).*|\\1\'$novncd_host_escape\'|" "$APP_PATH/webvirtcloud/settings.py"
echo "* Activate virtual environment."
activate_python_environment
@ -455,7 +463,7 @@ case $distro in
log "yum -y install wget epel-release"
echo "* Installing OS requirements."
PACKAGES="git python3-virtualenv python3-devel libvirt-devel glibc gcc nginx supervisor python3-lxml python3-libguestfs iproute-tc cyrus-sasl-md5 python3-libguestfs"
PACKAGES="git python3-virtualenv python3-devel libvirt-devel glibc gcc nginx supervisor python3-lxml python3-libguestfs iproute-tc cyrus-sasl-md5"
install_packages
set_hosts