1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-03-22 02:24:56 +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: How to update <code>gstfsd</code> daemon on hypervisor:
```bash ```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 sudo service supervisor restart
``` ```
@ -86,7 +86,7 @@ sudo service supervisor restart
Setup libvirt and KVM on server Setup libvirt and KVM on server
```bash ```bash
wget -O - https://clck.ru/9V9fH | sudo sh wget -O - https://bit.ly/36baWUu | sudo sh
``` ```
Done!! Done!!

View file

@ -4,8 +4,10 @@ from instances.models import Instance
def refresh_instance_database(compute): def refresh_instance_database(compute):
domains = compute.proxy.wvm.listAllDomains() domains = compute.proxy.wvm.listAllDomains()
domain_names = [d.name() for d in domains] 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 # 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(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 # Create instances that're on host but not in DB
names = Instance.objects.filter(compute=compute).values_list('name', flat=True) names = Instance.objects.filter(compute=compute).values_list('name', flat=True)
for domain in domains: for domain in domains:

View file

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

View file

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

View file

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

View file

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

View file

@ -10,14 +10,12 @@
} }
.breadcrumb-item { .breadcrumb-item {
display: flex;
// The separator between breadcrumbs (by default, a forward-slash: "/") // The separator between breadcrumbs (by default, a forward-slash: "/")
+ .breadcrumb-item { + .breadcrumb-item {
padding-left: $breadcrumb-item-padding; padding-left: $breadcrumb-item-padding;
&::before { &::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; padding-right: $breadcrumb-item-padding;
color: $breadcrumb-divider-color; color: $breadcrumb-divider-color;
content: escape-svg($breadcrumb-divider); content: escape-svg($breadcrumb-divider);

View file

@ -127,7 +127,7 @@
display: inline-block; display: inline-block;
width: $carousel-control-icon-width; width: $carousel-control-icon-width;
height: $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 { .carousel-control-prev-icon {
background-image: escape-svg($carousel-control-prev-icon-bg); background-image: escape-svg($carousel-control-prev-icon-bg);

View file

@ -104,7 +104,7 @@
width: $custom-control-indicator-size; width: $custom-control-indicator-size;
height: $custom-control-indicator-size; height: $custom-control-indicator-size;
content: ""; 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%; width: 100%;
height: $custom-file-height; height: $custom-file-height;
margin: 0; margin: 0;
overflow: hidden;
opacity: 0; opacity: 0;
&:focus ~ .custom-file-label { &:focus ~ .custom-file-label {
@ -347,6 +348,7 @@
z-index: 1; z-index: 1;
height: $custom-file-height; height: $custom-file-height;
padding: $custom-file-padding-y $custom-file-padding-x; padding: $custom-file-padding-y $custom-file-padding-x;
overflow: hidden;
font-family: $custom-file-font-family; font-family: $custom-file-font-family;
font-weight: $custom-file-font-weight; font-weight: $custom-file-font-weight;
line-height: $custom-file-line-height; line-height: $custom-file-line-height;
@ -388,7 +390,7 @@
appearance: none; appearance: none;
&:focus { &:focus {
outline: none; outline: 0;
// Pseudo-elements must be split across multiple rulesets to have an effect. // Pseudo-elements must be split across multiple rulesets to have an effect.
// No box-shadow() mixin for focus accessibility. // 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 // stylelint-disable-next-line no-duplicate-selectors
.dropdown-menu { .dropdown-menu {
&[x-placement^="top"], &[x-placement^="top"],

View file

@ -42,7 +42,6 @@
> .form-control, > .form-control,
> .custom-select { > .custom-select {
&:not(:last-child) { @include border-right-radius(0); }
&:not(:first-child) { @include border-left-radius(0); } &:not(:first-child) { @include border-left-radius(0); }
} }
@ -53,9 +52,24 @@
align-items: center; align-items: center;
&:not(:last-child) .custom-file-label, &: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(: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 > .btn,
.input-group > .input-group-prepend > .input-group-text, .input-group > .input-group-prepend > .input-group-text,
.input-group > .input-group-append:not(:last-child) > .btn, .input-group:not(.has-validation) > .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) > .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 > .btn:not(:last-child):not(.dropdown-toggle),
.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { .input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
@include border-right-radius(0); @include border-right-radius(0);

View file

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

View file

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

View file

@ -66,9 +66,9 @@
// //
.pagination-lg { .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 { .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 { @if $enable-transitions {
.progress-bar-animated { .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 { @if $enable-prefers-reduced-motion-media-query {
@media (prefers-reduced-motion: reduce) { @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 // Reboot
// //
@ -307,13 +307,13 @@ button {
border-radius: 0; border-radius: 0;
} }
// Work around a Firefox/IE bug where the transparent `button` background // Explicitly remove focus outline in Chromium when it shouldn't be
// results in a loss of the default `button` focus styles. // visible (e.g. as result of mouse click or touch tap). It already
// // should be doing this automatically, but seems to currently be
// Credit: https://github.com/suitcss/base/ // confused and applies its very visible two-tone outline anyway.
button:focus {
outline: 1px dotted; button:focus:not(:focus-visible) {
outline: 5px auto -webkit-focus-ring-color; outline: 0;
} }
input, input,

View file

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

View file

@ -15,7 +15,7 @@
border-right-color: transparent; border-right-color: transparent;
// stylelint-disable-next-line property-disallowed-list // stylelint-disable-next-line property-disallowed-list
border-radius: 50%; border-radius: 50%;
animation: spinner-border .75s linear infinite; animation: .75s linear infinite spinner-border;
} }
.spinner-border-sm { .spinner-border-sm {
@ -47,10 +47,19 @@
// stylelint-disable-next-line property-disallowed-list // stylelint-disable-next-line property-disallowed-list
border-radius: 50%; border-radius: 50%;
opacity: 0; opacity: 0;
animation: spinner-grow .75s linear infinite; animation: .75s linear infinite spinner-grow;
} }
.spinner-grow-sm { .spinner-grow-sm {
width: $spinner-width-sm; width: $spinner-width-sm;
height: $spinner-height-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 // Headings

View file

@ -274,7 +274,7 @@ $embed-responsive-aspect-ratios: join(
// Font, line-height, and color for body text, headings, and more. // Font, line-height, and color for body text, headings, and more.
// stylelint-disable value-keyword-case // 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-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
$font-family-base: $font-family-sans-serif !default; $font-family-base: $font-family-sans-serif !default;
// stylelint-enable value-keyword-case // 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-bg-size: 8px 10px !default; // In pixels because image dimensions
$custom-select-indicator-color: $gray-800 !default; $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-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-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; $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 // Navs
$nav-link-padding-y: .5rem !default; $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-link-disabled-color: $gray-600 !default;
$nav-tabs-border-color: $gray-300 !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-font-size: $font-size-lg !default;
$navbar-toggler-border-radius: $btn-border-radius !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-color: rgba($white, .5) !default;
$navbar-dark-hover-color: rgba($white, .75) !default; $navbar-dark-hover-color: rgba($white, .75) !default;
$navbar-dark-active-color: $white !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-color: $gray-900 !default;
$dropdown-link-hover-color: darken($gray-900, 5%) !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-color: $component-active-color !default;
$dropdown-link-active-bg: $component-active-bg !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-y: .25rem !default;
$dropdown-item-padding-x: 1.5rem !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-bg: $white !default;
$pagination-disabled-border-color: $gray-300 !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 // Jumbotron
@ -1054,7 +1058,7 @@ $figure-caption-color: $gray-600 !default;
$breadcrumb-font-size: null !default; $breadcrumb-font-size: null !default;
$breadcrumb-padding-y: .75rem !default; $breadcrumb-padding-y: .75rem !default;
$breadcrumb-padding-x: 1rem !default; $breadcrumb-padding-x: .5rem !default;
$breadcrumb-item-padding: .5rem !default; $breadcrumb-item-padding: .5rem !default;
$breadcrumb-margin-bottom: 1rem !default; $breadcrumb-margin-bottom: 1rem !default;

View file

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

View file

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

View file

@ -64,6 +64,13 @@
color: color-yiq($color); color: color-yiq($color);
background-color: rgba($color, $form-feedback-tooltip-opacity); background-color: rgba($color, $form-feedback-tooltip-opacity);
@include border-radius($form-feedback-tooltip-border-radius); @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) { @include form-validation-state-selector($state) {
@ -108,7 +115,7 @@
@if $enable-validation-icons { @if $enable-validation-icons {
padding-right: $custom-select-feedback-icon-padding-right; 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 { &:focus {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
// Materia 4.5.3 // Materia 4.6.0
// Bootswatch // Bootswatch
@ -287,7 +287,7 @@ select,
select.form-control { select.form-control {
appearance: none; appearance: none;
padding: .5rem 0; 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-size: 8px 4px;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: right center; background-position: right center;
@ -309,7 +309,7 @@ select.form-control {
&:focus { &:focus {
box-shadow: inset 0 -2px 0 $primary; 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] { &[multiple] {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
// Sandstone 4.5.3 // Sandstone 4.6.0
// Bootswatch // Bootswatch
// //
@ -51,7 +51,7 @@ $link-color: $success !default;
// stylelint-disable-next-line value-keyword-case // 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-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; $headings-font-weight: 400 !default;
// Dropdowns // Dropdowns
@ -64,7 +64,7 @@ $dropdown-link-active-bg: $dropdown-link-hover-bg !default;
// Navs // Navs
$nav-link-padding-x: .9rem !default; $nav-link-padding-x: 1.25rem !default;
$nav-link-disabled-color: $gray-300 !default; $nav-link-disabled-color: $gray-300 !default;
$nav-tabs-border-color: $gray-300 !default; $nav-tabs-border-color: $gray-300 !default;
$nav-tabs-link-hover-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 // Bootswatch

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
// Spacelab 4.5.3 // Spacelab 4.6.0
// Bootswatch // Bootswatch
// //
@ -51,14 +51,16 @@ $link-color: $info !default;
// stylelint-disable-next-line value-keyword-case // 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-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; $headings-color: $gray-900 !default;
// Navbar // Navbar
$navbar-padding-y: .7rem !default;
$navbar-dark-color: rgba($white, .75) !default; $navbar-dark-color: rgba($white, .75) !default;
$navbar-dark-hover-color: $white !default; $navbar-dark-hover-color: $white !default;
$navbar-light-color: rgba($black, .4) !default; $navbar-light-color: rgba($black, .4) !default;
$navbar-light-hover-color: $info !default; $navbar-light-hover-color: $info !default;
$navbar-light-active-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 // Bootswatch

View file

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

View file

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

View file

@ -1,4 +1,4 @@
// United 4.5.3 // United 4.6.0
// Bootswatch // Bootswatch
// //
@ -34,7 +34,7 @@ $success: $green !default;
$info: $cyan !default; $info: $cyan !default;
$warning: $yellow !default; $warning: $yellow !default;
$danger: $red !default; $danger: $red !default;
$light: $gray-200 !default; $light: $gray-100 !default;
$dark: $purple !default; $dark: $purple !default;
$yiq-contrasted-threshold: 200 !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-bg: $dark !default;
$table-dark-border-color: darken($dark, 5%) !default; $table-dark-border-color: darken($dark, 5%) !default;
// Navbar // 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 // Bootswatch

View file

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

View file

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

View file

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

View file

@ -54,7 +54,14 @@
{{ ipv4 }} | {{ ipv4 }} |
{% endfor %} {% endfor %}
{% 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> </div>
{% if user_quota_msg %} {% if user_quota_msg %}
<div class="alert alert-warning fade show"> <div class="alert alert-warning fade show">
@ -71,13 +78,13 @@
<ul class="nav nav-pills" id="navbtn" aria-label="Instance actions"> <ul class="nav nav-pills" id="navbtn" aria-label="Instance actions">
<li class="nav-item"> <li class="nav-item">
<a href="#power" class="nav-link action-button active" aria-controls="power" role="tab" data-toggle="tab"> <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" %} {% trans "Power" %}
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a href="#access" class="nav-link action-button" aria-controls="access" role="tab" data-toggle="tab"> <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" %} {% trans "Access" %}
</a> </a>
</li> </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'); var btnsect = $('#navbtn>li>a');
$(btnsect).each(function () { $(btnsect).each(function () {
if ($(this).attr('href') === '#settings') { if ($(this).attr('href') === '#settings') {
@ -715,4 +722,22 @@
}); });
} }
</script> </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 %} {% endblock %}

View file

@ -1,168 +1,166 @@
{% load i18n %} {% load i18n %}
<div role="tabpanel" class="tab-pane" id="access" aria-label="Instance access options"> <div role="tabpanel" class="tab-pane" id="access" aria-label="Instance access options">
<div role="tabpanel"> <!-- Nav tabs -->
<!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist">
<ul class="nav nav-tabs" role="tablist"> <li class="nav-item">
<li class="nav-item"> <a class="nav-link text-secondary active" href="#vnconsole" aria-controls="vnconsole" role="tab"
<a class="nav-link text-secondary active" href="#vnconsole" aria-controls="vnconsole" role="tab" data-toggle="tab">
data-toggle="tab"> {% trans "Console" %}
{% trans "Console" %} </a>
</a> </li>
</li> {% if app_settings.SHOW_ACCESS_ROOT_PASSWORD == 'True' %}
{% if app_settings.SHOW_ACCESS_ROOT_PASSWORD == 'True' %} <li class="nav-item">
<li class="nav-item"> <a class="nav-link text-secondary" href="#rootpasswd" aria-controls="rootpasswd" role="tab"
<a class="nav-link text-secondary" href="#rootpasswd" aria-controls="rootpasswd" role="tab" data-toggle="tab">
data-toggle="tab"> {% trans "Root Password" %}
{% trans "Root Password" %} </a>
</a> </li>
</li> {% endif %}
{% endif %} {% if app_settings.SHOW_ACCESS_SSH_KEYS == 'True' %}
{% if app_settings.SHOW_ACCESS_SSH_KEYS == 'True' %} <li class="nav-item">
<li class="nav-item"> <a class="nav-link text-secondary" href="#sshkeys" aria-controls="sshkeys" role="tab" data-toggle="tab">
<a class="nav-link text-secondary" href="#sshkeys" aria-controls="sshkeys" role="tab" data-toggle="tab"> {% trans "SSH Keys" %}
{% trans "SSH Keys" %} </a>
</a> </li>
</li> {% endif %}
{% endif %} {% if instance.status == 1 %}
{% if instance.status == 1 %} <li class="nav-item">
<li class="nav-item"> <a class="nav-link text-secondary" href="#vdiconsole" aria-controls="vdiconsole" role="tab"
<a class="nav-link text-secondary" href="#vdiconsole" aria-controls="vdiconsole" role="tab" data-toggle="tab">
data-toggle="tab"> {% trans "VDI" %}
{% trans "VDI" %} </a>
</a> </li>
</li> {% endif %}
{% endif %} </ul>
</ul> <!-- Tab panes -->
<!-- Tab panes --> <div class="tab-content">
<div class="tab-content"> <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="vnconsole">
<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>{% 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>
</p> {% if instance.console_type == 'vnc' %}
{% if instance.console_type == 'vnc' %} <div class="ml-3 form-row">
<div class="ml-3 form-row"> <div class="custom-control custom-switch">
<div class="custom-control custom-switch"> <input class="custom-control-input" type="checkbox" name="scale"
<input class="custom-control-input" type="checkbox" name="scale" {% if app_settings.CONSOLE_SCALE == 'True' %} checked {% endif %}
{% if app_settings.CONSOLE_SCALE == 'True' %} checked {% endif %} id="scale">
id="scale"> <label class="custom-control-label font-weight-bold" for="scale">{% trans "Scale" %}</label>
<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>
</div> </div>
</div> </div>
{% else %} <div class="ml-3 form-row">
<button class="btn btn-lg btn-success float-right disabled">{% trans "Console" %}</button> <div class="custom-control custom-switch">
{% endif %} <input class="custom-control-input" type="checkbox" name="view_only"
<div class="clearfix"></div> {% if app_settings.CONSOLE_VIEW_ONLY == 'True' %} checked {% endif %}
</div> id="view_only">
{% if app_settings.SHOW_ACCESS_ROOT_PASSWORD == 'True' %} <label class="custom-control-label font-weight-bold" for="view_only">{% trans "View Only" %}</label>
<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> </div>
{% if instance.status == 5 %} </div>
<input type="submit" class="btn btn-lg btn-success float-right" name="rootpasswd" <div class="ml-3 form-row">
value="{% trans "Reset Root Password" %}"> <div class="custom-control custom-switch">
{% else %} <input class="custom-control-input" type="checkbox" name="resize_session"
<button {% if app_settings.CONSOLE_RESIZE_SESSION == 'True' %} checked {% endif %}
class="btn btn-lg btn-success float-right disabled">{% trans "Reset Root Password" %}</button> id="resize_session">
{% endif %} <label class="custom-control-label font-weight-bold" for="resize_session">{% trans "Resize Session" %}</label>
</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>
{% if instance.status == 5 %} </div>
<input type="submit" class="btn btn-lg btn-success float-right" name="addpublickey" <div class="ml-3 form-row">
value="{% trans "Add Public Key" %}"> <div class="custom-control custom-switch">
{% else %} <input class="custom-control-input" type="checkbox" name="clip_viewport"
<button class="btn btn-lg btn-success float-right disabled">{% trans "Add Public Key" %}</button> {% if app_settings.CONSOLE_CLIP_VIEWPORT == 'True' %} checked {% endif %}
{% endif %} id="clip_viewport">
</form> <label class="custom-control-label font-weight-bold" for="clip_viewport">{% trans "View Clipboard" %}</label>
<div class="clearfix"></div> </div>
</div> </div>
{% endif %} {% endif %}
{% if instance.status == 1 %} {% if instance.status == 1 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vdiconsole"> <!-- Split button -->
<p>{% trans "This action opens a remote viewer with a connection to the console of the instance." %}</p> <div class="btn-group float-right">
<div class="input-group"> <button type="button" id="consoleBtnGroup" class="btn btn-lg btn-success"
<input type="text" class="input-lg disabled form-control" disabled id="vdi_url_input" /> onclick="open_console('lite')">{% trans 'Console' %}</button>
<span class="input-group-append"> <button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split"
<a href="#" class="btn btn-success" id="vdi_url">{% trans "VDI" %}</a> data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
</span> <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> </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> </div>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Console" %}</button>
{% endif %} {% endif %}
<div class="clearfix"></div>
</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>
</div> </div>
{% block script %} {% block script %}

View file

@ -1,29 +1,27 @@
{% load i18n %} {% load i18n %}
<div role="tabpanel" class="tab-pane" id="undefine"> <div role="tabpanel" class="tab-pane" id="undefine">
<div role="tabpanel"> <!-- Nav tabs -->
<!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist" aria-label="Instance destroy menu">
<ul class="nav nav-tabs" role="tablist" aria-label="Instance destroy menu"> <li class="nav-item">
<li class="nav-item"> <a class="nav-link active" href="#destroy" aria-controls="destroy" role="tab" data-toggle="tab">
<a class="nav-link active" href="#destroy" aria-controls="destroy" role="tab" data-toggle="tab"> {% trans "Destroy Instance" %}
{% trans "Destroy Instance" %} </a>
</a> </li>
</li> </ul>
</ul> <!-- Tab panes -->
<!-- Tab panes --> <div class="tab-content">
<div class="tab-content"> <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy"> <p>{% trans 'This action starts remove instance process' %}</p>
<p>{% trans 'This action starts remove instance process' %}</p> {% if request.user.is_superuser or userinstance.is_delete %}
{% if request.user.is_superuser or userinstance.is_delete %} {% if instance.status == 3 %}
{% if instance.status == 3 %} <a class="btn btn-lg btn-success disabled float-right">{% trans "Destroy" %}</a>
<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 %}
{% else %} {% 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 %} {% endif %}
<div class="clearfix"></div> {% else %}
</div> <button class="btn btn-lg btn-success disabled float-right" name="delete">{% trans "Destroy" %}</button>
{% endif %}
<div class="clearfix"></div>
</div> </div>
</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 %} {% load i18n %}
<div role="tabpanel" class="tab-pane active" id="power"> <div role="tabpanel" class="tab-pane active" id="power">
<div role="tabpanel"> <!-- Nav tabs -->
<!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist" aria-label="Instance power actions">
<ul class="nav nav-tabs" role="tablist" aria-label="Instance power actions"> {% if instance.status == 1 %}
{% 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"> <li class="nav-item">
<a class="nav-link text-secondary active" href="#poweroff" aria-controls="poweroff" role="tab" data-toggle="tab"> <a class="nav-link text-secondary" href="#suspend" aria-controls="suspend" role="tab" data-toggle="tab">
{% trans "Power Off" %} {% trans "Suspend" %}
</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> </a>
</li> </li>
{% endif %} {% endif %}
</ul> {% endif %}
<!-- Tab panes --> {% if instance.status == 3 %}
<div class="tab-content"> {% if request.user.is_superuser %}
{% if instance.status == 1 %} <li class="nav-item">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="poweroff"> <a class="nav-link text-secondary" href="#resume" aria-controls="resume" role="tab" data-toggle="tab">
<p>{% trans "This action sends an ACPI shutdown signal to the instance." %}</p> {% trans "Resume" %}
<form action="{% url 'instances:poweroff' instance.id %}" method="post" role="form" aria-label0="Power off instance form"> </a>
{% csrf_token %} </li>
<input type="submit" name="poweroff" class="btn btn-lg btn-success float-right" value="{% trans "Power Off" %}"> <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> <div class="clearfix"></div>
</form> </form>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="powercycle"> {% endif %}
<p>{% trans "This action forcibly powers off and start the instance and may cause data corruption." %}</p> {% endif %}
<form action="{% url 'instances:powercycle' instance.id %}" method="post" role="form" aria-label="Power cycle instance form">{% csrf_token %} {% if instance.status == 3 %}
<input type="submit" name="powercycle" class="btn btn-lg btn-success float-right" value="{% trans "Power Cycle" %}"> {% 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> <div class="clearfix"></div>
</form> </form>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="powerforce"> <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> <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"> <form action="{% url 'instances:force_off' instance.id %}" method="post" role="form" aria-label="Force to shutdown form">{% csrf_token %}
{% csrf_token %}
<input type="submit" name="powerforce" class="btn btn-lg btn-success float-right" value="{% trans "Force Off" %}"> <input type="submit" name="powerforce" class="btn btn-lg btn-success float-right" value="{% trans "Force Off" %}">
<div class="clearfix"></div> <div class="clearfix"></div>
</form> </form>
</div> </div>
{% if request.user.is_superuser %} {% else %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="suspend"> <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<p>{% trans "This action suspends the instance." %}</p> <p>{% trans "Administrator blocked your instance." %}</p>
<form action="{% url 'instances:suspend' instance.id %}" method="post" role="form" aria-label="Suspend instance form">{% csrf_token %} <form action="{% url 'instances:resume' instance.id %}" method="post" role="form" aria-label="Resume instance form">{% csrf_token %}
<input type="submit" name="suspend" class="btn btn-lg btn-success float-right" value="{% trans "Suspend" %}"> <button class="btn btn-lg btn-success disabled float-right">{% trans "Resume" %}</button>
<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 %}
<div class="clearfix"></div> <div class="clearfix"></div>
</form> </form>
</div> </div>
{% endif %} {% 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>
</div> </div>

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,99 +1,97 @@
{% load i18n %} {% load i18n %}
<div role="tabpanel" class="tab-pane" id="graphics"> <div role="tabpanel" class="tab-pane" id="graphics">
<div role="tabpanel"> <!-- Nav tabs -->
<!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist" aria-label="Instance graphs and logs menu">
<ul class="nav nav-tabs" role="tablist" aria-label="Instance graphs and logs menu"> <li class="nav-item">
<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">
<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" %}
{% trans "Real Time" %} </a>
</a> </li>
</li> <li class="nav-item">
<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 }}");'>
<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" %}
{% trans "Logs" %} </a>
</a> </li>
</li> </ul>
</ul> <!-- Tab panes -->
<!-- Tab panes --> <div class="tab-content">
<div class="tab-content"> <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="graphs">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="graphs"> <div class="mb-1 card border-success">
<div class="mb-1 card border-success"> <div class="card-header">
<div class="card-header"> <h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
<h5 class="card-title"><i class="fa fa-long-arrow-right"></i> {% trans "CPU Usage" %}</h5>
{% trans "CPU Usage" %}</h5> </div>
</div> <div class="card-body">
<div class="card-body"> <div class="flot-chart">
<div class="flot-chart"> <div class="flot-chart-content" id="flot-moving-line-chart">
<div class="flot-chart-content" id="flot-moving-line-chart"> <canvas id="cpuChart" width="735" height="160"></canvas>
<canvas id="cpuChart" width="735" height="160"></canvas>
</div>
</div> </div>
</div> </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>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="logs"> <div class="mb-1 card border-danger">
<div class="table-responsive"> <div class="card-header">
<table class="table table-striped sortable-theme-bootstrap" id="logs_table" data-sortable> <h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
<thead> {% trans "Memory Usage" %}</h5>
<tr> </div>
<th scope="col">{% trans "Date" %}</th> <div class="card-body">
<th scope="col">{% trans "User" %}</th> <div class="flot-chart">
<th scope="col">{% trans "Message" %}</th> <div class="flot-chart-content" id="flot-moving-line-chart">
</tr> <canvas id="memChart" width="735" height="160"></canvas>
</thead> </div>
<tbody class="searchable"> </div>
<tr>
<td colspan="3"><i>{% trans 'None' %}...</i></td>
</tr>
</tbody>
</table>
</div> </div>
<div class="clearfix"></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>
<div class="clearfix"></div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -20,6 +20,7 @@ urlpatterns = [
path('<int:pk>/migrate/', views.migrate, name='migrate'), path('<int:pk>/migrate/', views.migrate, name='migrate'),
path('<int:pk>/status/', views.status, name='status'), path('<int:pk>/status/', views.status, name='status'),
path('<int:pk>/stats/', views.stats, name='stats'), 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>/rootpasswd/', views.set_root_pass, name='rootpasswd'),
path('<int:pk>/add_public_key/', views.add_public_key, name='add_public_key'), 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'), 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: if compute.status is True:
domains = compute.proxy.wvm.listAllDomains() domains = compute.proxy.wvm.listAllDomains()
domain_names = [d.name() for d in domains] domain_names = [d.name() for d in domains]
domain_uuids = [d.UUIDString() for d in domains]
# Delete instances that're not on host # Delete instances that're not on host
Instance.objects.filter(compute=compute).exclude(name__in=domain_names).delete() 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 # Create instances that're not in DB
names = Instance.objects.filter(compute=compute).values_list('name', flat=True) 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: for domain in domains:
if domain.name() not in names: if domain.name() not in names:
Instance(compute=compute, name=domain.name(), uuid=domain.UUIDString()).save() 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): def get_dhcp_mac_address(vname):

View file

@ -25,6 +25,7 @@ from logs.views import addlogmsg
from vrtManager import util from vrtManager import util
from vrtManager.create import wvmCreate from vrtManager.create import wvmCreate
from vrtManager.instance import wvmInstances from vrtManager.instance import wvmInstances
from vrtManager.interface import wvmInterface
from vrtManager.storage import wvmStorage from vrtManager.storage import wvmStorage
from vrtManager.util import randomPasswd from vrtManager.util import randomPasswd
@ -121,6 +122,7 @@ def instance(request, pk):
memory_host = instance.proxy.get_max_memory() memory_host = instance.proxy.get_max_memory()
bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine) bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine)
networks_host = sorted(instance.proxy.get_networks()) networks_host = sorted(instance.proxy.get_networks())
interfaces_host = sorted(instance.proxy.get_ifaces())
nwfilters_host = instance.proxy.get_nwfilters() nwfilters_host = instance.proxy.get_nwfilters()
storages_host = sorted(instance.proxy.get_storages(True)) storages_host = sorted(instance.proxy.get_storages(True))
net_models_host = instance.proxy.get_network_models() 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): def guess_mac_address(request, vname):
data = {"vname": vname} data = {"vname": vname}
@ -764,7 +771,7 @@ def revert_snapshot(request, pk):
msg = _("Successful revert snapshot: ") msg = _("Successful revert snapshot: ")
msg += snap_name msg += snap_name
messages.success(request, msg) 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) addlogmsg(request.user.username, instance.name, msg)
return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot") return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
@ -882,7 +889,7 @@ def set_video_model(request, pk):
instance = get_instance(request.user, pk) instance = get_instance(request.user, pk)
video_model = request.POST.get("video_model", "vga") video_model = request.POST.get("video_model", "vga")
instance.proxy.set_video_model(video_model) 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) addlogmsg(request.user.username, instance.name, msg)
return redirect(request.META.get("HTTP_REFERER") + "#options") 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)) (source, source_type) = utils.get_network_tuple(request.POST.get(post))
network_data[post] = source network_data[post] = source
network_data[post + "-type"] = source_type 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-"): elif post.startswith("net-"):
network_data[post] = request.POST.get(post, "") network_data[post] = request.POST.get(post, "")
@ -918,6 +935,16 @@ def add_network(request, pk):
nwfilter = request.POST.get("add-net-nwfilter") nwfilter = request.POST.get("add-net-nwfilter")
(source, source_type) = utils.get_network_tuple(request.POST.get("add-net-network")) (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) instance.proxy.add_network(mac, source, source_type, nwfilter=nwfilter)
msg = _("Add network: %(mac)s") % {"mac": mac} msg = _("Add network: %(mac)s") % {"mac": mac}
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)

View file

@ -29,7 +29,8 @@ def interfaces(request, compute_id):
netdevs = ["eth0", "eth1"] netdevs = ["eth0", "eth1"]
for iface in ifaces: 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 request.method == "POST":
if "create" in request.POST: if "create" in request.POST:

View file

@ -296,12 +296,12 @@
<button type="submit" class="btn btn-sm btn-primary" <button type="submit" class="btn btn-sm btn-primary"
name="modify_fixed_address" name="modify_fixed_address"
title="{% trans "Edit entry" %}" onclick="return confirm('{% trans "Are you sure?" %}')"> 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>
<button type="submit" class="btn btn-sm btn-danger" <button type="submit" class="btn btn-sm btn-danger"
name="delete_fixed_address" name="delete_fixed_address"
title="{% trans "Delete entry" %}" onclick="return confirm('{% trans "Are you sure?" %}')"> title="{% trans "Delete entry" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
<i class="fa fa-trash"></i> <span class="fa fa-trash"></span>
</button> </button>
</td> </td>
</form> </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): def get_networks(self):
""" """
:return: list of networks :return: list of host networks
""" """
virtnet = [] virtnet = []
for net in self.wvm.listNetworks(): for net in self.wvm.listNetworks():
@ -493,7 +493,7 @@ class wvmConnect(object):
def get_ifaces(self): def get_ifaces(self):
""" """
:return: list of network interfaces :return: list of host interfaces
""" """
interface = [] interface = []
for inface in self.wvm.listInterfaces(): for inface in self.wvm.listInterfaces():

View file

@ -1,3 +1,4 @@
import json
import os.path import os.path
import time import time
@ -20,6 +21,10 @@ try:
VIR_MIGRATE_POSTCOPY, VIR_MIGRATE_POSTCOPY,
) )
from libvirt import VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT from libvirt import VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT
from libvirt_qemu import (
qemuAgentCommand,
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT
)
except: except:
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE
@ -160,6 +165,35 @@ class wvmInstance(wvmConnect):
self._ip_cache = None self._ip_cache = None
self.instance = self.get_instance(vname) 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): def start(self):
self.instance.create() self.instance.create()
@ -1332,19 +1366,15 @@ class wvmInstance(wvmConnect):
return bridge_name return bridge_name
def add_network(self, mac_address, source, source_type="net", model="virtio", nwfilter=None): 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" interface_type = "network"
elif forward_mode == "": elif source_type == "bridge":
interface_type = "direct" interface_type = "bridge"
else: else:
if self.get_bridge_name(source, source_type) is None: interface_type = "direct"
interface_type = "network"
else: # network modes not handled: default is bridge
interface_type = "bridge"
xml_iface = f""" xml_iface = f"""
<interface type='{interface_type}'> <interface type='{interface_type}'>
@ -1352,13 +1382,11 @@ class wvmInstance(wvmConnect):
if interface_type == "network": if interface_type == "network":
xml_iface += f"""<source network='{source}'/>""" xml_iface += f"""<source network='{source}'/>"""
elif interface_type == "direct": elif interface_type == "direct":
if source_type == "net": xml_iface += f"""<source dev='{source}' mode='bridge'/>"""
xml_iface += f"""<source network='{source}' mode='bridge'/>""" elif interface_type == "bridge":
else: xml_iface += f"""<source bridge='{source}'/>"""
xml_iface += f"""<source dev='{source}' mode='bridge'/>"""
else: else:
bridge_name = self.get_bridge_name(source, source_type) raise libvirtError(f"'{interface_type}' is an unexpected interface type.")
xml_iface += f"""<source bridge='{bridge_name}'/>"""
xml_iface += f"""<model type='{model}'/>""" xml_iface += f"""<model type='{model}'/>"""
if nwfilter: if nwfilter:
xml_iface += f"""<filterref filter='{nwfilter}'/>""" xml_iface += f"""<filterref filter='{nwfilter}'/>"""
@ -1382,8 +1410,35 @@ class wvmInstance(wvmConnect):
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG) self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5: if self.get_status() == 5:
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG) self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
return new_xml
return None
def change_network(self, network_data): 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) xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = ElementTree.fromstring(xml) tree = ElementTree.fromstring(xml)
for num, interface in enumerate(tree.findall("devices/interface")): for num, interface in enumerate(tree.findall("devices/interface")):

View file

@ -6,13 +6,6 @@ from vrtManager.connection import wvmConnect
class wvmInterfaces(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): def define_iface(self, xml, flag=0):
self.wvm.interfaceDefineXML(xml, flag) self.wvm.interfaceDefineXML(xml, flag)
@ -153,6 +146,12 @@ class wvmInterface(wvmConnect):
else: else:
return None 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): def stop_iface(self):
self.iface.destroy() self.iface.destroy()

View file

@ -146,10 +146,12 @@ configure_nginx () {
cp "$APP_PATH"/conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/ cp "$APP_PATH"/conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
if [ -n "$fqdn" ]; then 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 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 # Copy template supervisor service for gunicorn and novnc
echo " * Copying supervisor configuration" echo " * Copying supervisor configuration"
cp "$APP_PATH"/conf/supervisor/webvirtcloud.conf "$supervisor_conf_path"/"$supervisor_file_name" 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 () { create_user () {
@ -206,13 +209,18 @@ install_webvirtcloud () {
secret_key=$(generate_secret_key) secret_key=$(generate_secret_key)
echo "* Secret for Django generated: $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 #TODO escape SED delimiter in variables
sed -i "s|^\\(TIME_ZONE = \\).*|\\1$tzone|" "$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\'|" "$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|" "$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|" "$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\'|" "$APP_PATH/webvirtcloud/settings.py" sed -i "s|^\\(WS_HOST = \\).*|\\1\'$novncd_host_escape\'|" "$APP_PATH/webvirtcloud/settings.py"
echo "* Activate virtual environment." echo "* Activate virtual environment."
activate_python_environment activate_python_environment
@ -455,7 +463,7 @@ case $distro in
log "yum -y install wget epel-release" log "yum -y install wget epel-release"
echo "* Installing OS requirements." 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 install_packages
set_hosts set_hosts