mirror of
				https://github.com/retspen/webvirtcloud
				synced 2025-07-31 12:41:08 +00:00 
			
		
		
		
	
						commit
						3d3069d94d
					
				
					 69 changed files with 5011 additions and 4127 deletions
				
			
		
							
								
								
									
										2
									
								
								.github/workflows/linter.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/linter.yml
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -6,7 +6,7 @@ name: linter
 | 
			
		|||
# events but only for the master branch
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
    branches: [ '*' ]
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -316,6 +316,23 @@ pip3 install -U -r conf/requirements.txt
 | 
			
		|||
python3 manage.py migrate
 | 
			
		||||
sudo service supervisor restart
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Running tests
 | 
			
		||||
Server on which tests will be performed must have libvirt up and running.
 | 
			
		||||
It must not contain vms.
 | 
			
		||||
It must have `default` storage which not contain any disk images.
 | 
			
		||||
It must have `default` network which must be on.
 | 
			
		||||
Setup venv
 | 
			
		||||
```bash
 | 
			
		||||
python -m venv venv
 | 
			
		||||
source venv/bin/activate
 | 
			
		||||
pip install -r conf/requirements.txt
 | 
			
		||||
```
 | 
			
		||||
Run tests
 | 
			
		||||
```bash
 | 
			
		||||
python menage.py test
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Screenshots
 | 
			
		||||
Instance Detail:
 | 
			
		||||
<img src="doc/images/instance.PNG" width="95%" align="center"/>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,9 @@ from django.utils.translation import ugettext_lazy as _
 | 
			
		|||
 | 
			
		||||
from instances.models import Instance
 | 
			
		||||
 | 
			
		||||
class UserInstanceManager(models.Manager):
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        return super().get_queryset().select_related('instance', 'user')
 | 
			
		||||
 | 
			
		||||
class UserInstance(models.Model):
 | 
			
		||||
    user = models.ForeignKey(User, on_delete=models.CASCADE)
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +17,8 @@ class UserInstance(models.Model):
 | 
			
		|||
    is_delete = models.BooleanField(default=False)
 | 
			
		||||
    is_vnc = models.BooleanField(default=False)
 | 
			
		||||
 | 
			
		||||
    objects = UserInstanceManager()
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return _('Instance "%(inst)s" of user %(user)s') % {'inst': self.instance, 'user': self.user}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@
 | 
			
		|||
                    {% for inst in user_insts %}
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <td>{{ forloop.counter }}</td>
 | 
			
		||||
                            <td><a href="{% url 'instances:instance' inst.instance.compute.id inst.instance.name %}">{{ inst.instance.name }}</a></td>
 | 
			
		||||
                            <td><a href="{% url 'instances:instance' inst.instance.id %}">{{ inst.instance.name }}</a></td>
 | 
			
		||||
                            <td>{{ inst.is_vnc }}</td>
 | 
			
		||||
                            <td>{{ inst.is_change }}</td>
 | 
			
		||||
                            <td>{{ inst.is_delete }}</td>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,7 +55,7 @@
 | 
			
		|||
                                    </td>
 | 
			
		||||
                                    <td>{% if user.is_staff %}<span class="fa fa-check"></span>{% endif %}</td>
 | 
			
		||||
                                    <td>{% if user.is_superuser %}<span class="fa fa-check"></span>{% endif %}</td>
 | 
			
		||||
                                    <td>{% if user.userattributes.can_clone_instances %}<span class="fa fa-check"></span>{% endif %}</td>
 | 
			
		||||
                                    <td>{% if perms.instances.clone_instances %}<span class="fa fa-check"></span>{% endif %}</td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                            </tbody>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -126,7 +126,11 @@ def user_instance_delete(request, pk):
 | 
			
		|||
    if request.method == 'POST':
 | 
			
		||||
        user = user_instance.user
 | 
			
		||||
        user_instance.delete()
 | 
			
		||||
        return redirect(reverse('account', args=[user.id]))
 | 
			
		||||
        next = request.GET.get('next', None)
 | 
			
		||||
        if next:
 | 
			
		||||
            return redirect(next)
 | 
			
		||||
        else:
 | 
			
		||||
            return redirect(reverse('account', args=[user.id]))
 | 
			
		||||
 | 
			
		||||
    return render(
 | 
			
		||||
        request,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,10 @@
 | 
			
		|||
from django.db.models import CharField, IntegerField, Model
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from libvirt import virConnect
 | 
			
		||||
 | 
			
		||||
from vrtManager.connection import connection_manager
 | 
			
		||||
from vrtManager.hostdetails import wvmHostDetails
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Compute(Model):
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +17,45 @@ class Compute(Model):
 | 
			
		|||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def status(self):
 | 
			
		||||
        return connection_manager.host_is_up(self.type, self.hostname)
 | 
			
		||||
        # return connection_manager.host_is_up(self.type, self.hostname)
 | 
			
		||||
        # TODO: looks like socket has problems connecting via VPN
 | 
			
		||||
        if isinstance(self.connection, virConnect):
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            return self.connection
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def connection(self):
 | 
			
		||||
        try:
 | 
			
		||||
            return connection_manager.get_connection(
 | 
			
		||||
                self.hostname,
 | 
			
		||||
                self.login,
 | 
			
		||||
                self.password,
 | 
			
		||||
                self.type,
 | 
			
		||||
            )
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            return e
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def proxy(self):
 | 
			
		||||
        return wvmHostDetails(
 | 
			
		||||
            self.hostname,
 | 
			
		||||
            self.login,
 | 
			
		||||
            self.password,
 | 
			
		||||
            self.type,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def cpu_count(self):
 | 
			
		||||
        return self.proxy.get_node_info()[3]
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def ram_size(self):
 | 
			
		||||
        return self.proxy.get_node_info()[2]
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def ram_usage(self):
 | 
			
		||||
        return self.proxy.get_memory_usage()['percent']
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										121
									
								
								computes/templates/computes/instances.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								computes/templates/computes/instances.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
{% extends "base.html" %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load staticfiles %}
 | 
			
		||||
{% load icons %}
 | 
			
		||||
{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
 | 
			
		||||
{% block style %}
 | 
			
		||||
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% block page_header %}{{ compute.name }}{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block page_header_extra %}
 | 
			
		||||
<a href="{% url 'instances:create_instance_select_type' compute.id %}"
 | 
			
		||||
    class="btn btn-success btn-header float-right">
 | 
			
		||||
    {% icon 'plus' %}
 | 
			
		||||
</a>
 | 
			
		||||
{% if instances %}
 | 
			
		||||
<div class="float-right search">
 | 
			
		||||
    <input id="filter" class="form-control" type="text" placeholder="{% trans 'Search' %}">
 | 
			
		||||
</div>
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% endblock page_header_extra %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
<div class="row">
 | 
			
		||||
    <div class="col-lg-12">
 | 
			
		||||
        <nav aria-label="breadcrumb">
 | 
			
		||||
            <ol class="breadcrumb bg-light shadow-sm">
 | 
			
		||||
                <li class="breadcrumb-item active">
 | 
			
		||||
                    <a href="{% url 'overview' compute.id %}">{% icon 'dashboard' %} {% trans "Overview" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="breadcrumb-item">
 | 
			
		||||
                    <span class="font-weight-bold">{% icon 'server' %} {% trans "Instances" %}</span>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="breadcrumb-item">
 | 
			
		||||
                    <a href="{% url 'storages' compute.id %}">{% icon 'hdd-o' %} {% trans "Storages" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="breadcrumb-item">
 | 
			
		||||
                    <a href="{% url 'networks' compute.id %}">{% icon 'sitemap' %} {% trans "Networks" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="breadcrumb-item">
 | 
			
		||||
                    <a href="{% url 'interfaces' compute.id %}">{% icon 'wifi' %} {% trans "Interfaces" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="breadcrumb-item">
 | 
			
		||||
                    <a href="{% url 'nwfilters' compute.id %}">{% icon 'filter' %} {% trans "NWFilters" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="breadcrumb-item">
 | 
			
		||||
                    <a href="{% url 'secrets' compute.id %}">{% icon 'key' %} {% trans "Secrets" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
            </ol>
 | 
			
		||||
        </nav>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="row">
 | 
			
		||||
    <div class="col-lg-12">
 | 
			
		||||
        {% if not instances %}
 | 
			
		||||
            <div class="alert alert-warning alert-dismissable fade show">
 | 
			
		||||
                {% icon 'exclamation-triangle' %} <strong>{% trans "Warning" %}:</strong>
 | 
			
		||||
                {% trans "Hypervisor doesn't have any Instances" %}
 | 
			
		||||
                <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        {% else %}
 | 
			
		||||
            <table class="table table-hover sortable-theme-bootstrap" data-sortable>
 | 
			
		||||
                <thead>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <th scope="col">{% trans 'Name' %}<br>{% trans 'Description' %}</th>
 | 
			
		||||
                        <th scope="col">{% trans 'User' %}</th>
 | 
			
		||||
                        <th scope="col">{% trans 'Status' %}</th>
 | 
			
		||||
                        <th scope="col">{% trans 'VCPU' %}</th>
 | 
			
		||||
                        <th scope="col">{% trans 'Memory' %}</th>
 | 
			
		||||
                        <th scope="col" data-sortable="false">{% trans 'Actions' %}</th>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                </thead>
 | 
			
		||||
                <tbody class="searchable">
 | 
			
		||||
                    {% for instance in instances  %}
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <a class="text-secondary" href="{% url 'instances:instance' instance.id %}">
 | 
			
		||||
                                {{ instance.name }}
 | 
			
		||||
                            </a>
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <em>
 | 
			
		||||
                                {% if instance.userinstance_set.all.count > 0 %}
 | 
			
		||||
                                {{ instance.userinstance_set.all.0.user }}
 | 
			
		||||
                                {% if instance.userinstance_set.all.count > 1 %}
 | 
			
		||||
                                (+{{ instance.userinstance_set.all.count|add:"-1" }})
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            </em>
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {% if instance.proxy.instance.info.0 == 1 %}<span
 | 
			
		||||
                                class="text-success">{% trans "Active" %}</span>{% endif %}
 | 
			
		||||
                            {% if instance.proxy.instance.info.0 == 5 %}<span
 | 
			
		||||
                                class="text-danger">{% trans "Off" %}</span>{% endif %}
 | 
			
		||||
                            {% if instance.proxy.instance.info.0 == 3 %}<span
 | 
			
		||||
                                class="text-warning">{% trans "Suspended" %}</span>{% endif %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>{{ instance.proxy.instance.info.3 }}</td>
 | 
			
		||||
                        <td>{% widthratio instance.proxy.instance.info.1 1024 1 %} MiB</td>
 | 
			
		||||
                        <td class="text-nowrap">
 | 
			
		||||
                            {% include 'instance_actions.html' %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </tbody>
 | 
			
		||||
            </table>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% block script %}
 | 
			
		||||
<script src="{% static "js/sortable.min.js" %}"></script>
 | 
			
		||||
<script>
 | 
			
		||||
    function open_console(uuid) {
 | 
			
		||||
        window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=685");
 | 
			
		||||
    }
 | 
			
		||||
</script>
 | 
			
		||||
<script src="{% static 'js/filter-table.js' %}"></script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +83,9 @@ class ComputesTestCase(TestCase):
 | 
			
		|||
        response = self.client.get(reverse('storages', args=[1]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
    def test_storage(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def test_default_storage_volumes(self):
 | 
			
		||||
        response = self.client.get(reverse('volumes', kwargs={'compute_id': 1, 'pool': 'default'}))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
| 
						 | 
				
			
			@ -115,9 +118,9 @@ class ComputesTestCase(TestCase):
 | 
			
		|||
        response = self.client.get(reverse('secrets', args=[1]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
    def test_create_instance_select_type(self):
 | 
			
		||||
        response = self.client.get(reverse('create_instance_select_type', args=[1]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
    # def test_create_instance_select_type(self):
 | 
			
		||||
    #     response = self.client.get(reverse('create_instance_select_type', args=[1]))
 | 
			
		||||
    #     self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
    # TODO: create_instance
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,14 @@
 | 
			
		|||
from django.urls import path, include
 | 
			
		||||
from secrets.views import secrets
 | 
			
		||||
 | 
			
		||||
from . import views
 | 
			
		||||
from . import forms
 | 
			
		||||
from create.views import create_instance, create_instance_select_type
 | 
			
		||||
from instances.views import instances
 | 
			
		||||
from django.urls import include, path
 | 
			
		||||
 | 
			
		||||
# from instances.views import create_instance, create_instance_select_type
 | 
			
		||||
from interfaces.views import interface, interfaces
 | 
			
		||||
from networks.views import network, networks
 | 
			
		||||
from nwfilters.views import nwfilter, nwfilters
 | 
			
		||||
from secrets.views import secrets
 | 
			
		||||
from storages.views import get_volumes, storage, storages
 | 
			
		||||
from storages.views import create_volume, get_volumes, storage, storages
 | 
			
		||||
 | 
			
		||||
from . import forms, views
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('', views.computes, name='computes'),
 | 
			
		||||
| 
						 | 
				
			
			@ -23,10 +23,11 @@ urlpatterns = [
 | 
			
		|||
            path('update/', views.compute_update, name='compute_update'),
 | 
			
		||||
            path('delete/', views.compute_delete, name='compute_delete'),
 | 
			
		||||
            path('statistics', views.compute_graph, name='compute_graph'),
 | 
			
		||||
            path('instances/', instances, name='instances'),
 | 
			
		||||
            path('instances/', views.instances, name='instances'),
 | 
			
		||||
            path('storages/', storages, name='storages'),
 | 
			
		||||
            path('storage/<str:pool>/volumes', get_volumes, name='volumes'),
 | 
			
		||||
            path('storage/<str:pool>/volumes/', get_volumes, name='volumes'),
 | 
			
		||||
            path('storage/<str:pool>/', storage, name='storage'),
 | 
			
		||||
            path('storage/<str:pool>/create_volume/', create_volume, name='create_volume'),
 | 
			
		||||
            path('networks/', networks, name='networks'),
 | 
			
		||||
            path('network/<str:pool>/', network, name='network'),
 | 
			
		||||
            path('interfaces/', interfaces, name='interfaces'),
 | 
			
		||||
| 
						 | 
				
			
			@ -34,8 +35,8 @@ urlpatterns = [
 | 
			
		|||
            path('nwfilters/', nwfilters, name='nwfilters'),
 | 
			
		||||
            path('nwfilter/<str:nwfltr>/', nwfilter, name='nwfilter'),
 | 
			
		||||
            path('secrets/', secrets, name='secrets'),
 | 
			
		||||
            path('create/', create_instance_select_type, name='create_instance_select_type'),
 | 
			
		||||
            path('create/archs/<str:arch>/machines/<str:machine>/', create_instance, name='create_instance'),
 | 
			
		||||
            # path('create/', create_instance_select_type, name='create_instance_select_type'),
 | 
			
		||||
            # path('create/archs/<str:arch>/machines/<str:machine>/', create_instance, name='create_instance'),
 | 
			
		||||
            path('archs/<str:arch>/machines/', views.get_compute_machine_types, name='machines'),
 | 
			
		||||
            path(
 | 
			
		||||
                'archs/<str:arch>/machines/<str:machine>/disks/<str:disk>/buses/',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								computes/utils.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								computes/utils.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
from instances.models import Instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def refresh_instance_database(compute):
 | 
			
		||||
    domains = compute.proxy.wvm.listAllDomains()
 | 
			
		||||
    domain_names = [d.name() for d in domains]
 | 
			
		||||
    # Delete instances that're not on host from DB
 | 
			
		||||
    Instance.objects.filter(compute=compute).exclude(name__in=domain_names).delete()
 | 
			
		||||
    # Create instances that're on host but not in DB
 | 
			
		||||
    names = Instance.objects.filter(compute=compute).values_list('name', flat=True)
 | 
			
		||||
    for domain in domains:
 | 
			
		||||
        if domain.name() not in names:
 | 
			
		||||
            Instance(compute=compute, name=domain.name(), uuid=domain.UUIDString()).save()
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,8 @@ from instances.models import Instance
 | 
			
		|||
from vrtManager.connection import (CONN_SOCKET, CONN_SSH, CONN_TCP, CONN_TLS, connection_manager, wvmConnect)
 | 
			
		||||
from vrtManager.hostdetails import wvmHostDetails
 | 
			
		||||
 | 
			
		||||
from . import utils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@superuser_only
 | 
			
		||||
def computes(request):
 | 
			
		||||
| 
						 | 
				
			
			@ -30,36 +32,36 @@ def computes(request):
 | 
			
		|||
 | 
			
		||||
@superuser_only
 | 
			
		||||
def overview(request, compute_id):
 | 
			
		||||
    """
 | 
			
		||||
    :param request:
 | 
			
		||||
    :param compute_id:
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    error_messages = []
 | 
			
		||||
    compute = get_object_or_404(Compute, pk=compute_id)
 | 
			
		||||
    status = 'true' if connection_manager.host_is_up(compute.type, compute.hostname) is True else 'false'
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        conn = wvmHostDetails(
 | 
			
		||||
            compute.hostname,
 | 
			
		||||
            compute.login,
 | 
			
		||||
            compute.password,
 | 
			
		||||
            compute.type,
 | 
			
		||||
        )
 | 
			
		||||
        hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
 | 
			
		||||
        hypervisor = conn.get_hypervisors_domain_types()
 | 
			
		||||
        mem_usage = conn.get_memory_usage()
 | 
			
		||||
        emulator = conn.get_emulator(host_arch)
 | 
			
		||||
        version = conn.get_version()
 | 
			
		||||
        lib_version = conn.get_lib_version()
 | 
			
		||||
        conn.close()
 | 
			
		||||
    except libvirtError as lib_err:
 | 
			
		||||
        error_messages.append(lib_err)
 | 
			
		||||
    conn = wvmHostDetails(
 | 
			
		||||
        compute.hostname,
 | 
			
		||||
        compute.login,
 | 
			
		||||
        compute.password,
 | 
			
		||||
        compute.type,
 | 
			
		||||
    )
 | 
			
		||||
    hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
 | 
			
		||||
    hypervisor = conn.get_hypervisors_domain_types()
 | 
			
		||||
    mem_usage = conn.get_memory_usage()
 | 
			
		||||
    emulator = conn.get_emulator(host_arch)
 | 
			
		||||
    version = conn.get_version()
 | 
			
		||||
    lib_version = conn.get_lib_version()
 | 
			
		||||
    conn.close()
 | 
			
		||||
 | 
			
		||||
    return render(request, 'overview.html', locals())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@superuser_only
 | 
			
		||||
def instances(request, compute_id):
 | 
			
		||||
    compute = get_object_or_404(Compute, pk=compute_id)
 | 
			
		||||
 | 
			
		||||
    utils.refresh_instance_database(compute)
 | 
			
		||||
    instances = Instance.objects.filter(compute=compute).prefetch_related('userinstance_set')
 | 
			
		||||
 | 
			
		||||
    return render(request, 'computes/instances.html', {'compute': compute, 'instances': instances})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@superuser_only
 | 
			
		||||
def compute_create(request, FormClass):
 | 
			
		||||
    form = FormClass(request.POST or None)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,6 @@ import json
 | 
			
		|||
import guestfs
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PORT = 16510
 | 
			
		||||
ADDRESS = "0.0.0.0"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +19,8 @@ class MyTCPServer(socketserver.ThreadingTCPServer):
 | 
			
		|||
class MyTCPServerHandler(socketserver.BaseRequestHandler):
 | 
			
		||||
    def handle(self):
 | 
			
		||||
        # recive data
 | 
			
		||||
        data = json.loads(self.request.recv(1024).strip())
 | 
			
		||||
        d = self.request.recv(1024).strip()
 | 
			
		||||
        data = json.loads(d)
 | 
			
		||||
 | 
			
		||||
        # GuestFS
 | 
			
		||||
        gfs = guestfs.GuestFS(python_return_dict=True)
 | 
			
		||||
| 
						 | 
				
			
			@ -51,8 +51,8 @@ class MyTCPServerHandler(socketserver.BaseRequestHandler):
 | 
			
		|||
                    pass
 | 
			
		||||
            gfs.shutdown()
 | 
			
		||||
            gfs.close()
 | 
			
		||||
        except RuntimeError as err:
 | 
			
		||||
            self.request.sendall(json.dumps({'return': 'error', 'message': err}))
 | 
			
		||||
        except Exception as err:
 | 
			
		||||
            self.request.sendall(bytes(json.dumps({'return': 'error', 'message': str(err)}).encode()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,11 +26,13 @@ def console(request):
 | 
			
		|||
        host = int(temptoken[0])
 | 
			
		||||
        uuid = temptoken[1]
 | 
			
		||||
        instance = Instance.objects.get(compute_id=host, uuid=uuid)
 | 
			
		||||
        conn = wvmInstance(instance.compute.hostname,
 | 
			
		||||
                           instance.compute.login,
 | 
			
		||||
                           instance.compute.password,
 | 
			
		||||
                           instance.compute.type,
 | 
			
		||||
                           instance.name)
 | 
			
		||||
        conn = wvmInstance(
 | 
			
		||||
            instance.compute.hostname,
 | 
			
		||||
            instance.compute.login,
 | 
			
		||||
            instance.compute.password,
 | 
			
		||||
            instance.compute.type,
 | 
			
		||||
            instance.name,
 | 
			
		||||
        )
 | 
			
		||||
        console_type = conn.get_console_type()
 | 
			
		||||
        console_websocket_port = conn.get_console_websocket_port()
 | 
			
		||||
        console_passwd = conn.get_console_passwd()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,24 +0,0 @@
 | 
			
		|||
# Generated by Django 2.2.10 on 2020-01-28 07:01
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    initial = True
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='Flavor',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('label', models.CharField(max_length=12)),
 | 
			
		||||
                ('memory', models.IntegerField()),
 | 
			
		||||
                ('vcpu', models.IntegerField()),
 | 
			
		||||
                ('disk', models.IntegerField()),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,33 +0,0 @@
 | 
			
		|||
# Generated by Django 2.2.13 on 2020-06-15 06:37
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('create', '0002_addFlavors'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='flavor',
 | 
			
		||||
            name='disk',
 | 
			
		||||
            field=models.IntegerField(verbose_name='disk'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='flavor',
 | 
			
		||||
            name='label',
 | 
			
		||||
            field=models.CharField(max_length=12, verbose_name='label'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='flavor',
 | 
			
		||||
            name='memory',
 | 
			
		||||
            field=models.IntegerField(verbose_name='memory'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='flavor',
 | 
			
		||||
            name='vcpu',
 | 
			
		||||
            field=models.IntegerField(verbose_name='vcpu'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +0,0 @@
 | 
			
		|||
from django.db.models import Model, CharField, IntegerField
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Flavor(Model):
 | 
			
		||||
    label = CharField(_('label'), max_length=12)
 | 
			
		||||
    memory = IntegerField(_('memory'))
 | 
			
		||||
    vcpu = IntegerField(_('vcpu'))
 | 
			
		||||
    disk = IntegerField(_('disk'))
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
| 
						 | 
				
			
			@ -1,61 +0,0 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% if request.user.is_superuser %}
 | 
			
		||||
    <button href="#addFlavor" type="button" class="btn btn-success btn-header float-right" data-toggle="modal">
 | 
			
		||||
        <span class="fa fa-plus" aria-hidden="true"></span>
 | 
			
		||||
    </button>
 | 
			
		||||
 | 
			
		||||
    <!-- Modal Flavor -->
 | 
			
		||||
    <div class="modal fade" id="addFlavor" tabindex="-1" role="dialog" aria-labelledby="addFlavorLabel"
 | 
			
		||||
         aria-hidden="true">
 | 
			
		||||
        <div class="modal-dialog">
 | 
			
		||||
            <div class="modal-content">
 | 
			
		||||
                <div class="modal-header">
 | 
			
		||||
                    <h4 class="modal-title">{% trans "Add New Flavor" %}</h4>
 | 
			
		||||
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-body">
 | 
			
		||||
                    <form method="post" role="form" aria-label="Flavor selection form">{% csrf_token %}
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-3 col-form-label">{% trans "Name" %}</label>
 | 
			
		||||
                            <div class="col-sm-6">
 | 
			
		||||
                                <input type="text" name="label" class="form-control" placeholder="{% trans "Micro" %}" maxlength="20"
 | 
			
		||||
                                       required pattern="[a-zA-Z0-9]+">
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-3 col-form-label">{% trans "VCPU" %}</label>
 | 
			
		||||
                            <div class="col-sm-6">
 | 
			
		||||
                                <input type="text" class="form-control" name="vcpu" value="1" maxlength="1" required
 | 
			
		||||
                                       pattern="[0-9]">
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-3 col-form-label">{% trans "RAM" %}</label>
 | 
			
		||||
                            <div class="col-sm-6">
 | 
			
		||||
                                <input type="text" class="form-control" name="memory" value="512" maxlength="5" required
 | 
			
		||||
                                       pattern="[0-9]+">
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <label class="col-sm-1 col-form-label">{% trans "MB" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-3 col-form-label">{% trans "HDD" %}</label>
 | 
			
		||||
                            <div class="col-sm-6">
 | 
			
		||||
                                <input type="text" class="form-control" name="disk" value="10" maxlength="3" required
 | 
			
		||||
                                       pattern="[0-9]+">
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <label class="col-sm-1 col-form-label">{% trans "GB" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-footer">
 | 
			
		||||
                    <button type="button" class="btn btn-secondary" data-dismiss="modal">
 | 
			
		||||
                        {% trans "Close" %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <button type="submit" class="btn btn-primary" name="create_flavor">
 | 
			
		||||
                        {% trans "Add" %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                </div>
 | 
			
		||||
                    </form>
 | 
			
		||||
            </div> <!-- /.modal-content -->
 | 
			
		||||
        </div> <!-- /.modal-dialog -->
 | 
			
		||||
    </div> <!-- /.modal -->
 | 
			
		||||
{% endif %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +0,0 @@
 | 
			
		|||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
							
								
								
									
										304
									
								
								create/views.py
									
										
									
									
									
								
							
							
						
						
									
										304
									
								
								create/views.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,304 +0,0 @@
 | 
			
		|||
from django.contrib import messages
 | 
			
		||||
from django.shortcuts import render, get_object_or_404
 | 
			
		||||
from django.http import HttpResponseRedirect
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from libvirt import libvirtError
 | 
			
		||||
from admin.decorators import superuser_only
 | 
			
		||||
from computes.models import Compute
 | 
			
		||||
from create.models import Flavor
 | 
			
		||||
from create.forms import FlavorAddForm, NewVMForm
 | 
			
		||||
from appsettings.models import AppSettings
 | 
			
		||||
from instances.models import Instance
 | 
			
		||||
from vrtManager.create import wvmCreate
 | 
			
		||||
from vrtManager import util
 | 
			
		||||
from logs.views import addlogmsg
 | 
			
		||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@superuser_only
 | 
			
		||||
def create_instance_select_type(request, compute_id):
 | 
			
		||||
    """
 | 
			
		||||
    :param request:
 | 
			
		||||
    :param compute_id:
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
    conn = None
 | 
			
		||||
    error_messages = list()
 | 
			
		||||
    storages = list()
 | 
			
		||||
    networks = list()
 | 
			
		||||
    hypervisors = list()
 | 
			
		||||
    meta_prealloc = False
 | 
			
		||||
    compute = get_object_or_404(Compute, pk=compute_id)
 | 
			
		||||
    appsettings = AppSettings.objects.all()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
 | 
			
		||||
        instances = conn.get_instances()
 | 
			
		||||
        all_hypervisors = conn.get_hypervisors_machines()
 | 
			
		||||
        # Supported hypervisors by webvirtcloud: i686, x86_64(for now)
 | 
			
		||||
        supported_arch = ["x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"]
 | 
			
		||||
        hypervisors = [hpv for hpv in all_hypervisors.keys() if hpv in supported_arch]
 | 
			
		||||
        default_machine = appsettings.get(key="INSTANCE_MACHINE_DEFAULT_TYPE").value
 | 
			
		||||
        default_arch = appsettings.get(key="INSTANCE_ARCH_DEFAULT_TYPE").value
 | 
			
		||||
 | 
			
		||||
        if request.method == 'POST':
 | 
			
		||||
            if 'create_xml' in request.POST:
 | 
			
		||||
                xml = request.POST.get('dom_xml', '')
 | 
			
		||||
                try:
 | 
			
		||||
                    name = util.get_xml_path(xml, '/domain/name')
 | 
			
		||||
                except util.etree.Error as err:
 | 
			
		||||
                    name = None
 | 
			
		||||
                if name in instances:
 | 
			
		||||
                    error_msg = _("A virtual machine with this name already exists")
 | 
			
		||||
                    error_messages.append(error_msg)
 | 
			
		||||
                else:
 | 
			
		||||
                    try:
 | 
			
		||||
                        conn._defineXML(xml)
 | 
			
		||||
                        return HttpResponseRedirect(reverse('instances:instance', args=[compute_id, name]))
 | 
			
		||||
                    except libvirtError as lib_err:
 | 
			
		||||
                        error_messages.append(lib_err)
 | 
			
		||||
 | 
			
		||||
    except libvirtError as lib_err:
 | 
			
		||||
        error_messages.append(lib_err)
 | 
			
		||||
 | 
			
		||||
    return render(request, 'create_instance_w1.html', locals())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@superuser_only
 | 
			
		||||
def create_instance(request, compute_id, arch, machine):
 | 
			
		||||
    """
 | 
			
		||||
    :param request:
 | 
			
		||||
    :param compute_id:
 | 
			
		||||
    :param arch:
 | 
			
		||||
    :param machine:
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    conn = None
 | 
			
		||||
    error_messages = list()
 | 
			
		||||
    storages = list()
 | 
			
		||||
    networks = list()
 | 
			
		||||
    hypervisors = list()
 | 
			
		||||
    firmwares = list()
 | 
			
		||||
    meta_prealloc = False
 | 
			
		||||
    compute = get_object_or_404(Compute, pk=compute_id)
 | 
			
		||||
    flavors = Flavor.objects.filter().order_by('id')
 | 
			
		||||
    appsettings = AppSettings.objects.all()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
 | 
			
		||||
 | 
			
		||||
        default_firmware = appsettings.get(key="INSTANCE_FIRMWARE_DEFAULT_TYPE").value
 | 
			
		||||
        default_cpu_mode = appsettings.get(key="INSTANCE_CPU_DEFAULT_MODE").value
 | 
			
		||||
        instances = conn.get_instances()
 | 
			
		||||
        videos = conn.get_video_models(arch, machine)
 | 
			
		||||
        cache_modes = sorted(conn.get_cache_modes().items())
 | 
			
		||||
        default_cache = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_CACHE").value
 | 
			
		||||
        default_io = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_IO").value
 | 
			
		||||
        default_zeroes = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES").value
 | 
			
		||||
        default_discard = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_DISCARD").value
 | 
			
		||||
        default_disk_format = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_FORMAT").value
 | 
			
		||||
        default_disk_owner_uid = int(appsettings.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_UID").value)
 | 
			
		||||
        default_disk_owner_gid = int(appsettings.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_GID").value)
 | 
			
		||||
        default_scsi_disk_model = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER").value
 | 
			
		||||
        listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES
 | 
			
		||||
        mac_auto = util.randomMAC()
 | 
			
		||||
        disk_devices = conn.get_disk_device_types(arch, machine)
 | 
			
		||||
        disk_buses = conn.get_disk_bus_types(arch, machine)
 | 
			
		||||
        default_bus = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_BUS").value
 | 
			
		||||
        networks = sorted(conn.get_networks())
 | 
			
		||||
        nwfilters = conn.get_nwfilters()
 | 
			
		||||
        storages = sorted(conn.get_storages(only_actives=True))
 | 
			
		||||
        default_graphics = appsettings.get(key="QEMU_CONSOLE_DEFAULT_TYPE").value
 | 
			
		||||
 | 
			
		||||
        dom_caps = conn.get_dom_capabilities(arch, machine)
 | 
			
		||||
        caps = conn.get_capabilities(arch)
 | 
			
		||||
 | 
			
		||||
        virtio_support = conn.is_supports_virtio(arch, machine)
 | 
			
		||||
        hv_supports_uefi = conn.supports_uefi_xml(dom_caps["loader_enums"])
 | 
			
		||||
        # Add BIOS
 | 
			
		||||
        label = conn.label_for_firmware_path(arch, None)
 | 
			
		||||
        if label: firmwares.append(label)
 | 
			
		||||
        # Add UEFI
 | 
			
		||||
        loader_path = conn.find_uefi_path_for_arch(arch, dom_caps["loaders"])
 | 
			
		||||
        label = conn.label_for_firmware_path(arch, loader_path)
 | 
			
		||||
        if label: firmwares.append(label)
 | 
			
		||||
        firmwares = list(set(firmwares))
 | 
			
		||||
 | 
			
		||||
    except libvirtError as lib_err:
 | 
			
		||||
        error_messages.append(lib_err)
 | 
			
		||||
 | 
			
		||||
    if conn:
 | 
			
		||||
        if not storages:
 | 
			
		||||
            msg = _("You haven't defined any storage pools")
 | 
			
		||||
            error_messages.append(msg)
 | 
			
		||||
        if not networks:
 | 
			
		||||
            msg = _("You haven't defined any network pools")
 | 
			
		||||
            error_messages.append(msg)
 | 
			
		||||
 | 
			
		||||
        if request.method == 'POST':
 | 
			
		||||
            if 'create_flavor' in request.POST:
 | 
			
		||||
                form = FlavorAddForm(request.POST)
 | 
			
		||||
                if form.is_valid():
 | 
			
		||||
                    data = form.cleaned_data
 | 
			
		||||
                    create_flavor = Flavor(label=data['label'], vcpu=data['vcpu'], memory=data['memory'], disk=data['disk'])
 | 
			
		||||
                    create_flavor.save()
 | 
			
		||||
                    return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
            if 'delete_flavor' in request.POST:
 | 
			
		||||
                flavor_id = request.POST.get('flavor', '')
 | 
			
		||||
                delete_flavor = Flavor.objects.get(id=flavor_id)
 | 
			
		||||
                delete_flavor.delete()
 | 
			
		||||
                return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
            if 'create' in request.POST:
 | 
			
		||||
                firmware = dict()
 | 
			
		||||
                volume_list = list()
 | 
			
		||||
                is_disk_created = False
 | 
			
		||||
                clone_path = ""
 | 
			
		||||
                form = NewVMForm(request.POST)
 | 
			
		||||
                if form.is_valid():
 | 
			
		||||
                    data = form.cleaned_data
 | 
			
		||||
                    if data['meta_prealloc']:
 | 
			
		||||
                        meta_prealloc = True
 | 
			
		||||
                    if instances:
 | 
			
		||||
                        if data['name'] in instances:
 | 
			
		||||
                            msg = _("A virtual machine with this name already exists")
 | 
			
		||||
                            error_messages.append(msg)
 | 
			
		||||
                        if Instance.objects.filter(name__exact=data['name']):
 | 
			
		||||
                            messages.warning(request, _("There is an instance with same name. Are you sure?"))
 | 
			
		||||
                    if not error_messages:
 | 
			
		||||
                        if data['hdd_size']:
 | 
			
		||||
                            if not data['mac']:
 | 
			
		||||
                                error_msg = _("No Virtual Machine MAC has been entered")
 | 
			
		||||
                                error_messages.append(error_msg)
 | 
			
		||||
                            else:
 | 
			
		||||
                                try:
 | 
			
		||||
                                    path = conn.create_volume(
 | 
			
		||||
                                        data['storage'],
 | 
			
		||||
                                        data['name'],
 | 
			
		||||
                                        data['hdd_size'],
 | 
			
		||||
                                        default_disk_format,
 | 
			
		||||
                                        meta_prealloc,
 | 
			
		||||
                                        default_disk_owner_uid,
 | 
			
		||||
                                        default_disk_owner_gid,
 | 
			
		||||
                                    )
 | 
			
		||||
                                    volume = dict()
 | 
			
		||||
                                    volume['device'] = 'disk'
 | 
			
		||||
                                    volume['path'] = path
 | 
			
		||||
                                    volume['type'] = conn.get_volume_type(path)
 | 
			
		||||
                                    volume['cache_mode'] = data['cache_mode']
 | 
			
		||||
                                    volume['bus'] = default_bus
 | 
			
		||||
                                    if volume['bus'] == 'scsi':
 | 
			
		||||
                                        volume['scsi_model'] = default_scsi_disk_model
 | 
			
		||||
                                    volume['discard_mode'] = default_discard
 | 
			
		||||
                                    volume['detect_zeroes_mode'] = default_zeroes
 | 
			
		||||
                                    volume['io_mode'] = default_io
 | 
			
		||||
 | 
			
		||||
                                    volume_list.append(volume)
 | 
			
		||||
                                    is_disk_created = True
 | 
			
		||||
 | 
			
		||||
                                except libvirtError as lib_err:
 | 
			
		||||
                                    error_messages.append(lib_err)
 | 
			
		||||
                        elif data['template']:
 | 
			
		||||
                            templ_path = conn.get_volume_path(data['template'])
 | 
			
		||||
                            dest_vol = conn.get_volume_path(data["name"] + ".img", data['storage'])
 | 
			
		||||
                            if dest_vol:
 | 
			
		||||
                                error_msg = _("Image has already exist. Please check volumes or change instance name")
 | 
			
		||||
                                error_messages.append(error_msg)
 | 
			
		||||
                            else:
 | 
			
		||||
                                clone_path = conn.clone_from_template(
 | 
			
		||||
                                    data['name'],
 | 
			
		||||
                                    templ_path,
 | 
			
		||||
                                    data['storage'],
 | 
			
		||||
                                    meta_prealloc,
 | 
			
		||||
                                    default_disk_owner_uid,
 | 
			
		||||
                                    default_disk_owner_gid,
 | 
			
		||||
                                )
 | 
			
		||||
                                volume = dict()
 | 
			
		||||
                                volume['path'] = clone_path
 | 
			
		||||
                                volume['type'] = conn.get_volume_type(clone_path)
 | 
			
		||||
                                volume['device'] = 'disk'
 | 
			
		||||
                                volume['cache_mode'] = data['cache_mode']
 | 
			
		||||
                                volume['bus'] = default_bus
 | 
			
		||||
                                if volume['bus'] == 'scsi':
 | 
			
		||||
                                    volume['scsi_model'] = default_scsi_disk_model
 | 
			
		||||
                                volume['discard_mode'] = default_discard
 | 
			
		||||
                                volume['detect_zeroes_mode'] = default_zeroes
 | 
			
		||||
                                volume['io_mode'] = default_io
 | 
			
		||||
 | 
			
		||||
                                volume_list.append(volume)
 | 
			
		||||
                                is_disk_created = True
 | 
			
		||||
                        else:
 | 
			
		||||
                            if not data['images']:
 | 
			
		||||
                                error_msg = _("First you need to create or select an image")
 | 
			
		||||
                                error_messages.append(error_msg)
 | 
			
		||||
                            else:
 | 
			
		||||
                                for idx, vol in enumerate(data['images'].split(',')):
 | 
			
		||||
                                    try:
 | 
			
		||||
                                        path = conn.get_volume_path(vol)
 | 
			
		||||
                                        volume = dict()
 | 
			
		||||
                                        volume['path'] = path
 | 
			
		||||
                                        volume['type'] = conn.get_volume_type(path)
 | 
			
		||||
                                        volume['device'] = request.POST.get('device' + str(idx), '')
 | 
			
		||||
                                        volume['bus'] = request.POST.get('bus' + str(idx), '')
 | 
			
		||||
                                        if volume['bus'] == 'scsi':
 | 
			
		||||
                                            volume['scsi_model'] = default_scsi_disk_model
 | 
			
		||||
                                        volume['cache_mode'] = data['cache_mode']
 | 
			
		||||
                                        volume['discard_mode'] = default_discard
 | 
			
		||||
                                        volume['detect_zeroes_mode'] = default_zeroes
 | 
			
		||||
                                        volume['io_mode'] = default_io
 | 
			
		||||
 | 
			
		||||
                                        volume_list.append(volume)
 | 
			
		||||
                                    except libvirtError as lib_err:
 | 
			
		||||
                                        error_messages.append(lib_err)
 | 
			
		||||
                        if data['cache_mode'] not in conn.get_cache_modes():
 | 
			
		||||
                            error_msg = _("Invalid cache mode")
 | 
			
		||||
                            error_messages.append(error_msg)
 | 
			
		||||
 | 
			
		||||
                        if 'UEFI' in data["firmware"]:
 | 
			
		||||
                            firmware["loader"] = data["firmware"].split(":")[1].strip()
 | 
			
		||||
                            firmware["secure"] = 'no'
 | 
			
		||||
                            firmware["readonly"] = 'yes'
 | 
			
		||||
                            firmware["type"] = 'pflash'
 | 
			
		||||
                            if 'secboot' in firmware["loader"] and machine != 'q35':
 | 
			
		||||
                                messages.warning(
 | 
			
		||||
                                    request, "Changing machine type from '%s' to 'q35' "
 | 
			
		||||
                                    "which is required for UEFI secure boot." % machine)
 | 
			
		||||
                                machine = 'q35'
 | 
			
		||||
                                firmware["secure"] = 'yes'
 | 
			
		||||
 | 
			
		||||
                        if not error_messages:
 | 
			
		||||
                            uuid = util.randomUUID()
 | 
			
		||||
                            try:
 | 
			
		||||
                                conn.create_instance(name=data['name'],
 | 
			
		||||
                                                     memory=data['memory'],
 | 
			
		||||
                                                     vcpu=data['vcpu'],
 | 
			
		||||
                                                     vcpu_mode=data['vcpu_mode'],
 | 
			
		||||
                                                     uuid=uuid,
 | 
			
		||||
                                                     arch=arch,
 | 
			
		||||
                                                     machine=machine,
 | 
			
		||||
                                                     firmware=firmware,
 | 
			
		||||
                                                     volumes=volume_list,
 | 
			
		||||
                                                     networks=data['networks'],
 | 
			
		||||
                                                     virtio=data['virtio'],
 | 
			
		||||
                                                     listen_addr=data["listener_addr"],
 | 
			
		||||
                                                     nwfilter=data["nwfilter"],
 | 
			
		||||
                                                     graphics=data["graphics"],
 | 
			
		||||
                                                     video=data["video"],
 | 
			
		||||
                                                     console_pass=data["console_pass"],
 | 
			
		||||
                                                     mac=data['mac'],
 | 
			
		||||
                                                     qemu_ga=data['qemu_ga'])
 | 
			
		||||
                                create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
 | 
			
		||||
                                create_instance.save()
 | 
			
		||||
                                msg = _("Instance is created")
 | 
			
		||||
                                messages.success(request, msg)
 | 
			
		||||
                                addlogmsg(request.user.username, create_instance.name, msg)
 | 
			
		||||
                                return HttpResponseRedirect(reverse('instances:instance', args=[compute_id, data['name']]))
 | 
			
		||||
                            except libvirtError as lib_err:
 | 
			
		||||
                                if data['hdd_size'] or len(volume_list) > 0:
 | 
			
		||||
                                    if is_disk_created:
 | 
			
		||||
                                        for vol in volume_list:
 | 
			
		||||
                                            conn.delete_volume(vol['path'])
 | 
			
		||||
                                error_messages.append(lib_err)
 | 
			
		||||
        conn.close()
 | 
			
		||||
    return render(request, 'create_instance_w2.html', locals())
 | 
			
		||||
| 
						 | 
				
			
			@ -1,38 +1,40 @@
 | 
			
		|||
import re
 | 
			
		||||
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from create.models import Flavor
 | 
			
		||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
 | 
			
		||||
 | 
			
		||||
from appsettings.models import AppSettings
 | 
			
		||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES, QEMU_KEYMAPS
 | 
			
		||||
 | 
			
		||||
from .models import Flavor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FlavorAddForm(forms.Form):
 | 
			
		||||
    label = forms.CharField(label="Name",
 | 
			
		||||
                            error_messages={'required': _('No flavor name has been entered')},
 | 
			
		||||
                            max_length=64)
 | 
			
		||||
    vcpu = forms.IntegerField(label="VCPU",
 | 
			
		||||
                              error_messages={'required': _('No VCPU has been entered')}, )
 | 
			
		||||
    disk = forms.IntegerField(label="HDD",
 | 
			
		||||
                              error_messages={'required': _('No HDD image has been entered')}, )
 | 
			
		||||
    memory = forms.IntegerField(label="RAM",
 | 
			
		||||
                                error_messages={'required': _('No RAM size has been entered')}, )
 | 
			
		||||
class FlavorForm(forms.ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Flavor
 | 
			
		||||
        fields = '__all__'
 | 
			
		||||
 | 
			
		||||
    def clean_name(self):
 | 
			
		||||
        label = self.cleaned_data['label']
 | 
			
		||||
        have_symbol = re.match('^[a-zA-Z0-9._-]+$', label)
 | 
			
		||||
        if not have_symbol:
 | 
			
		||||
            raise forms.ValidationError(_('The flavor name must not contain any special characters'))
 | 
			
		||||
        elif len(label) > 64:
 | 
			
		||||
            raise forms.ValidationError(_('The flavor name must not exceed 20 characters'))
 | 
			
		||||
        try:
 | 
			
		||||
            Flavor.objects.get(label=label)
 | 
			
		||||
        except Flavor.DoesNotExist:
 | 
			
		||||
            return label
 | 
			
		||||
        raise forms.ValidationError(_('Flavor name is already use'))
 | 
			
		||||
 | 
			
		||||
class ConsoleForm(forms.Form):
 | 
			
		||||
    type = forms.ChoiceField()
 | 
			
		||||
    listen_on = forms.ChoiceField()
 | 
			
		||||
    generate_password = forms.BooleanField(required=False)
 | 
			
		||||
    clear_password = forms.BooleanField(required=False)
 | 
			
		||||
    password = forms.CharField(widget=forms.PasswordInput(render_value=True), required=False)
 | 
			
		||||
    clear_keymap = forms.BooleanField(required=False)
 | 
			
		||||
    keymap = forms.ChoiceField(required=False)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(ConsoleForm, self).__init__(*args, **kwargs)
 | 
			
		||||
        type_choices = ((c, c) for c in AppSettings.objects.get(key="QEMU_CONSOLE_DEFAULT_TYPE").choices_as_list())
 | 
			
		||||
        keymap_choices = [('auto', 'Auto')] + list((c, c) for c in QEMU_KEYMAPS)
 | 
			
		||||
        self.fields['type'] = forms.ChoiceField(choices=type_choices)
 | 
			
		||||
        self.fields['listen_on'] = forms.ChoiceField(choices=QEMU_CONSOLE_LISTEN_ADDRESSES)
 | 
			
		||||
        self.fields['keymap'] = forms.ChoiceField(choices=keymap_choices)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewVMForm(forms.Form):
 | 
			
		||||
    name = forms.CharField(error_messages={'required': _('No Virtual Machine name has been entered')},
 | 
			
		||||
                           max_length=64)
 | 
			
		||||
    name = forms.CharField(error_messages={'required': _('No Virtual Machine name has been entered')}, max_length=64)
 | 
			
		||||
    firmware = forms.CharField(max_length=50, required=False)
 | 
			
		||||
    vcpu = forms.IntegerField(error_messages={'required': _('No VCPU has been entered')})
 | 
			
		||||
    vcpu_mode = forms.CharField(max_length=20, required=False)
 | 
			
		||||
							
								
								
									
										23
									
								
								instances/migrations/0004_auto_20200618_0817.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								instances/migrations/0004_auto_20200618_0817.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
# Generated by Django 2.2.13 on 2020-06-18 08:17
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('instances', '0003_auto_20200615_0637'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='instance',
 | 
			
		||||
            name='name',
 | 
			
		||||
            field=models.CharField(db_index=True, max_length=120, verbose_name='name'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='instance',
 | 
			
		||||
            name='uuid',
 | 
			
		||||
            field=models.CharField(db_index=True, max_length=36, verbose_name='uuid'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										23
									
								
								instances/migrations/0005_flavor.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								instances/migrations/0005_flavor.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
# Generated by Django 2.2.13 on 2020-06-23 12:12
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('instances', '0004_auto_20200618_0817'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='Flavor',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('label', models.CharField(max_length=12, verbose_name='label')),
 | 
			
		||||
                ('memory', models.IntegerField(verbose_name='memory')),
 | 
			
		||||
                ('vcpu', models.IntegerField(verbose_name='vcpu')),
 | 
			
		||||
                ('disk', models.IntegerField(verbose_name='disk')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -3,8 +3,8 @@
 | 
			
		|||
from django.db import migrations
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_favors(apps, schema_editor):
 | 
			
		||||
    Flavor = apps.get_model("create", "Flavor")
 | 
			
		||||
def add_flavors(apps, schema_editor):
 | 
			
		||||
    Flavor = apps.get_model("instances", "Flavor")
 | 
			
		||||
    add_flavor = Flavor(label="micro", vcpu="1", memory="512", disk="20")
 | 
			
		||||
    add_flavor.save()
 | 
			
		||||
    add_flavor = Flavor(label="mini", vcpu="2", memory="1024", disk="30")
 | 
			
		||||
| 
						 | 
				
			
			@ -19,12 +19,17 @@ def add_favors(apps, schema_editor):
 | 
			
		|||
    add_flavor.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def del_flavors(apps, schema_editor):
 | 
			
		||||
    Flavor = apps.get_model("instances", "Flavor")
 | 
			
		||||
    Flavor.objects.all().delete()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('create', '0001_initial'),
 | 
			
		||||
        ('instances', '0005_flavor'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RunPython(add_favors),
 | 
			
		||||
        migrations.RunPython(add_flavors, del_flavors),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										18
									
								
								instances/migrations/0007_auto_20200624_0821.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								instances/migrations/0007_auto_20200624_0821.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# Generated by Django 2.2.13 on 2020-06-24 08:21
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('instances', '0006_addFlavors'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='flavor',
 | 
			
		||||
            name='label',
 | 
			
		||||
            field=models.CharField(max_length=12, unique=True, verbose_name='label'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										18
									
								
								instances/migrations/0008_auto_20200708_0950.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								instances/migrations/0008_auto_20200708_0950.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# Generated by Django 2.2.13 on 2020-07-08 09:50
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('instances', '0007_auto_20200624_0821'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='instance',
 | 
			
		||||
            name='created',
 | 
			
		||||
            field=models.DateTimeField(auto_now_add=True, verbose_name='created'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,21 +1,211 @@
 | 
			
		|||
from django.db.models import (CASCADE, BooleanField, CharField, DateField, ForeignKey, Model)
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from libvirt import VIR_DOMAIN_XML_SECURE
 | 
			
		||||
 | 
			
		||||
from computes.models import Compute
 | 
			
		||||
from vrtManager.instance import wvmInstance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Instance(Model):
 | 
			
		||||
    compute = ForeignKey(Compute, on_delete=CASCADE)
 | 
			
		||||
    name = CharField(_('name'), max_length=120)
 | 
			
		||||
    uuid = CharField(_('uuid'), max_length=36)
 | 
			
		||||
    is_template = BooleanField(_('is template'), default=False)
 | 
			
		||||
    created = DateField(_('created'), auto_now_add=True)
 | 
			
		||||
class Flavor(models.Model):
 | 
			
		||||
    label = models.CharField(_('label'), max_length=12, unique=True)
 | 
			
		||||
    memory = models.IntegerField(_('memory'))
 | 
			
		||||
    vcpu = models.IntegerField(_('vcpu'))
 | 
			
		||||
    disk = models.IntegerField(_('disk'))
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.label
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InstanceManager(models.Manager):
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        return super().get_queryset().select_related('compute')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Instance(models.Model):
 | 
			
		||||
    compute = models.ForeignKey(Compute, on_delete=models.CASCADE)
 | 
			
		||||
    name = models.CharField(_('name'), max_length=120, db_index=True)
 | 
			
		||||
    uuid = models.CharField(_('uuid'), max_length=36, db_index=True)
 | 
			
		||||
    is_template = models.BooleanField(_('is template'), default=False)
 | 
			
		||||
    created = models.DateTimeField(_('created'), auto_now_add=True)
 | 
			
		||||
 | 
			
		||||
    objects = InstanceManager()
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f'{self.compute}/{self.name}'
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def proxy(self):
 | 
			
		||||
        return wvmInstance(
 | 
			
		||||
            self.compute.hostname,
 | 
			
		||||
            self.compute.login,
 | 
			
		||||
            self.compute.password,
 | 
			
		||||
            self.compute.type,
 | 
			
		||||
            self.name,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
class PermissionSet(Model):
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def media(self):
 | 
			
		||||
        return self.proxy.get_media_devices()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def media_iso(self):
 | 
			
		||||
        return sorted(self.proxy.get_iso_media())
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def disks(self):
 | 
			
		||||
        return self.proxy.get_disk_devices()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def status(self):
 | 
			
		||||
        return self.proxy.get_status()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def autostart(self):
 | 
			
		||||
        return self.proxy.get_autostart()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def bootmenu(self):
 | 
			
		||||
        return self.proxy.get_bootmenu()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def boot_order(self):
 | 
			
		||||
        return self.proxy.get_bootorder()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def arch(self):
 | 
			
		||||
        return self.proxy.get_arch()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def machine(self):
 | 
			
		||||
        return self.proxy.get_machine_type()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def firmware(self):
 | 
			
		||||
        return self.proxy.get_loader()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def nvram(self):
 | 
			
		||||
        return self.proxy.get_nvram()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def vcpu(self):
 | 
			
		||||
        return self.proxy.get_vcpu()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def vcpu_range(self):
 | 
			
		||||
        return self.proxy.get_max_cpus()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def cur_vcpu(self):
 | 
			
		||||
        return self.proxy.get_cur_vcpu()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def vcpus(self):
 | 
			
		||||
        return self.proxy.get_vcpus()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def get_uuid(self):
 | 
			
		||||
        return self.proxy.get_uuid()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def memory(self):
 | 
			
		||||
        return self.proxy.get_memory()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def cur_memory(self):
 | 
			
		||||
        return self.proxy.get_cur_memory()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def title(self):
 | 
			
		||||
        return self.proxy.get_title()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def description(self):
 | 
			
		||||
        return self.proxy.get_description()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def networks(self):
 | 
			
		||||
        return self.proxy.get_net_devices()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def qos(self):
 | 
			
		||||
        return self.proxy.get_all_qos()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def telnet_port(self):
 | 
			
		||||
        return self.proxy.get_telnet_port()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def console_type(self):
 | 
			
		||||
        return self.proxy.get_console_type()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def console_port(self):
 | 
			
		||||
        return self.proxy.get_console_port()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def console_keymap(self):
 | 
			
		||||
        return self.proxy.get_console_keymap()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def console_listen_address(self):
 | 
			
		||||
        return self.proxy.get_console_listen_addr()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def guest_agent(self):
 | 
			
		||||
        return False if self.proxy.get_guest_agent() is None else True
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def guest_agent_ready(self):
 | 
			
		||||
        return self.proxy.is_agent_ready()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def video_model(self):
 | 
			
		||||
        return self.proxy.get_video_model()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def video_models(self):
 | 
			
		||||
        return self.proxy.get_video_models(self.arch, self.machine)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def snapshots(self):
 | 
			
		||||
        return sorted(self.proxy.get_snapshot(), reverse=True, key=lambda k: k['date'])
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def inst_xml(self):
 | 
			
		||||
        return self.proxy._XMLDesc(VIR_DOMAIN_XML_SECURE)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def has_managed_save_image(self):
 | 
			
		||||
        return self.proxy.get_managed_save_image()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def console_passwd(self):
 | 
			
		||||
        return self.proxy.get_console_passwd()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def cache_modes(self):
 | 
			
		||||
        return sorted(self.proxy.get_cache_modes().items())
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def io_modes(self):
 | 
			
		||||
        return sorted(self.proxy.get_io_modes().items())
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def discard_modes(self):
 | 
			
		||||
        return sorted(self.proxy.get_discard_modes().items())
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def detect_zeroes_modes(self):
 | 
			
		||||
        return sorted(self.proxy.get_detect_zeroes_modes().items())
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def formats(self):
 | 
			
		||||
        return self.proxy.get_image_formats()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PermissionSet(models.Model):
 | 
			
		||||
    """
 | 
			
		||||
    Dummy model for holding set of permissions we need to be automatically added by Django
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@
 | 
			
		|||
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> 
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-body">
 | 
			
		||||
                    <form method="post" action="" role="form" aria-label="Add instance network form">{% csrf_token %}
 | 
			
		||||
                    <form action="{% url 'instances:add_network' instance.id %}" method="post" action="" role="form" aria-label="Add instance network form">{% csrf_token %}
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-4 col-form-label">{% trans "MAC" %}</label>
 | 
			
		||||
                            <div class="col-sm-6">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@
 | 
			
		|||
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> 
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-body">
 | 
			
		||||
                    <form method="post" action="" role="form" aria-label="Add instance owner form">{% csrf_token %}
 | 
			
		||||
                    <form id="add-owner-form" method="post" action="{% url 'instances:add_owner' instance.id %}" role="form" aria-label="Add instance owner form">{% csrf_token %}
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-4 col-form-label">{% trans "User" %}</label>
 | 
			
		||||
                            <div class="col-sm-6">
 | 
			
		||||
| 
						 | 
				
			
			@ -24,12 +24,12 @@
 | 
			
		|||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-footer">
 | 
			
		||||
                    <button type="button" class="btn btn-secondary" data-dismiss="modal">{% trans "Close" %}</button>
 | 
			
		||||
                    <button type="submit" class="btn btn-primary" name="add_owner">{% trans "Add" %}</button>
 | 
			
		||||
                    <button type="submit" class="btn btn-primary" form="add-owner-form">{% trans "Add" %}</button>
 | 
			
		||||
                </div>
 | 
			
		||||
                    </form>
 | 
			
		||||
            </div> <!-- /.modal-content -->
 | 
			
		||||
        </div> <!-- /.modal-dialog -->
 | 
			
		||||
    </div> <!-- /.modal -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@
 | 
			
		|||
                </ul>
 | 
			
		||||
                <div class="tab-content">
 | 
			
		||||
                    <div class="tab-pane active" id="NewDisk">
 | 
			
		||||
                        <form method="post" role="form" aria-label="Add new volume to disk form">{% csrf_token %}
 | 
			
		||||
                        <form action="{% url 'instances:add_new_vol' instance.id %}" method="post" role="form" aria-label="Add new volume to disk form">{% csrf_token %}
 | 
			
		||||
                            <div class="modal-body">
 | 
			
		||||
                                <p class="font-weight-bold">{% trans "Volume parameters" %}</p>
 | 
			
		||||
                                <div class="form-group row">
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@
 | 
			
		|||
                                    <label class="col-sm-3 col-form-label">{% trans "Format" %}</label>
 | 
			
		||||
                                    <div class="col-sm-6">
 | 
			
		||||
                                        <select name="format" class="custom-select image-format">
 | 
			
		||||
                                            {% for format in formats %}
 | 
			
		||||
                                            {% for format in instance.formats %}
 | 
			
		||||
                                                <option value="{{ format }}" {% if format == default_format %}selected{% endif %}>{% trans format %}</option>
 | 
			
		||||
                                            {% endfor %}
 | 
			
		||||
                                        </select>
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +73,7 @@
 | 
			
		|||
                                    <label class="col-sm-3 col-form-label">{% trans "Cache" %}</label>
 | 
			
		||||
                                    <div class="col-sm-6">
 | 
			
		||||
                                        <select name="cache" class="custom-select image-format">
 | 
			
		||||
                                            {% for mode, name in cache_modes %}
 | 
			
		||||
                                            {% for mode, name in instance.cache_modes %}
 | 
			
		||||
                                                <option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>{% trans name %}</option>
 | 
			
		||||
                                            {% endfor %}
 | 
			
		||||
                                        </select>
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +93,7 @@
 | 
			
		|||
                            </div>
 | 
			
		||||
                        </div> <!-- /.modal-body -->
 | 
			
		||||
                        <div class="tab-pane" id="ExistingDisk">
 | 
			
		||||
                            <form method="post" role="form" aria-label="Add existing volume to instance form">{% csrf_token %}
 | 
			
		||||
                            <form action="{% url 'instances:add_existing_vol' instance.id %}" method="post" role="form" aria-label="Add existing volume to instance form">{% csrf_token %}
 | 
			
		||||
                                <div class="modal-body">
 | 
			
		||||
                                    <p class="font-weight-bold">{% trans "Volume parameters" %}</p>
 | 
			
		||||
                                    <div class="form-group row">
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +103,7 @@
 | 
			
		|||
                                                <button id="select_storage" class="btn btn-secondary dropdown-toggle form-control" type="button" data-toggle="dropdown">{% trans 'Select Pool' %}...</button>
 | 
			
		||||
                                                <div class="dropdown-menu">
 | 
			
		||||
                                                    {% for storage in storages_host %}
 | 
			
		||||
                                                        <a class="dropdown-item" href="#" onclick="get_volumes({{ compute_id }}, '{{ storage }}')">{{ storage }}</a>
 | 
			
		||||
                                                        <a class="dropdown-item" href="#" onclick="get_volumes({{ instance.compute.id }}, '{{ storage }}')">{{ storage }}</a>
 | 
			
		||||
                                                    {% endfor %}
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                                <input id="selected_storage" name="selected_storage" hidden/>
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +133,7 @@
 | 
			
		|||
                                        <label class="col-sm-3 col-form-label">{% trans "Cache" %}</label>
 | 
			
		||||
                                        <div class="col-sm-6">
 | 
			
		||||
                                            <select name="cache" class="custom-select image-format">
 | 
			
		||||
                                                {% for mode, name in cache_modes %}
 | 
			
		||||
                                                {% for mode, name in instance.cache_modes %}
 | 
			
		||||
                                                    <option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>{% trans name %}</option>
 | 
			
		||||
                                                {% endfor %}
 | 
			
		||||
                                            </select>
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +142,7 @@
 | 
			
		|||
                                </div>
 | 
			
		||||
                                <div class="modal-footer">
 | 
			
		||||
                                    <button type="button" class="btn btn-secondary" data-dismiss="modal">{% trans "Close" %}</button>
 | 
			
		||||
                                    <button type="submit" class="btn btn-success" name="add_existing_vol">{% trans "Add Volume" %}</button>
 | 
			
		||||
                                    <button type="submit" class="btn btn-success">{% trans "Add Volume" %}</button>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </form>
 | 
			
		||||
                        </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,131 +1,55 @@
 | 
			
		|||
{% extends "base.html" %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load icons %}
 | 
			
		||||
{% load staticfiles %}
 | 
			
		||||
{% block title %}{% trans "Instances" %}{% endblock %}
 | 
			
		||||
{% block style %}
 | 
			
		||||
    <link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% block page_header %}{% trans "Instances" %}{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block page_header_extra %}
 | 
			
		||||
{% if request.user.is_superuser %}
 | 
			
		||||
    {% include 'create_inst_block.html' %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
<div class="float-right search">
 | 
			
		||||
    <input id="filter" class="form-control" type="text" placeholder="{% trans 'Search' %}">
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock page_header_extra %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <!-- Page Heading -->
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-lg-12">
 | 
			
		||||
            {% if request.user.is_superuser %}
 | 
			
		||||
                {% include 'create_inst_block.html' %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if all_host_vms or all_user_vms %}
 | 
			
		||||
                <div class="float-right search">
 | 
			
		||||
                    <input id="filter" class="form-control" type="text" placeholder="{% trans 'Search' %}">
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <h2 class="page-header">{% trans "Instances" %}</h2>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- /.row -->
 | 
			
		||||
 | 
			
		||||
    {% include 'errors_block.html' %}
 | 
			
		||||
 | 
			
		||||
    {% for compute in computes %}
 | 
			
		||||
        {% if compute.status is not True %}
 | 
			
		||||
            <div class="alert alert-danger alert-dismissible fade show" role="alert">
 | 
			
		||||
                {% trans 'Problem occurred with host' %} {{ compute.name }}: {{ compute.status }}
 | 
			
		||||
                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
 | 
			
		||||
                    <span aria-hidden="true">×</span>
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    <div class="col-lg-12">
 | 
			
		||||
        {% if request.user.is_superuser %}
 | 
			
		||||
            {% if not all_host_vms %}
 | 
			
		||||
                <div class="col-lg-12">
 | 
			
		||||
                    <div class="alert alert-warning alert-dismissable">
 | 
			
		||||
                        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
 | 
			
		||||
                        <i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any Instance" %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% else %}
 | 
			
		||||
               {% if view_style == "nongrouped"  %}
 | 
			
		||||
                   {% include 'allinstances_index_nongrouped.html' %}
 | 
			
		||||
               {% endif %}
 | 
			
		||||
               {% if view_style == "grouped" %}
 | 
			
		||||
                   {% include 'allinstances_index_grouped.html' %}
 | 
			
		||||
               {% endif %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        {% if app_settings.VIEW_INSTANCES_LIST_STYLE == 'grouped' and request.user.is_superuser %}
 | 
			
		||||
            {% include 'allinstances_index_grouped.html' %}
 | 
			
		||||
        {% else %}
 | 
			
		||||
            {% if not all_user_vms %}
 | 
			
		||||
                <div class="col-lg-12">
 | 
			
		||||
                    <div class="alert alert-warning alert-dismissable">
 | 
			
		||||
                        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
 | 
			
		||||
                        <i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any Instance" %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% else  %}
 | 
			
		||||
                <table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
 | 
			
		||||
                    <thead>
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <th scope="col">{% trans 'Name' %}</th>
 | 
			
		||||
                            <th scope="col">{% trans 'Status' %}</th>
 | 
			
		||||
                            <th scope="col">{% trans 'VCPU' %}</th>
 | 
			
		||||
                            <th scope="col">{% trans 'Memory' %}</th>
 | 
			
		||||
                            <th scope="col" data-sortable="false" style="width: 165px;">{% trans 'Actions' %}</th>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                    </thead>
 | 
			
		||||
                    <tbody class="searchable">
 | 
			
		||||
                        {% for inst, vm in all_user_vms.items %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td><a href="{% url 'instances:instance' vm.compute_id vm.name %}">{{ vm.name }}</a><br><small><em>{{ vm.title }}</em></small></td>
 | 
			
		||||
                                <td>{% if vm.status == 1 %}
 | 
			
		||||
                                        <span class="text-success">{% trans "Active" %}</span>
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                    {% if vm.status == 5 %}
 | 
			
		||||
                                        <span class="text-danger">{% trans "Off" %}</span>
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                    {% if vm.status == 3 %}
 | 
			
		||||
                                        <span class="text-warning">{% trans "Suspend" %}</span>
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                </td>
 | 
			
		||||
                                <td>{{ vm.vcpu }}</td>
 | 
			
		||||
                                <td>{{ vm.memory }} {% trans "MB" %}</td>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    {% include "instance_actions.html" %}
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </tbody>
 | 
			
		||||
                </table>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% include 'allinstances_index_nongrouped.html' %}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% endblock content %}
 | 
			
		||||
{% block script %}
 | 
			
		||||
<script src="{% static "js/sortable.min.js" %}"></script>
 | 
			
		||||
<script>
 | 
			
		||||
    function open_console(uuid) {
 | 
			
		||||
        window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=485");
 | 
			
		||||
    }
 | 
			
		||||
</script>
 | 
			
		||||
<script>
 | 
			
		||||
    function filter_table() {
 | 
			
		||||
        var rex = new RegExp($(this).val(), 'i');
 | 
			
		||||
        $('.searchable tr').hide();
 | 
			
		||||
        $('.searchable tr').filter(function () {
 | 
			
		||||
            return rex.test($(this).text());
 | 
			
		||||
        }).show();
 | 
			
		||||
        Cookies.set("instances_filter", $(this).val(), { expires: 1 });
 | 
			
		||||
    }
 | 
			
		||||
    $(document).ready(function () {
 | 
			
		||||
        instances_filter_cookie = Cookies.get("instances_filter");
 | 
			
		||||
        if (instances_filter_cookie) {
 | 
			
		||||
            $('#filter').val(instances_filter_cookie);
 | 
			
		||||
            $('#filter').each(filter_table);
 | 
			
		||||
        }
 | 
			
		||||
        (function ($) {
 | 
			
		||||
            $('#filter').keyup(filter_table)
 | 
			
		||||
        }(jQuery));
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
<script>
 | 
			
		||||
    function goto_instance_clone(compute, instance) {
 | 
			
		||||
        window.location = "/instances/" + compute + "/" + instance + "/#clone";
 | 
			
		||||
    }
 | 
			
		||||
</script>
 | 
			
		||||
{% if request.user.is_superuser %}
 | 
			
		||||
    <script src="{% static "js/sortable.min.js" %}"></script>
 | 
			
		||||
    <script>
 | 
			
		||||
        function goto_compute() {
 | 
			
		||||
            let compute = $("#compute_select").val();
 | 
			
		||||
            {#window.location.href = "{% url 'create_instance' 1 %}".replace(1, compute);#}
 | 
			
		||||
            window.location.href = "{% url 'create_instance_select_type' 1 %}".replace(1, compute);
 | 
			
		||||
        function open_console(uuid) {
 | 
			
		||||
            window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=485");
 | 
			
		||||
        }
 | 
			
		||||
    </script>
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
    <script src="{% static 'js/filter-table.js' %}"></script>
 | 
			
		||||
    {% if request.user.is_superuser %}
 | 
			
		||||
        <script>
 | 
			
		||||
            function goto_compute() {
 | 
			
		||||
                let compute = $("#compute_select").val();
 | 
			
		||||
                window.location.href = "{% url 'instances:create_instance_select_type' 1 %}".replace(1, compute);
 | 
			
		||||
            }
 | 
			
		||||
        </script>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
{% endblock script %}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,72 +1,77 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load icons %}
 | 
			
		||||
<table class="table table-hover sortable-theme-bootstrap" data-sortable>
 | 
			
		||||
    <thead>
 | 
			
		||||
    <tr style="border: 2px solid transparent; ">
 | 
			
		||||
        <th scope="col" data-sortable="false"><a class="text-secondary" href="#" id="hide_all_instances" onclick="hide_all_host_instances()">#</a></th>
 | 
			
		||||
        <th scope="col">{% trans "Name" %}<br>{% trans "Description" %}</th>
 | 
			
		||||
        <th scope="col" class="d-none d-sm-table-cell">{% trans "User"%}</th>
 | 
			
		||||
        <th scope="col">{% trans "Status" %}</th>
 | 
			
		||||
        <th scope="col" class="d-none d-sm-table-cell">{% trans "VCPU" %}</th>
 | 
			
		||||
        <th scope="col" class="d-none d-sm-table-cell">{% trans "Memory" %}</th>
 | 
			
		||||
        <th scope="col" style="width:200px;" data-sortable="false">{% trans "Actions" %} & {% trans "Mem Usage" %}</th>
 | 
			
		||||
    </tr>
 | 
			
		||||
        <tr style="border: 2px solid transparent; ">
 | 
			
		||||
            <th scope="col" data-sortable="false"><a class="text-secondary" href="#" id="hide_all_instances" onclick="hide_all_host_instances()">#</a></th>
 | 
			
		||||
            <th scope="col">{% trans "Name" %}<br>{% trans "Description" %}</th>
 | 
			
		||||
            <th scope="col" class="d-none d-sm-table-cell">{% trans "User"%}</th>
 | 
			
		||||
            <th scope="col">{% trans "Status" %}</th>
 | 
			
		||||
            <th scope="col" class="d-none d-sm-table-cell">{% trans "VCPU" %}</th>
 | 
			
		||||
            <th scope="col" class="d-none d-sm-table-cell">{% trans "Memory" %}</th>
 | 
			
		||||
            <th scope="col" style="width:200px;" data-sortable="false">{% trans "Actions" %} & {% trans "Mem Usage" %}</th>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody class="searchable">
 | 
			
		||||
    {% for host, insts  in all_host_vms.items %}
 | 
			
		||||
        <tr class="font-weight-bold active" style="border-bottom: 2px solid darkgray;border-top: 2px solid darkgray;">
 | 
			
		||||
            <td>
 | 
			
		||||
                <span id="collapse_host_instances_{{ host.1 }}" class="fa fa-chevron-up" onclick="hide_host_instances('{{ host.1 }}');"></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <a class="text-secondary" href="{% url 'overview' host.0 %}">{{ host.1 }}</a>
 | 
			
		||||
                <span id="inst_count_badge_{{ host.1 }}" class="badge badge-secondary d-none">{{ insts.items|length }}</span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td class="d-none d-sm-table-cell"></td>
 | 
			
		||||
            <td>
 | 
			
		||||
                {% if host.2 == 1 %}<span class="text-success">{% trans "Active" %}</span>{% endif %}
 | 
			
		||||
                {% if host.2 == 2 %}<span class="text-warning">{% trans "Not Active" %}</span>{% endif %}
 | 
			
		||||
                {% if host.2 == 3 %}<span class="text-danger">{% trans "Connection Failed" %}</span>{% endif %}
 | 
			
		||||
            </td>
 | 
			
		||||
            <td class="d-none d-sm-table-cell text-center">{{ host.3 }}</td>
 | 
			
		||||
            <td class="d-none d-sm-table-cell text-right">{{ host.4|filesizeformat }}</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <div class="progress">
 | 
			
		||||
                    <div class="progress-bar bg-success" role="progressbar" style="width: {{ host.5 }}%"
 | 
			
		||||
                        aria-valuenow="{{ host.5 }}" aria-valuemin="0" aria-valuemax="100">{{ host.5 }}%
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
        {% for inst, vm in insts.items %}
 | 
			
		||||
            <tr host="{{ host.1 }}">
 | 
			
		||||
                <td class="text-right">{{ forloop.counter }} </td>
 | 
			
		||||
                <td>  
 | 
			
		||||
                    <a class="text-secondary" href="{% url 'instances:instance' host.0 inst %}">{{ inst }}</a><br>
 | 
			
		||||
                    <small><em>{{ vm.title }}</em></small>
 | 
			
		||||
                </td>
 | 
			
		||||
                 <td class="d-none d-sm-table-cell">
 | 
			
		||||
                    <span class="font-small font-italic">
 | 
			
		||||
                        {% if vm.userinstances.count > 0 %} {{ vm.userinstances.first_user.user.username }}
 | 
			
		||||
                            {% if vm.userinstances.count > 1 %} (+{{ vm.userinstances.count|add:"-1" }}){% endif %}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </span>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    {% if vm.status == 1 %}<span class="text-success">{% trans "Active" %}</span>{% endif %}
 | 
			
		||||
                    {% if vm.status == 5 %}<span class="text-danger">{% trans "Off" %}</span>{% endif %}
 | 
			
		||||
                    {% if vm.status == 3 %}<span class="text-warning">{% trans "Suspend" %}</span>{% endif %}
 | 
			
		||||
                </td>
 | 
			
		||||
                <td class="d-none d-sm-table-cell text-center">{{ vm.vcpu }}</td>
 | 
			
		||||
                <td class="d-none d-sm-table-cell text-right">{{ vm.memory |filesizeformat }}</td>
 | 
			
		||||
                <td  class="text-nowrap">
 | 
			
		||||
                    {% include 'instance_actions.html' %}
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        {% for compute in computes %}
 | 
			
		||||
            {% if compute.status is True and compute.instance_set.count > 0 %}
 | 
			
		||||
                <tr class="font-weight-bold active" style="border-bottom: 2px solid darkgray;border-top: 2px solid darkgray;">
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <span id="collapse_host_instances_{{ compute.id }}" class="fa fa-chevron-up" onclick="hide_host_instances('{{ compute.id }}');"></span>
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <a class="text-secondary" href="{% url 'overview' compute.id %}">{{ compute.name }}</a>
 | 
			
		||||
                        <span id="inst_count_badge_{{ compute.id }}" class="badge badge-secondary d-none">{{ compute.instance_set.count }}</span>
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td class="d-none d-sm-table-cell"></td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <span class="text-success">{% trans "Connected" %}</span>
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td class="d-none d-sm-table-cell text-center">{{ compute.cpu_count }}</td>
 | 
			
		||||
                    <td class="d-none d-sm-table-cell text-right">{{ compute.ram_size|filesizeformat }}</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <div class="progress">
 | 
			
		||||
                            <div class="progress-bar bg-success" role="progressbar" style="width: {{ compute.ram_usage }}%"
 | 
			
		||||
                                aria-valuenow="{{ compute.ram_usage }}" aria-valuemin="0" aria-valuemax="100">{{ compute.ram_usage }}%
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                {% for instance in compute.instance_set.all %}
 | 
			
		||||
                    <tr host="{{ compute.id }}">
 | 
			
		||||
                        <td class="text-right">{{ forloop.counter }} </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <a class="text-secondary" href="{% url 'instances:instance' instance.id %}">{{ instance.name }}</a><br>
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <em>
 | 
			
		||||
                                {% if instance.userinstance_set.all.count > 0 %}
 | 
			
		||||
                                {{ instance.userinstance_set.all.0.user }}
 | 
			
		||||
                                {% if instance.userinstance_set.all.count > 1 %}
 | 
			
		||||
                                (+{{ instance.userinstance_set.all.count|add:"-1" }})
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            </em>
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {% if instance.proxy.instance.info.0 == 1 %}<span class="text-success">{% trans "Active" %}</span>{% endif %}
 | 
			
		||||
                            {% if instance.proxy.instance.info.0 == 5 %}<span class="text-danger">{% trans "Off" %}</span>{% endif %}
 | 
			
		||||
                            {% if instance.proxy.instance.info.0 == 3 %}
 | 
			
		||||
                                <span class="text-warning">{% trans "Suspended" %}</span>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>{{ instance.proxy.instance.info.3 }}</td>
 | 
			
		||||
                        <td>{{ instance.cur_memory }} MB</td>
 | 
			
		||||
                        <td class="text-nowrap">
 | 
			
		||||
                            {% include 'instance_actions.html' %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
{% block script %}
 | 
			
		||||
<script>
 | 
			
		||||
function hide_all_host_instances() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,33 +1,55 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
<table class="table table-hover sortable-theme-bootstrap" data-sortable>
 | 
			
		||||
    <thead>
 | 
			
		||||
    <tr>
 | 
			
		||||
        <th scope="col">{% trans "Name" %}<br>{% trans "Description" %}</th>
 | 
			
		||||
        <th scope="col">{% trans "Host" %}<br>{% trans "User"%}</th>
 | 
			
		||||
        <th scope="col">{% trans "Status" %}</th>
 | 
			
		||||
        <th scope="col">{% trans "VCPU" %}</th>
 | 
			
		||||
        <th scope="col">{% trans "Memory" %}</th>
 | 
			
		||||
        <th scope="col" style="width:200px;" data-sortable="false">{% trans "Actions" %}</th>
 | 
			
		||||
    </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <th scope="col">{% trans 'Name' %}<br>{% trans 'Description' %}</th>
 | 
			
		||||
            {% if request.user.is_superuser %}
 | 
			
		||||
                <th scope="col">{% trans 'Host' %}<br>{% trans 'User' %}</th>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <th scope="col">{% trans 'Status' %}</th>
 | 
			
		||||
            <th scope="col">{% trans 'VCPU' %}</th>
 | 
			
		||||
            <th scope="col">{% trans 'Memory' %}</th>
 | 
			
		||||
            <th scope="col" data-sortable="false">{% trans 'Actions' %}</th>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody class="searchable">
 | 
			
		||||
    {% for host, inst  in all_host_vms.items %}
 | 
			
		||||
        {% for inst, vm in inst.items %}
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td><a href="{% url 'instances:instance' host.0 inst %}">{{ inst }}</a><br><small><em>{{ info.title }}</em></small></td>
 | 
			
		||||
                <td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a><br><small><em>{% if info.userinstances.count > 0 %}{{ info.userinstances.first_user.user.username }}{% if info.userinstances.count > 1 %} (+{{ info.userinstances.count|add:"-1" }}){% endif %}{% endif %}</em></small></td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    {% if vm.status == 1 %}<span class="text-success">{% trans "Active" %}</span>{% endif %}
 | 
			
		||||
                    {% if vm.status == 5 %}<span class="text-danger">{% trans "Off" %}</span>{% endif %}
 | 
			
		||||
                    {% if vm.status == 3 %}<span class="text-warning">{% trans "Suspend" %}</span>{% endif %}
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>{{ vm.vcpu }}</td>
 | 
			
		||||
                <td>{{ vm.memory|filesizeformat }}</td>
 | 
			
		||||
                <td class="text-nowrap">
 | 
			
		||||
                   {% include "instance_actions.html" %}
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        {% for instance in instances  %}
 | 
			
		||||
            {% if instance.compute.status is True %}
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <a class="text-secondary" href="{% url 'instances:instance' instance.id %}">
 | 
			
		||||
                            {{ instance.name }}
 | 
			
		||||
                        </a><br>
 | 
			
		||||
                        <small><em>{{ instance.title }}</em></small>
 | 
			
		||||
                    </td>
 | 
			
		||||
                    {% if request.user.is_superuser %}
 | 
			
		||||
                        <td>
 | 
			
		||||
                            <a href="{% url 'overview' instance.compute.id %}">{{ instance.compute.name }}</a><br>
 | 
			
		||||
                                <small><em>
 | 
			
		||||
                                    {% if instance.userinstance_set.all.count > 0 %}
 | 
			
		||||
                                    {{ instance.userinstance_set.all.0.user }}
 | 
			
		||||
                                    {% if instance.userinstance_set.all.count > 1 %}
 | 
			
		||||
                                    (+{{ instance.userinstance_set.all.count|add:"-1" }})
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                </em></small>
 | 
			
		||||
                        </td>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    <td>
 | 
			
		||||
                        {% if instance.proxy.instance.info.0 == 1 %}<span
 | 
			
		||||
                            class="text-success">{% trans "Active" %}</span>{% endif %}
 | 
			
		||||
                        {% if instance.proxy.instance.info.0 == 5 %}<span
 | 
			
		||||
                            class="text-danger">{% trans "Off" %}</span>{% endif %}
 | 
			
		||||
                        {% if instance.proxy.instance.info.0 == 3 %}<span
 | 
			
		||||
                            class="text-warning">{% trans "Suspended" %}</span>{% endif %}
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td>{{ instance.proxy.instance.info.3 }}</td>
 | 
			
		||||
                    <td>{{ instance.cur_memory }} MB</td>
 | 
			
		||||
                    <td class="text-nowrap">
 | 
			
		||||
                        {% include 'instance_actions.html' %}
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@
 | 
			
		|||
                    <a class="nav-link" href="{% url 'instances' compute.id %}"><i class="fa fa-desktop"></i> {% trans "Instances" %}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item active">
 | 
			
		||||
                    <a class="nav-link" href="{% url 'instances:instance' compute.id vname %}"><i class="fa fa-hdd-o"></i> {{ vname }}</a>
 | 
			
		||||
                    <a class="nav-link" href="{% url 'instances:instance' instance.id %}"><i class="fa fa-hdd-o"></i> {{ instance.name }}</a>
 | 
			
		||||
                </li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										34
									
								
								instances/templates/create_flav_block.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								instances/templates/create_flav_block.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load bootstrap4 %}
 | 
			
		||||
{% if request.user.is_superuser %}
 | 
			
		||||
    <button href="#addFlavor" type="button" class="btn btn-success btn-header float-right" data-toggle="modal">
 | 
			
		||||
        <span class="fa fa-plus" aria-hidden="true"></span>
 | 
			
		||||
    </button>
 | 
			
		||||
 | 
			
		||||
    <!-- Modal Flavor -->
 | 
			
		||||
    <div class="modal fade" id="addFlavor" tabindex="-1" role="dialog" aria-labelledby="addFlavorLabel"
 | 
			
		||||
         aria-hidden="true">
 | 
			
		||||
        <div class="modal-dialog">
 | 
			
		||||
            <div class="modal-content">
 | 
			
		||||
                <div class="modal-header">
 | 
			
		||||
                    <h4 class="modal-title">{% trans "Add New Flavor" %}</h4>
 | 
			
		||||
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-body">
 | 
			
		||||
                    <form method="post" role="form" action="{% url 'instances:flavor_create' %}" id="flavor-create-form">
 | 
			
		||||
                        {% csrf_token %}
 | 
			
		||||
                        {% bootstrap_form flavor_form layout='horizontal' %}
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-footer">
 | 
			
		||||
                    <button type="button" class="btn btn-secondary" data-dismiss="modal">
 | 
			
		||||
                        {% trans "Close" %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <button form="flavor-create-form" type="submit" class="btn btn-primary">
 | 
			
		||||
                        {% trans "Add" %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div> <!-- /.modal-content -->
 | 
			
		||||
        </div> <!-- /.modal-dialog -->
 | 
			
		||||
    </div> <!-- /.modal -->
 | 
			
		||||
{% endif %}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,49 +1,48 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% if request.user.is_superuser %}
 | 
			
		||||
    <a href="#AddInstance" type="button" class="btn btn-success btn-header float-right" data-toggle="modal">
 | 
			
		||||
        <span class="fa fa-plus" aria-hidden="true"></span>
 | 
			
		||||
    </a>
 | 
			
		||||
<a href="#AddInstance" type="button" class="btn btn-success btn-header float-right" data-toggle="modal">
 | 
			
		||||
    <span class="fa fa-plus" aria-hidden="true"></span>
 | 
			
		||||
</a>
 | 
			
		||||
 | 
			
		||||
    <!-- Modal pool -->
 | 
			
		||||
    <div class="modal fade" id="AddInstance" tabindex="-1" role="dialog" aria-labelledby="AddNetPoolLabel" aria-hidden="true">
 | 
			
		||||
        <div class="modal-dialog">
 | 
			
		||||
            <div class="modal-content">
 | 
			
		||||
                <div class="modal-header">
 | 
			
		||||
                    <h5 class="modal-title">{% trans "Choose a compute for new instance" %}</h5>
 | 
			
		||||
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-body">
 | 
			
		||||
                    <form method="post" aria-label="Select compute for instance create form"> {% csrf_token %}
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-4 col-form-label">{% trans "Compute" %}</label>
 | 
			
		||||
                            <div class="col-sm-6">
 | 
			
		||||
                                <select class="custom-select" id="compute_select">
 | 
			
		||||
                                    <option>{% trans "Please select" %}</option>
 | 
			
		||||
                                    {% for compute in computes_data %}
 | 
			
		||||
                                        <option {% if compute.status is not True %} class="font-italic text-muted" {% else %} value="{{ compute.id }}" {% endif %}>{{ compute.name }}</option>
 | 
			
		||||
                                    {% empty %}
 | 
			
		||||
                                        <option value="None">{% trans "None" %}</option>
 | 
			
		||||
                                    {% endfor %}
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
<!-- Modal pool -->
 | 
			
		||||
<div class="modal fade" id="AddInstance" tabindex="-1" role="dialog" aria-labelledby="AddNetPoolLabel" aria-hidden="true">
 | 
			
		||||
    <div class="modal-dialog">
 | 
			
		||||
        <div class="modal-content">
 | 
			
		||||
            <div class="modal-header">
 | 
			
		||||
                <h5 class="modal-title">{% trans "Choose a compute for new instance" %}</h5>
 | 
			
		||||
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="modal-body">
 | 
			
		||||
                <form method="post" aria-label="Select compute for instance create form">
 | 
			
		||||
                    {% csrf_token %}
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label class="col-sm-4 col-form-label">{% trans "Compute" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <select class="custom-select" id="compute_select">
 | 
			
		||||
                                <option>{% trans "Please select" %}</option>
 | 
			
		||||
                                {% for compute in computes %}
 | 
			
		||||
                                    <option {% if compute.status is not True %} class="font-italic text-muted" {% else %} value="{{ compute.id }}" {% endif %}>{{ compute.name }}</option>
 | 
			
		||||
                                {% empty %}
 | 
			
		||||
                                    <option value="None">{% trans "None" %}</option>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            </select>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="modal-footer">
 | 
			
		||||
                    <button type="button" class="btn btn-secondary" data-dismiss="modal">
 | 
			
		||||
                        {% trans "Close" %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </form>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="modal-footer">
 | 
			
		||||
                <button type="button" class="btn btn-secondary" data-dismiss="modal">
 | 
			
		||||
                    {% trans "Close" %}
 | 
			
		||||
                </button>
 | 
			
		||||
                {% if computes %}
 | 
			
		||||
                    <button type="submit" class="btn btn-primary" name="choose" onclick='goto_compute()'>
 | 
			
		||||
                        {% trans "Choose" %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                    {% if computes %}
 | 
			
		||||
                        <button type="submit" class="btn btn-primary" name="choose" onclick='goto_compute()'>
 | 
			
		||||
                            {% trans "Choose" %}
 | 
			
		||||
                        </button>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <button class="btn btn-primary disabled">
 | 
			
		||||
                            {% trans "Choose" %}
 | 
			
		||||
                        </button>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </div>
 | 
			
		||||
            </div> <!-- /.modal-content -->
 | 
			
		||||
        </div> <!-- /.modal-dialog -->
 | 
			
		||||
    </div> <!-- /.modal -->
 | 
			
		||||
{% endif %}
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <button class="btn btn-primary disabled">
 | 
			
		||||
                        {% trans "Choose" %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div> <!-- /.modal-content -->
 | 
			
		||||
    </div> <!-- /.modal-dialog -->
 | 
			
		||||
</div> <!-- /.modal -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -143,9 +143,9 @@
 | 
			
		|||
            let compute = '{{ compute.id }}';
 | 
			
		||||
            let arch = $("#select_archs").val();
 | 
			
		||||
            let machine = $("#select_chipset").val();
 | 
			
		||||
            create_machine_url = "/computes/" + compute + "/create/archs/" + arch + "/machines/" + machine;
 | 
			
		||||
            {#url = "{% url 'create_instance' compute.id 'x86_64' 'pc' %}".replace(/x86_64/, arch).replace(/pc/, machine);#}
 | 
			
		||||
            window.location.href = create_machine_url;
 | 
			
		||||
            //create_machine_url = "/computes/" + compute + "/create/archs/" + arch + "/machines/" + machine;
 | 
			
		||||
            url = "{% url 'instances:create_instance' compute.id 'x86_64' 'pc' %}".replace(/x86_64/, arch).replace(/pc/, machine);
 | 
			
		||||
            window.location.href = url;//create_machine_url;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    </script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
{% extends "base.html" %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load staticfiles %}
 | 
			
		||||
{% load icons %}
 | 
			
		||||
{% block title %}{% trans "Create new instance" %}{% endblock %}
 | 
			
		||||
{% block style %}
 | 
			
		||||
    <link href="{% static "css/bootstrap-multiselect.css" %}" rel="stylesheet">
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +75,7 @@
 | 
			
		|||
                            {% include 'create_flav_block.html' %}
 | 
			
		||||
                            <h3 class="page-header">{% trans "Create from flavor" %}</h3>
 | 
			
		||||
                            <div class="table-responsive">
 | 
			
		||||
                                <table class="table table-bordered table-hover">
 | 
			
		||||
                                <table class="table table-hover">
 | 
			
		||||
                                <thead>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <th scope="col">#</th>
 | 
			
		||||
| 
						 | 
				
			
			@ -286,10 +287,10 @@
 | 
			
		|||
                                            </a>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                        <td style="width:5px;">
 | 
			
		||||
                                            <form action="" method="post" role="form" aria-label="Delete flavor form">{% csrf_token %}
 | 
			
		||||
                                                <input type="hidden" name="flavor" value="{{ flavor.id }}">
 | 
			
		||||
                                            <form action="{% url 'instances:flavor_delete' flavor.id %}" method="post" role="form" aria-label="Delete flavor form">
 | 
			
		||||
                                                {% csrf_token %}
 | 
			
		||||
                                                <button type="submit" class="btn btn-sm btn-secondary" name="delete_flavor" onclick="return confirm('{% trans "Are you sure?" %}')">
 | 
			
		||||
                                                    <span class="fa fa-trash"></span>
 | 
			
		||||
                                                    {% icon 'trash' %}
 | 
			
		||||
                                                </button>
 | 
			
		||||
                                            </form>
 | 
			
		||||
                                        </td>
 | 
			
		||||
| 
						 | 
				
			
			@ -872,7 +873,7 @@
 | 
			
		|||
    <script>
 | 
			
		||||
        function goto_compute() {
 | 
			
		||||
            let compute = {{ compute.id }}
 | 
			
		||||
            window.location.href = "{% url 'create_instance_select_type' 1 %}".replace(1, compute);
 | 
			
		||||
            window.location.href = "{% url 'instances:create_instance_select_type' 1 %}".replace(1, compute);
 | 
			
		||||
        }
 | 
			
		||||
    </script>
 | 
			
		||||
{% endif %}
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +52,7 @@
 | 
			
		|||
                            <div class="form-group row">
 | 
			
		||||
                                <label class="col-sm-4 col-form-label">{% trans 'Bus' %}</label>
 | 
			
		||||
                                <div class="col-sm-8">
 | 
			
		||||
                                    <select class="custom-select" name="vol_bus" {% if status != 5 %} disabled {% endif %}>
 | 
			
		||||
                                    <select class="custom-select" name="vol_bus" {% if instance.status != 5 %} disabled {% endif %}>
 | 
			
		||||
                                    {% for bus in bus_host %}
 | 
			
		||||
                                        <option value="{{ bus }}" {% if bus == disk.bus %}selected{% endif %}>{{ bus }}</option>
 | 
			
		||||
                                    {% endfor %}
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +78,7 @@
 | 
			
		|||
                                    <label class="col-sm-4 col-form-label">{% trans 'Cache mode' %}</label>
 | 
			
		||||
                                    <div class="col-sm-8">
 | 
			
		||||
                                        <select class="custom-select" name="vol_cache">
 | 
			
		||||
                                        {% for key, val in cache_modes %}
 | 
			
		||||
                                        {% for key, val in instance.cache_modes %}
 | 
			
		||||
                                            <option value="{{ key }}" {% if key == disk.cache %}selected{% endif %}>{{ val }}</option>
 | 
			
		||||
                                        {% endfor %}
 | 
			
		||||
                                        </select>
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +88,7 @@
 | 
			
		|||
                                    <label class="col-sm-4 col-form-label">{% trans 'IO mode' %}</label>
 | 
			
		||||
                                    <div class="col-sm-8">
 | 
			
		||||
                                        <select class="custom-select" name="vol_io_mode">
 | 
			
		||||
                                        {% for key, val in io_modes %}
 | 
			
		||||
                                        {% for key, val in instance.io_modes %}
 | 
			
		||||
                                            <option value="{{ key }}" {% if key == disk.io %}selected{% endif %}>{{ val }}</option>
 | 
			
		||||
                                        {% endfor %}
 | 
			
		||||
                                        </select>
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +98,7 @@
 | 
			
		|||
                                    <label class="col-sm-4 col-form-label">{% trans 'Discard mode' %}</label>
 | 
			
		||||
                                    <div class="col-sm-8">
 | 
			
		||||
                                        <select class="custom-select" name="vol_discard_mode">
 | 
			
		||||
                                        {% for key, val in discard_modes %}
 | 
			
		||||
                                        {% for key, val in instance.discard_modes %}
 | 
			
		||||
                                            <option value="{{ key }}" {% if key == disk.discard %}selected{% endif %}>{{ val }}</option>
 | 
			
		||||
                                        {% endfor %}
 | 
			
		||||
                                        </select>
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +108,7 @@
 | 
			
		|||
                                    <label class="col-sm-4 col-form-label">{% trans 'Detect zeroes' %}</label>
 | 
			
		||||
                                    <div class="col-sm-8">
 | 
			
		||||
                                        <select class="custom-select" name="vol_detect_zeroes">
 | 
			
		||||
                                        {% for key, val in detect_zeroes_modes %}
 | 
			
		||||
                                        {% for key, val in instance.detect_zeroes_modes %}
 | 
			
		||||
                                            <option value="{{ key }}" {% if key == disk.detect_zeroes %}selected{% endif %}>{{ val }}</option>
 | 
			
		||||
                                        {% endfor %}
 | 
			
		||||
                                        </select>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,61 +1,44 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load icons %}
 | 
			
		||||
<form action="" method="post" role="form" aria-label="Shortcut instance action form">{% csrf_token %}
 | 
			
		||||
    <input type="hidden" name="name" value="{{ inst }}"/>
 | 
			
		||||
    <input type="hidden" name="compute_id" value="{{ host.0 }}"/>
 | 
			
		||||
    {% if vm.status == 5 %}
 | 
			
		||||
        {% if vm.is_template %}
 | 
			
		||||
            <button class="btn btn-sm btn-secondary" type="button" name="clone" title="{% trans "Clone" %}" onclick="goto_instance_clone({{ host.0 }}, '{{ inst }}');">
 | 
			
		||||
                <span class="fa fa-clone"></span>
 | 
			
		||||
            </button>
 | 
			
		||||
    {% if instance.proxy.instance.info.0 == 5 %}
 | 
			
		||||
        {% if instance.is_template %}
 | 
			
		||||
            <a href="{% url 'instances:instance' instance.id %}#clone" class="btn btn-sm btn-secondary" title="{% trans "Clone" %}">
 | 
			
		||||
                {% icon 'clone' %}
 | 
			
		||||
            </a>
 | 
			
		||||
        {% else %}
 | 
			
		||||
            <button class="btn btn-sm btn-secondary" type="submit" name="poweron" title="{% trans "Power On" %}">
 | 
			
		||||
                <span class="fa fa-play"></span>
 | 
			
		||||
            </button>
 | 
			
		||||
            <a class="btn btn-sm btn-secondary" href="{% url 'instances:poweron' instance.id %}" title="{% trans "Power On" %}">
 | 
			
		||||
                {% icon 'play' %}
 | 
			
		||||
            </a>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <button class="btn btn-sm btn-secondary disabled" title="{% trans "Suspend" %}" disabled>
 | 
			
		||||
            <span class="fa fa-pause"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary disabled" title="{% trans "Power Off" %}" disabled>
 | 
			
		||||
            <span class="fa fa-power-off"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary disabled" title="{% trans "Power Cycle" %}" disabled>
 | 
			
		||||
            <span class="fa fa-refresh"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary disabled" title="{% trans "Suspend" %}">{% icon 'pause' %}</a>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary disabled" title="{% trans "Power Off" %}">{% icon 'power-off' %}</a>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary disabled" title="{% trans "Power Cycle" %}">{% icon 'refresh' %}</a>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary disabled" title="{% trans "VNC Console" %}" disabled>
 | 
			
		||||
            <span class="fa fa-eye"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% if vm.status == 3 %}
 | 
			
		||||
        <button class="btn btn-sm btn-secondary" type="submit" name="resume" title="{% trans "Resume" %}">
 | 
			
		||||
            <span class="fa fa-play"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary disabled" title="{% trans "Suspend" %}" disabled>
 | 
			
		||||
            <span class="fa fa-pause"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary" type="submit" name="powerforce" title="{% trans "Force Off" %}" onclick="return confirm('Are you sure to force it down?')">
 | 
			
		||||
            <span class="fa fa-power-off"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary disabled" title="{% trans "Power Cycle" %}" disabled>
 | 
			
		||||
            <span class="fa fa-refresh"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
    {% if instance.proxy.instance.info.0 == 3 %}
 | 
			
		||||
        <a class="btn btn-sm btn-secondary" href="{% url 'instances:resume' instance.id %}" title="{% trans "Resume" %}">
 | 
			
		||||
            {% icon 'play' %}
 | 
			
		||||
        </a>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary disabled" title="{% trans "Suspend" %}">{% icon 'pause' %}</a>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary" href="{% url 'instances:force_off' instance.id %}" title="{% trans "Force Off" %}">
 | 
			
		||||
            {% icon 'power-off' %}
 | 
			
		||||
        </a>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary disabled" title="{% trans "Power Cycle" %}">{% icon 'refresh' %}</a>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary disabled" title="{% trans "VNC Console" %}" disabled>
 | 
			
		||||
            <span class="fa fa-eye"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% if vm.status == 1 %}
 | 
			
		||||
        <button class="btn btn-sm btn-secondary disabled" title="{% trans "Power On" %}" disabled>
 | 
			
		||||
            <span class="fa fa-play"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary" type="submit" name="suspend" title="{% trans "Suspend" %}">
 | 
			
		||||
            <span class="fa fa-pause"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary" type="submit" name="poweroff" title="{% trans "Power Off" %}" onclick="return confirm('Are you sure?')">
 | 
			
		||||
            <span class="fa fa-power-off"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary" type="submit" name="powercycle" title="{% trans "Power Cycle" %}" onclick="return confirm('Are you sure?')">
 | 
			
		||||
            <span class="fa fa-refresh"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary" type="button" onclick='open_console("{{ host.0 }}-{{ vm.uuid }}")' title="{% trans "Console" %}">
 | 
			
		||||
    {% if instance.proxy.instance.info.0 == 1 %}
 | 
			
		||||
        <a class="btn btn-sm btn-secondary disabled" title="{% trans "Power On" %}">{% icon 'play' %}</a>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary" href="{% url 'instances:suspend' instance.id %}"
 | 
			
		||||
            title="{% trans "Suspend" %}">{% icon 'pause' %}</a>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary" href="{% url 'instances:poweroff' instance.id %}">{% icon 'power-off' %}</a>
 | 
			
		||||
        <a class="btn btn-sm btn-secondary" href="{% url 'instances:powercycle' instance.id %}">{% icon 'refresh' %}</a>
 | 
			
		||||
        <button class="btn btn-sm btn-secondary" type="button" onclick='open_console("{{ instance.compute.id }}-{{ instance.get_uuid }}")'
 | 
			
		||||
            title="{% trans "Console" %}">
 | 
			
		||||
            <span class="fa fa-eye"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,135 +0,0 @@
 | 
			
		|||
{% extends "base.html" %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load staticfiles %}
 | 
			
		||||
{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
 | 
			
		||||
{% block style %}
 | 
			
		||||
    <link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <!-- Page Heading -->
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-lg-12">
 | 
			
		||||
            {% if request.user.is_superuser %}
 | 
			
		||||
                <a href="{% url 'create_instance_select_type' compute.id %}" type="button" class="btn btn-success btn-header float-right">
 | 
			
		||||
                    <span class="fa fa-plus" aria-hidden="true"></span>
 | 
			
		||||
                </a>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if all_host_vms or all_user_vms %}
 | 
			
		||||
                <div class="float-right search">
 | 
			
		||||
                    <input id="filter" class="form-control" type="text" placeholder="{% trans 'Search' %}">
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <h2 class="page-header">{{ compute.name }}</h2>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="row">
 | 
			
		||||
       <div class="col-lg-12">
 | 
			
		||||
            <nav aria-label="breadcrumb">
 | 
			
		||||
                <ol class="breadcrumb bg-light shadow-sm">
 | 
			
		||||
                    <li class="breadcrumb-item active">
 | 
			
		||||
                        <a href="{% url 'overview' compute.id %}"><i class="fa fa-dashboard"></i> {% trans "Overview" %}</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="breadcrumb-item">
 | 
			
		||||
                        <span class="font-weight-bold"><i class="fa fa-server"></i> {% trans "Instances" %}</span>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="breadcrumb-item">
 | 
			
		||||
                        <a href="{% url 'storages' compute.id %}"><i class="fa fa-hdd-o"></i> {% trans "Storages" %}</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="breadcrumb-item">
 | 
			
		||||
                        <a href="{% url 'networks' compute.id %}"><i class="fa fa-sitemap"></i> {% trans "Networks" %}</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="breadcrumb-item">
 | 
			
		||||
                        <a href="{% url 'interfaces' compute.id %}"><i class="fa fa-wifi"></i> {% trans "Interfaces" %}</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="breadcrumb-item">
 | 
			
		||||
                        <a href="{% url 'nwfilters' compute.id %}"><i class="fa fa-filter"></i> {% trans "NWFilters" %}</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="breadcrumb-item">
 | 
			
		||||
                        <a href="{% url 'secrets' compute.id %}"><i class="fa fa-key"></i> {% trans "Secrets" %}</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                </ol>
 | 
			
		||||
            </nav>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- /.row -->
 | 
			
		||||
    {% include 'errors_block.html' %}
 | 
			
		||||
    {% include 'messages_block.html' %}
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        {% if not  all_host_vms %}
 | 
			
		||||
            <div class="col-lg-12">
 | 
			
		||||
                <div class="alert alert-warning alert-dismissable fade show">
 | 
			
		||||
                    <i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning" %}:</strong> {% trans "Hypervisor doesn't have any Instances" %}
 | 
			
		||||
                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% else %}
 | 
			
		||||
            <div class="col-lg-12">
 | 
			
		||||
                <table class="table table-hover sortable-theme-bootstrap" data-sortable>
 | 
			
		||||
                    <thead>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <th scope="col">{% trans 'Name' %}<br>{% trans 'Description' %}</th>
 | 
			
		||||
                        <th scope="col" class="d-none d-md-table-cell">{% trans 'User' %}</th>
 | 
			
		||||
                        <th scope="col">{% trans 'Status' %}</th>
 | 
			
		||||
                        <th scope="col">{% trans 'VCPU' %}</th>
 | 
			
		||||
                        <th scope="col">{% trans 'Memory' %}</th>
 | 
			
		||||
                        <th scope="col" style="width:200px;" data-sortable="false">{% trans 'Actions' %}</th>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    </thead>
 | 
			
		||||
                    <tbody class="searchable">
 | 
			
		||||
                        {% for host, insts  in all_host_vms.items %}
 | 
			
		||||
                            {% for inst, vm in insts.items %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td><a class="text-secondary" href="{% url 'instances:instance' host.0 inst %}">{{ inst }}</a><br><small><em>{{ vm.title }}</em></small></td>
 | 
			
		||||
                                    <td class="d-none d-md-table-cell"><small><em>{% if vm.userinstances.count > 0 %}{{ vm.userinstances.first_user.user.username }}{% if vm.userinstances.count > 1 %} (+{{ vm.userinstances.count|add:"-1" }}){% endif %}{% endif %}</em></small></td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {% if vm.status == 1 %}<span class="text-success">{% trans "Active" %}</span>{% endif %}
 | 
			
		||||
                                        {% if vm.status == 5 %}<span class="text-danger">{% trans "Off" %}</span>{% endif %}
 | 
			
		||||
                                        {% if vm.status == 3 %}<span class="text-warning">{% trans "Suspend" %}</span>{% endif %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td>{{ vm.vcpu }}</td>
 | 
			
		||||
                                    <td>{{ vm.memory|filesizeformat }}</td>
 | 
			
		||||
                                    <td class="text-nowrap">
 | 
			
		||||
                                        {% include 'instance_actions.html' %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </tbody>
 | 
			
		||||
                </table>
 | 
			
		||||
             </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% block script %}
 | 
			
		||||
<script src="{% static "js/sortable.min.js" %}"></script>
 | 
			
		||||
<script>
 | 
			
		||||
    function open_console(uuid) {
 | 
			
		||||
        window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=685");
 | 
			
		||||
    }
 | 
			
		||||
</script>
 | 
			
		||||
<script>
 | 
			
		||||
    function filter_table() {
 | 
			
		||||
        var rex = new RegExp($(this).val(), 'i');
 | 
			
		||||
        $('.searchable tr').hide();
 | 
			
		||||
        $('.searchable tr').filter(function () {
 | 
			
		||||
            return rex.test($(this).text());
 | 
			
		||||
        }).show();
 | 
			
		||||
        Cookies.set("instances_filter", $(this).val(), { expires: 1 });
 | 
			
		||||
    }
 | 
			
		||||
    $(document).ready(function () {
 | 
			
		||||
        instances_filter_cookie = Cookies.get("instances_filter");
 | 
			
		||||
        if (instances_filter_cookie) {
 | 
			
		||||
            $('#filter').val(instances_filter_cookie);
 | 
			
		||||
            $('#filter').each(filter_table);
 | 
			
		||||
        }
 | 
			
		||||
        (function ($) {
 | 
			
		||||
            $('#filter').keyup(filter_table)
 | 
			
		||||
        }(jQuery));
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
<script>
 | 
			
		||||
    function goto_instance_clone(compute, instance) {
 | 
			
		||||
        window.location = "/instances/" + compute + "/" + instance + "/#clone";
 | 
			
		||||
    }
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										114
									
								
								instances/templates/instances/access_tab.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								instances/templates/instances/access_tab.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,114 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
<div role="tabpanel" class="tab-pane" id="access" aria-label="Instance access options">
 | 
			
		||||
    <div role="tabpanel">
 | 
			
		||||
        <!-- Nav tabs -->
 | 
			
		||||
        <ul class="nav nav-tabs" role="tablist">
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary active" href="#vnconsole" aria-controls="vnconsole" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "Console" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            {% if app_settings.SHOW_ACCESS_ROOT_PASSWORD == 'True' %}
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary" href="#rootpasswd" aria-controls="rootpasswd" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "Root Password" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if app_settings.SHOW_ACCESS_SSH_KEYS == 'True' %}
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary" href="#sshkeys" aria-controls="sshkeys" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "SSH Keys" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if instance.status == 1 %}
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary" href="#vdiconsole" aria-controls="vdiconsole" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "VDI" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- Tab panes -->
 | 
			
		||||
        <div class="tab-content">
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="vnconsole">
 | 
			
		||||
                <p>{% trans "This action opens a new window with a VNC connection to the console of the instance." %}</p>
 | 
			
		||||
                {% 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>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <button class="btn btn-lg btn-success float-right disabled">{% trans "Console" %}</button>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% if app_settings.SHOW_ACCESS_SSH_KEYS == '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>
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										49
									
								
								instances/templates/instances/destroy_instance_form.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								instances/templates/instances/destroy_instance_form.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}
 | 
			
		||||
{% trans "Confirm Destroy" %}
 | 
			
		||||
{% endblock title %}
 | 
			
		||||
 | 
			
		||||
{% block page_header %}
 | 
			
		||||
{% trans "Destroy instance" %} {{ instance }}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
{% if request.user.is_superuser or userinstance.is_delete %}
 | 
			
		||||
    {% if instance.status == 3 %}
 | 
			
		||||
        <div class="alert alert-danger">
 | 
			
		||||
            {% trans "Instance is suspended, cannot destroy!" %}
 | 
			
		||||
        </div>
 | 
			
		||||
    {% else %}
 | 
			
		||||
        <div class="alert alert-danger">
 | 
			
		||||
            {% trans "This action is irreversible!" %}
 | 
			
		||||
        </div>
 | 
			
		||||
        <form action="{% url 'instances:destroy' instance.id %}" class="form" method="post" role="form" id="delete_form">
 | 
			
		||||
            {% csrf_token %}
 | 
			
		||||
            <div class="ml-3 form-row">
 | 
			
		||||
                <div class="custom-control custom-switch">
 | 
			
		||||
                    <input class="custom-control-input" type="checkbox" name="delete_disk" value="true" checked id="delete_disk">
 | 
			
		||||
                    <label class="custom-control-label font-weight-bold" for="delete_disk">{% trans "Remove Instance's data" %}</label>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% if instance.nvram %}
 | 
			
		||||
            <div class="ml-3 form-row">
 | 
			
		||||
                <div class="custom-control custom-switch">
 | 
			
		||||
                    <input class="custom-control-input" type="checkbox" name="delete_nvram" value="true" id="delete_nvram" checked>
 | 
			
		||||
                    <label class="custom-control-label font-weight-bold" for="delete_nvram">
 | 
			
		||||
                        {% trans "Remove Instance's NVRAM" %}
 | 
			
		||||
                    </label>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <button type="submit" class="btn btn-lg btn-success float-right" name="delete">
 | 
			
		||||
                {% trans "Destroy" %}
 | 
			
		||||
            </button>
 | 
			
		||||
        </form>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
{% else %}
 | 
			
		||||
    <div class="alert alert-danger">
 | 
			
		||||
        {% trans "You cannot destroy instance!" %}
 | 
			
		||||
    </div>
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% endblock content %}
 | 
			
		||||
							
								
								
									
										28
									
								
								instances/templates/instances/destroy_tab.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								instances/templates/instances/destroy_tab.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
<div role="tabpanel" class="tab-pane" id="undefine">
 | 
			
		||||
    <div role="tabpanel">
 | 
			
		||||
        <!-- Nav tabs -->
 | 
			
		||||
        <ul class="nav nav-tabs" role="tablist" aria-label="Instance destroy menu">
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link active" href="#destroy" aria-controls="destroy" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "Destroy Instance" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- Tab panes -->
 | 
			
		||||
        <div class="tab-content">
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy">
 | 
			
		||||
                {% if request.user.is_superuser or userinstance.is_delete %}
 | 
			
		||||
                    {% if instance.status == 3 %}
 | 
			
		||||
                        <a class="btn btn-lg btn-success disabled float-right">{% trans "Destroy" %}</a>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <a href="{% url 'instances:destroy' instance.id %}" class="btn btn-lg btn-success float-right">{% trans "Destroy" %}</a>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <button class="btn btn-lg btn-success disabled float-right" name="delete">{% trans "Destroy" %}</button>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										131
									
								
								instances/templates/instances/power_tab.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								instances/templates/instances/power_tab.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,131 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
<div role="tabpanel" class="tab-pane active" id="power">
 | 
			
		||||
    <div role="tabpanel">
 | 
			
		||||
        <!-- Nav tabs -->
 | 
			
		||||
        <ul class="nav nav-tabs" role="tablist" aria-label="Instance power actions">
 | 
			
		||||
            {% if instance.status == 1 %}
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary active" href="#poweroff" aria-controls="poweroff" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Power Off" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#powercycle" aria-controls="powercycle" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Power Cycle" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#powerforce" aria-controls="powerforce" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Force Off" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
                {% if request.user.is_superuser %}
 | 
			
		||||
                    <li class="nav-item">
 | 
			
		||||
                        <a class="nav-link text-secondary" href="#suspend" aria-controls="suspend" role="tab" data-toggle="tab">
 | 
			
		||||
                            {% trans "Suspend" %}
 | 
			
		||||
                        </a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if instance.status == 3 %}
 | 
			
		||||
                {% if request.user.is_superuser %}
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#resume" aria-controls="resume" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Resume" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#powerforce" aria-controls="powerforce" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Force Off" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if instance.status == 5 %}
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary active" href="#boot" aria-controls="boot" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Power On" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- Tab panes -->
 | 
			
		||||
        <div class="tab-content">
 | 
			
		||||
            {% if instance.status == 1 %}
 | 
			
		||||
                <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="poweroff">
 | 
			
		||||
                    <p>{% trans "This action sends an ACPI shutdown signal to the instance." %}</p>
 | 
			
		||||
                    <form action="{% url 'instances:poweroff' instance.id %}" method="post" role="form" aria-label0="Power off instance form">
 | 
			
		||||
                        {% csrf_token %}
 | 
			
		||||
                        <input type="submit" name="poweroff" class="btn btn-lg btn-success float-right" value="{% trans "Power Off" %}">
 | 
			
		||||
                        <div class="clearfix"></div>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div role="tabpanel" class="tab-pane tab-pane-bordered" id="powercycle">
 | 
			
		||||
                    <p>{% trans "This action forcibly powers off and start the instance and may cause data corruption." %}</p>
 | 
			
		||||
                    <form action="{% url 'instances:powercycle' instance.id %}" method="post" role="form" aria-label="Power cycle instance form">{% csrf_token %}
 | 
			
		||||
                        <input type="submit" name="powercycle" class="btn btn-lg btn-success float-right" value="{% trans "Power Cycle" %}">
 | 
			
		||||
                        <div class="clearfix"></div>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div role="tabpanel" class="tab-pane tab-pane-bordered" id="powerforce">
 | 
			
		||||
                    <p>{% trans "This action forcibly powers off the instance and may cause data corruption." %}</p>
 | 
			
		||||
                    <form action="{% url 'instances:force_off' instance.id %}" method="post" role="form" aria-label="Force to shotdown instance form">
 | 
			
		||||
                        {% csrf_token %}
 | 
			
		||||
                        <input type="submit" name="powerforce" class="btn btn-lg btn-success float-right" value="{% trans "Force Off" %}">
 | 
			
		||||
                        <div class="clearfix"></div>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
                {% if request.user.is_superuser %}
 | 
			
		||||
                    <div role="tabpanel" class="tab-pane tab-pane-bordered" id="suspend">
 | 
			
		||||
                        <p>{% trans "This action suspends the instance." %}</p>
 | 
			
		||||
                        <form action="{% url 'instances:suspend' instance.id %}" method="post" role="form" aria-label="Suspend instance form">{% csrf_token %}
 | 
			
		||||
                            <input type="submit" name="suspend" class="btn btn-lg btn-success float-right" value="{% trans "Suspend" %}">
 | 
			
		||||
                            <div class="clearfix"></div>
 | 
			
		||||
                        </form>
 | 
			
		||||
                    </div>
 | 
			
		||||
                {% 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>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										164
									
								
								instances/templates/instances/resize_tab.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								instances/templates/instances/resize_tab.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,164 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
<div role="tabpanel" class="tab-pane" id="resize">
 | 
			
		||||
    <div role="tabpanel">
 | 
			
		||||
        <!-- Nav tabs -->
 | 
			
		||||
        <ul class="nav nav-tabs" role="tablist" aria-label="Instance resize options">
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary active" href="#resizevm_cpu" aria-controls="resizevm_cpu" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "CPU" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary" href="#resizevm_mem" aria-controls="resizevm_mem" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "Memory" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary" href="#resizevm_disk" aria-controls="resizevm_disk" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "Disk" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- Tab panes -->
 | 
			
		||||
        <div class="tab-content">
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resizevm_cpu">
 | 
			
		||||
                {% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
 | 
			
		||||
                    {% if instance.status == 5 or not instance.vcpus %}
 | 
			
		||||
                    <form action="{% url 'instances:resizevm_cpu' instance.id %}" method="post" role="form" aria-label="Resize instance cpu form">{% csrf_token %}
 | 
			
		||||
                        <p class="font-weight-bold">{% trans "Logical host CPUs" %} : {{ vcpu_host }}</p>
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-4 col-form-label"> {% trans "Current Allocation" %}</label>
 | 
			
		||||
                            <div class="col-sm-4">
 | 
			
		||||
                                <select name="cur_vcpu" class="custom-select">
 | 
			
		||||
                                    {% for cpu in instance.vcpu_range %}
 | 
			
		||||
                                        {% if instance.cur_vcpu %}
 | 
			
		||||
                                            <option value="{{ cpu }}" {% if cpu == instance.cur_vcpu %}selected{% endif %}>{{ cpu }}</option>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <option value="{{ cpu }}" {% if cpu == instance.vcpu %}selected{% endif %}>{{ cpu }}</option>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    {% endfor %}
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <label class="col-sm-4 col-form-label">{% trans "Maximum Allocation" %}</label>
 | 
			
		||||
                            <div class="col-sm-4">
 | 
			
		||||
                                <select name="vcpu" class="custom-select">
 | 
			
		||||
                                    {% for cpu in instance.vcpu_range %}
 | 
			
		||||
                                        <option value="{{ cpu }}" {% if cpu == instance.vcpu %}selected{% endif %}>{{ cpu }}</option>
 | 
			
		||||
                                    {% endfor %}
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
                        {% if instance.status == 5 %}
 | 
			
		||||
                            <button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_cpu">{% trans "Resize" %}</button>
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            <button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </form>
 | 
			
		||||
                    <div class="clearfix"></div>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <p class="font-weight-bold">{% trans "Logical Instance Active/Maximum CPUs" %} : {{ instance.cur_vcpu }} / {{ instance.vcpu }} </p>
 | 
			
		||||
                        <div class="col-sm-3"></div>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            {% for id, vcpu in instance.vcpus.items %}
 | 
			
		||||
                                <form action="{% url 'instances:set_vcpu' instance.id %}" method="post" role="form" aria-label="Resize instance cpu form">{% csrf_token %}
 | 
			
		||||
                                <div class="col-sm-3">
 | 
			
		||||
                                    <input name="id" value="{{ id }}" hidden/>
 | 
			
		||||
                                    {% if vcpu.enabled == 'yes' and vcpu.hotpluggable == "yes" %}
 | 
			
		||||
                                            <button type="submit" class="btn btn-block btn-success" value="False" name="set_vcpu" title="{% trans "Disable" %}">{{ id }}</button>
 | 
			
		||||
                                    {% elif vcpu.enabled == 'yes' and vcpu.hotpluggable == "no" %}
 | 
			
		||||
                                            <button type="button" class="btn btn btn-block btn-info" title="{% trans "Constant" %}">{{ id }}</button>
 | 
			
		||||
                                    {% else %}
 | 
			
		||||
                                            <button type="submit" class="btn btn btn-block btn-secondary" value="True" name="set_vcpu" title="{% trans "Enable" %}">{{ id }}</button>
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                </div>
 | 
			
		||||
                                </form>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-sm-3"></div>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    {% trans "You don't have permission for resizing instance" %}
 | 
			
		||||
                    <button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <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>
 | 
			
		||||
							
								
								
									
										904
									
								
								instances/templates/instances/settings_tab.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										904
									
								
								instances/templates/instances/settings_tab.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,904 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load bootstrap4 %}
 | 
			
		||||
{% load icons %}
 | 
			
		||||
<div role="tabpanel" class="tab-pane" id="settings">
 | 
			
		||||
    <div role="tabpanel">
 | 
			
		||||
        <!-- Nav tabs -->
 | 
			
		||||
        <ul class="nav nav-tabs" role="tablist" aria-label="Instance settings">
 | 
			
		||||
            {% if request.user.is_superuser %}
 | 
			
		||||
                <li class="nav-item ">
 | 
			
		||||
                    <a class="nav-link text-secondary active" href="#boot_opt" aria-controls="boot" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Boot" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#disks" aria-controls="disks" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Disk" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if request.user.is_superuser or request.user.is_staff or userinstance.is_vnc %}
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#vncsettings" aria-controls="vncsettings" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Console" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if request.user.is_superuser %}
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#network" aria-controls="network" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Network" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if perms.instances.clone_instances %}
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#clone" aria-controls="clone" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Clone" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if request.user.is_superuser %}
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#migrate" aria-controls="migrate" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Migrate" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#xmledit" aria-controls="xmledit" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "XML" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if perms.instances.clone_instances %}
 | 
			
		||||
                <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#options" aria-controls="options" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Options" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if request.user.is_superuser %}
 | 
			
		||||
                    <li class="nav-item">
 | 
			
		||||
                    <a class="nav-link text-secondary" href="#users" aria-controls="users" role="tab" data-toggle="tab">
 | 
			
		||||
                        {% trans "Users" %}
 | 
			
		||||
                    </a>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- Tab panes -->
 | 
			
		||||
        <div class="tab-content">
 | 
			
		||||
            {% if request.user.is_superuser %}
 | 
			
		||||
                <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot_opt">
 | 
			
		||||
                    <p class="font-weight-bold">{% trans 'Autostart' %}</p>
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <div class="col-sm-12 text-center">
 | 
			
		||||
                            <p>{% trans "Autostart your instance when host server is power on " %}
 | 
			
		||||
                                {% if instance.autostart == 0 %}
 | 
			
		||||
                                    <a class="btn btn-success" href="{% url 'instances:set_autostart' instance.id %}">{% trans "Enable" %}</a>
 | 
			
		||||
                                {% else %}
 | 
			
		||||
                                    <a class="btn btn-danger" href="{% url 'instances:unset_autostart' instance.id %}">{% trans "Disable" %}</a>
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            </p>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    <p class="font-weight-bold">{% trans 'Boot Order' %}</p>
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <div class="col-sm-12 text-center">
 | 
			
		||||
                            {% if instance.status == 5 %}
 | 
			
		||||
                                <p>{% trans "Enable Boot Menu for your instance when it starts up " %}
 | 
			
		||||
                                {% if instance.bootmenu == 0 %}
 | 
			
		||||
                                    <form action="{% url 'instances:set_bootmenu' instance.id %}" method="post" role="form" aria-label="Enable/disable instance boot order form">{% csrf_token %}
 | 
			
		||||
                                        <input type="submit" class="btn btn-success" name="set_bootmenu" title="{% trans 'Show boot menu' %}" value="{% trans "Enable" %}">
 | 
			
		||||
                                    </form>
 | 
			
		||||
                                {% else %}
 | 
			
		||||
                                    <form action="{% url 'instances:unset_bootmenu' instance.id %}" method="post" role="form" aria-label="Enable/disable instance boot order form">{% csrf_token %}
 | 
			
		||||
                                        <input type="submit" class="btn btn-danger" name="unset_bootmenu" title="{% trans 'Hide boot menu' %}" value="{% trans "Disable" %}">
 | 
			
		||||
                                    </form>
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            {% else %}
 | 
			
		||||
                                {% if instance.bootmenu == 0  %}
 | 
			
		||||
                                    <p>**** {% trans "Please shutdown instance to modify boot menu" %} ****</p>
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
                    {% if instance.bootmenu == 1  %}
 | 
			
		||||
                        <div class="d-flex justify-content-center">
 | 
			
		||||
                            <div class="col-sm-6 bg-light rounded shadow-sm">
 | 
			
		||||
                                {% for idx, val in instance.boot_order.items %}
 | 
			
		||||
                                    <label>{{ idx|add:1 }}:{{ val.target }}, </label>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <form action="{% url 'instances:set_bootorder' instance.id %}" method="post" role="form" aria-label="Boot order edit form">{% csrf_token %}
 | 
			
		||||
                            <input id="bootorder" name="bootorder" hidden>
 | 
			
		||||
                            <div class="d-flex justify-content-center">
 | 
			
		||||
                                <div id="b_order" class="multipleselect border-0">
 | 
			
		||||
                                    {% for disk in instance.disks %}
 | 
			
		||||
                                        <label><input type="checkbox" name="disk:{{ disk.dev }}" value="disk:{{ disk.dev }}" onclick="set_orderlist($('#bootorder'))" />{{ disk.dev }} - {{ disk.image }}</label>
 | 
			
		||||
                                    {% endfor %}
 | 
			
		||||
                                    {% for cd in instance.media %}
 | 
			
		||||
                                        <label><input type="checkbox" name="cdrom:{{ cd.dev }}" value="cdrom:{{ cd.dev }}" onclick="set_orderlist($('#bootorder'))"/>{{ cd.dev }} - {{ cd.image }}</label>
 | 
			
		||||
                                    {% endfor %}
 | 
			
		||||
                                    {% for net in instance.networks %}
 | 
			
		||||
                                        <label><input type="checkbox" name="network:{{ net.mac }}" value="network:{{ net.mac }}" onclick="set_orderlist($('#bootorder'))"/>NIC - {{ net.mac|slice:"9:" }}</label>
 | 
			
		||||
                                    {% endfor %}
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div>
 | 
			
		||||
                                    <div class="row mt-4">
 | 
			
		||||
                                        <a href="#" id="boot_order_up" class="btn btn-light shadow-sm"><span class="fa fa-arrow-up" title="{% trans 'up: move selected devices' %}"></span></a>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                    <div class="row mt-2">
 | 
			
		||||
                                        <a href="#" id="boot_order_down" class="btn btn-light shadow-sm"><span class="fa fa-arrow-down" title="{% trans 'down: move selected devices' %}"></span></a>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="d-flex justify-content-center">
 | 
			
		||||
                                <div class="col-sm-6">
 | 
			
		||||
                                    <input type="submit" class="btn btn-success btn-block" name="set_bootorder" value="{% trans "Apply" %}">
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </form>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    <div class="clearfix"></div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div role="tabpanel" class="tab-pane tab-pane-bordered" id="disks">
 | 
			
		||||
                <form action="{% url 'instances:add_cdrom' instance.id %}" method="post" role="form" aria-label="Add CD-ROM form">{% csrf_token %}
 | 
			
		||||
                    <p class="font-weight-bold">
 | 
			
		||||
                        {% trans "Instance Media" %}
 | 
			
		||||
                        <button class="btn btn-success float-right"
 | 
			
		||||
                                type="submit" type="button"
 | 
			
		||||
                                title="{% trans 'Add CD-ROM' %}"
 | 
			
		||||
                                {% if instance.status != 5 %} disabled {% endif %}>
 | 
			
		||||
                            <span class="fa fa-plus"></span>
 | 
			
		||||
                        </button>
 | 
			
		||||
                    </p>
 | 
			
		||||
                </form>
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
                {% for cd in instance.media %}
 | 
			
		||||
                    <div class="row mt-2">
 | 
			
		||||
                        <a class="col-sm-3 col-form-label"
 | 
			
		||||
                                name="details"
 | 
			
		||||
                                title="{% trans "Details" %}"
 | 
			
		||||
                                tabindex="0"
 | 
			
		||||
                                data-trigger="focus"
 | 
			
		||||
                                data-toggle="popover"
 | 
			
		||||
                                data-html="true"
 | 
			
		||||
                                data-content="<strong>{% trans 'Bus' %}:</strong> {{ cd.bus }} <br/>
 | 
			
		||||
                                    <strong>{% trans 'Device' %}:</strong> {{ cd.dev }}">
 | 
			
		||||
                            {% trans "CD-ROM" %} {{ forloop.counter }}
 | 
			
		||||
                        </a>
 | 
			
		||||
                        <div class="col-sm-9">
 | 
			
		||||
                            {% if not cd.image %}
 | 
			
		||||
                                <form action="{% url 'instances:mount_iso' instance.id %}" method="post">
 | 
			
		||||
                                    {% csrf_token %}
 | 
			
		||||
                                    <div class="input-group">
 | 
			
		||||
                                        <select name="media" class="form-control">
 | 
			
		||||
                                        {% if instance.media_iso %}
 | 
			
		||||
                                            {% for iso in instance.media_iso %}
 | 
			
		||||
                                                <option value="{{ iso }}">{{ iso }}</option>
 | 
			
		||||
                                            {% endfor %}
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <option value="none">{% trans "None" %}</option>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                        </select>
 | 
			
		||||
                                        <div class="input-group-append">
 | 
			
		||||
                                        {% if instance.media_iso and allow_admin_or_not_template %}
 | 
			
		||||
                                            <button type="submit" class="btn btn-sm btn-success float-left" name="mount_iso" value="{{ cd.dev }}">{% trans "Mount" %}</button>
 | 
			
		||||
                                        {% else %}
 | 
			
		||||
                                            <button class="btn btn-sm btn-success float-left disabled">{% trans "Mount" %}</button>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                        {% if instance.status == 5 and allow_admin_or_not_template %}
 | 
			
		||||
                                            <a  href="{% url 'instances:detach_cdrom' instance.id cd.dev %}" class="btn btn-sm btn-danger float-right" title="{% trans "Detach CD-ROM (remove device)" %}">
 | 
			
		||||
                                                {% icon 'remove' %}
 | 
			
		||||
                                            </a>
 | 
			
		||||
                                        {% endif %}  
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </form>
 | 
			
		||||
                            {% else %}
 | 
			
		||||
                                <form action="{% url 'instances:unmount_iso' instance.id %}" method="post">
 | 
			
		||||
                                    {% csrf_token %}
 | 
			
		||||
                                    <div class="input-group">
 | 
			
		||||
                                        <input type="text" class="form-control" value="{{ cd.image }}" disabled/>
 | 
			
		||||
                                        <div class="input-group-append">
 | 
			
		||||
                                            <input type="hidden" name="path" value="{{ cd.path }}">
 | 
			
		||||
                                            {% if allow_admin_or_not_template %}
 | 
			
		||||
                                                <button type="submit" class="btn btn-sm btn-success float-left" value="{{ cd.dev }}" name="umount_iso">{% trans "Unmount" %}</button>
 | 
			
		||||
                                            {% else %}
 | 
			
		||||
                                                <button class="btn btn-sm btn-success float-left disabled" value="{{ cd.dev }}" name="umount_iso">{% trans "Unmount" %}</button>
 | 
			
		||||
                                            {% endif %}                                                       
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </form>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                {% empty %}
 | 
			
		||||
                    <div class="offset-3 col-sm-6">
 | 
			
		||||
                        <div class="bg-light rounded shadow-sm">{% trans 'There is not any CD-ROM device.' %}</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
                <p class="font-weight-bold">
 | 
			
		||||
                    {% trans "Instance Volume" %}
 | 
			
		||||
                    {% include 'add_instance_volume.html' %}
 | 
			
		||||
                </p>
 | 
			
		||||
 | 
			
		||||
                <div class="col-12 col-sm-12">
 | 
			
		||||
                    <div class="table-responsive">
 | 
			
		||||
                        <table class="table table-hover mt-3">
 | 
			
		||||
                            <thead>
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <th scope="col">{% trans "Device" %}</th>
 | 
			
		||||
                                    <th scope="col">{% trans "Used" %}</th>
 | 
			
		||||
                                    <th scope="col">{% trans "Capacity" %}</th>
 | 
			
		||||
                                    <th scope="col">{% trans "Storage" %}</th>
 | 
			
		||||
                                    <th scope="col">{% trans "Source" %}</th>
 | 
			
		||||
                                    <th scope="col">{% trans "Action" %}</th>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            </thead>
 | 
			
		||||
                            <tbody>
 | 
			
		||||
                            {% for disk in instance.disks %}
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>
 | 
			
		||||
                                            <button type="submit" class="btn btn-sm btn-secondary"
 | 
			
		||||
                                                    name="details{{ forloop.counter0 }}"
 | 
			
		||||
                                                    title="{% trans "Details" %}"
 | 
			
		||||
                                                    tabindex="0"
 | 
			
		||||
                                                    data-trigger="focus"
 | 
			
		||||
                                                    data-toggle="popover"
 | 
			
		||||
                                                    data-html="true"
 | 
			
		||||
                                                    data-content="<strong>Bus:</strong> {{ disk.bus }} <br/>
 | 
			
		||||
                                                                    <strong>Format:</strong> {{ disk.format }} <br/>
 | 
			
		||||
                                                                    <strong>Cache:</strong> {{ disk.cache }} <br/>
 | 
			
		||||
                                                                    <strong>Serial:</strong> {{ disk.serial }} <br/>
 | 
			
		||||
                                                                    <strong>Readonly:</strong> {{ disk.readonly }} <br/>
 | 
			
		||||
                                                                    <strong>Shareable:</strong> {{ disk.shareable }}</br>
 | 
			
		||||
                                                                    <strong>IO Mode:</strong> {{ disk.io }} <br/>
 | 
			
		||||
                                                                    <strong>Discard:</strong> {{ disk.discard }} <br/>
 | 
			
		||||
                                                                    <strong>Detect Zeroes:</strong> {{ disk.detect_zeroes }}">
 | 
			
		||||
                                                <i class="fa fa-info"></i>
 | 
			
		||||
                                            </button>
 | 
			
		||||
                                            {{ disk.dev }}                                                            
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                    {% if disk.storage is None %}
 | 
			
		||||
                                        <td colspan="4">
 | 
			
		||||
                                            <div class="alert alert-danger">
 | 
			
		||||
                                                {% trans "Error getting disk info" %}
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                    {% else %}
 | 
			
		||||
                                        <td>{{ disk.used | filesizeformat}}</td>
 | 
			
		||||
                                        <td>{{ disk.size | filesizeformat }}</td>
 | 
			
		||||
                                        <td>{{ disk.storage }}</td>
 | 
			
		||||
                                        <td>{{ disk.path }}</td>
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                        <td class="text-nowrap">
 | 
			
		||||
                                            <form class="d-inline" action="{% url 'instances:edit_volume' instance.id %}" method="post" role="form" aria-label="Edit instance volume form">
 | 
			
		||||
                                                {% csrf_token %}
 | 
			
		||||
                                                <input type="hidden" name="path" value="{{ disk.path }}">
 | 
			
		||||
                                                <input type="hidden" name="dev" value="{{ disk.dev }}">
 | 
			
		||||
                                                <input type="hidden" name="storage" value="{{ disk.storage }}">
 | 
			
		||||
                                                <input type="hidden" name="name" value="{{ disk.image }}">
 | 
			
		||||
                                                {% include 'edit_instance_volume.html' with id=forloop.counter0 %}
 | 
			
		||||
                                            </form>
 | 
			
		||||
                                            <form class="d-inline" action="{% url 'instances:detach_vol' instance.id %}" method="post">
 | 
			
		||||
                                                {% csrf_token %}
 | 
			
		||||
                                                <input type="hidden" name="path" value="{{ disk.path }}">
 | 
			
		||||
                                                <input type="hidden" name="dev" value="{{ disk.dev }}">
 | 
			
		||||
                                                <input type="hidden" name="storage" value="{{ disk.storage }}">
 | 
			
		||||
                                                <input type="hidden" name="name" value="{{ disk.image }}">
 | 
			
		||||
                                                {% if instance.status == 5 %}
 | 
			
		||||
                                                    <button type="submit" class="btn btn-sm btn-secondary" value="{{ disk.dev }}" title="{% trans "Detach" %}" onclick="return confirm('{% trans "Are you sure to detach volume?" %}')">
 | 
			
		||||
                                                        {% icon 'eject' %}
 | 
			
		||||
                                                    </button>
 | 
			
		||||
                                                {% else %}
 | 
			
		||||
                                                    <button class="btn btn-sm btn-secondary disabled" value="{{ disk.dev }}" title="{% trans "Detach" %}" onclick="return confirm('{% trans "Are you sure? This may lead data corruption!" %}')">
 | 
			
		||||
                                                        {% icon 'eject' %}
 | 
			
		||||
                                                    </button>
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                            </form>
 | 
			
		||||
                                            <form class="d-inline" action="{% url 'instances:delete_vol' instance.id %}" method="post">
 | 
			
		||||
                                                {% csrf_token %}
 | 
			
		||||
                                                <input type="hidden" name="path" value="{{ disk.path }}">
 | 
			
		||||
                                                <input type="hidden" name="dev" value="{{ disk.dev }}">
 | 
			
		||||
                                                <input type="hidden" name="storage" value="{{ disk.storage }}">
 | 
			
		||||
                                                <input type="hidden" name="name" value="{{ disk.image }}">
 | 
			
		||||
                                                {% if instance.status == 5 %}
 | 
			
		||||
                                                    <button type="submit" class="btn btn-sm btn-secondary" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure to delete volume?" %}')">
 | 
			
		||||
                                                        <i class="fa fa-trash"></i>
 | 
			
		||||
                                                    </button>
 | 
			
		||||
                                                {% else %}
 | 
			
		||||
                                                    <button class="btn btn-sm btn-secondary disabled" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure? This may lead data corruption!" %}')">
 | 
			
		||||
                                                        <i class="fa fa-trash"></i>
 | 
			
		||||
                                                    </button>
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                            </form>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                            </tbody>
 | 
			
		||||
                        </table>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
 | 
			
		||||
            <p>
 | 
			
		||||
                {% trans "Add a network device" %}
 | 
			
		||||
                {% include 'add_instance_network_block.html' %}
 | 
			
		||||
            </p>
 | 
			
		||||
 | 
			
		||||
            <div class="row mt-3">
 | 
			
		||||
                <div class="col-lg-12 mt-3">
 | 
			
		||||
                <h5 class="font-weight-bold">{% trans "Network Devices" %}</h5>
 | 
			
		||||
                <table class="table">
 | 
			
		||||
                    <thead>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                        <th scope="col">{% trans 'Name' %}</th>
 | 
			
		||||
                        <th scope="col" class="d-none d-table-cell d-sm-table-cell" colspan="6">{% trans 'Info' %}</th>
 | 
			
		||||
                        <th scope="colgroup" class="d-none" colspan="2">{% trans 'Info' %}</th>
 | 
			
		||||
                        <th scope="colgroup" colspan="2">{% trans 'Actions' %}</th>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    </thead>
 | 
			
		||||
                    <tbody>
 | 
			
		||||
                    {% for network in instance.networks %}
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <td rowspan="2">eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})
 | 
			
		||||
                                <form action="{% url 'instances:set_link_state' instance.id %}" method="post" aria-label="set instance link state form">
 | 
			
		||||
                                    {% csrf_token %}
 | 
			
		||||
                                    <input name="mac" value="{{ network.mac }}" hidden/>
 | 
			
		||||
                                    <input name="set_link_state" value="{{ network.state }}" hidden/>
 | 
			
		||||
                                    <input type="checkbox" {% if network.state == 'up' %} checked{% endif %} onclick='submit();' />
 | 
			
		||||
                                    <strong>{% trans 'active' %}</strong>
 | 
			
		||||
                                </form>
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <th class="d-none d-table-cell d-sm-table-cell">{% trans 'MAC' %}</th>
 | 
			
		||||
                            <td>{{ network.mac }}</td>
 | 
			
		||||
                            <th scope="row" class="d-none d-table-cell d-sm-table-cell">{% trans 'Filter' %}</th>
 | 
			
		||||
                            <td class="d-none d-table-cell">{{ network.filterref|default:"None" }}</td>
 | 
			
		||||
                            <th scope="row" class="d-none d-table-cell d-sm-table-cell">{% trans 'Source' %}</th>
 | 
			
		||||
                            <td>{{ network.nic }}</td>
 | 
			
		||||
                            <td>
 | 
			
		||||
                            <form action="{% url 'instances:change_network' instance.id %}" method="post" name="edit_network{{ forloop.counter0 }}" role="form">{% csrf_token %}
 | 
			
		||||
                                <button data-target="#editInstanceNetwork{{ forloop.counter0 }}" type="button" class="btn btn-sm btn-primary"
 | 
			
		||||
                                        title="{% trans "Edit NIC" %}" data-toggle="modal">
 | 
			
		||||
                                        <span class="fa fa-edit" aria-hidden="true"></span>
 | 
			
		||||
                                </button>
 | 
			
		||||
 | 
			
		||||
                                <div class="modal fade" id="editInstanceNetwork{{ forloop.counter0 }}" role="dialog" aria-labelledby="editInstanceNetworkLabel" aria-hidden="true">
 | 
			
		||||
                                    <div class="modal-dialog">
 | 
			
		||||
                                        <div class="modal-content">
 | 
			
		||||
                                            <div class="modal-header">
 | 
			
		||||
                                                <h5 class="modal-title">{% trans "Edit Instance Network" %}</h5>
 | 
			
		||||
                                                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> 
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div class="modal-body">
 | 
			
		||||
                                                <div class="container">
 | 
			
		||||
                                                <div class="form-group row">
 | 
			
		||||
                                                    <label class="col-form-label">{% trans "MAC" %}</label>
 | 
			
		||||
                                                    <div class="input-group">
 | 
			
		||||
                                                        <input class="form-control" type="text" value="{{ network.mac }}" readonly/>
 | 
			
		||||
                                                        <input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
 | 
			
		||||
                                                    </div>                                                                    
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                                <div class="form-group row">
 | 
			
		||||
                                                    <label class="col-form-label">{% trans "Net Source" %}</label>
 | 
			
		||||
                                                    <div class="input-group">
 | 
			
		||||
                                                        <input class="form-control" type="text" value="{{ network.nic }}" readonly/>
 | 
			
		||||
                                                        <select class="form-control" name="net-source-{{ forloop.counter0 }}">
 | 
			
		||||
                                                            {% for c_net in networks_host %}
 | 
			
		||||
                                                                <option value="net:{{ c_net }}" {% if c_net == network.nic %} selected {% endif %}>{% trans 'Network' %} {{ c_net }}</option>
 | 
			
		||||
                                                            {% endfor %}
 | 
			
		||||
                                                            {% for c_iface in interfaces_host %}
 | 
			
		||||
                                                                <option value="iface:{{ c_iface }}" {% if c_iface == network.nic %} selected {% endif %}>{% trans 'Interface' %} {{ c_iface }}</option>
 | 
			
		||||
                                                            {% endfor %}
 | 
			
		||||
                                                        </select>
 | 
			
		||||
                                                    </div>                                      
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                                <div class="form-group row">
 | 
			
		||||
                                                    <label class="col-form-label">{% trans "NWFilter" %}</label>
 | 
			
		||||
                                                    <div class="input-group">
 | 
			
		||||
                                                        <input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
 | 
			
		||||
                                                        <select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
 | 
			
		||||
                                                            <option value="">{% trans "None" %}</option>
 | 
			
		||||
                                                            {% for c_filters in nwfilters_host %}
 | 
			
		||||
                                                                <option value="{{ c_filters }}" {% if c_filters == network.filterref  %} selected {% endif %}>{{ c_filters }}</option>
 | 
			
		||||
                                                            {% endfor %}
 | 
			
		||||
                                                        </select>
 | 
			
		||||
                                                    </div>
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                                <div class="form-group row">
 | 
			
		||||
                                                    <label class="col-form-label">{% trans "Model" %} </label>
 | 
			
		||||
                                                    <div class="input-group">
 | 
			
		||||
                                                        <input class="form-control" type="text" value="{{ network.model }}" readonly/>
 | 
			
		||||
                                                        <select class="form-control" name="net-model-{{ forloop.counter0 }}">
 | 
			
		||||
                                                            {% for model in net_models_host %}
 | 
			
		||||
                                                            <option value="{{ model }}" {% if model == network.model  %} selected {% endif %}>{{ model }}</option>
 | 
			
		||||
                                                            {% endfor %}
 | 
			
		||||
                                                        </select>
 | 
			
		||||
                                                    </div>
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div class="modal-footer">
 | 
			
		||||
                                                <button class="btn btn-secondary" data-dismiss="modal">{% trans 'Close' %}</button>
 | 
			
		||||
                                                <button class="btn btn-success" name="change_network" title="{% trans "Apply network changes" %}">{% trans "Apply" %}</button>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </form>
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td align="right">
 | 
			
		||||
                                <form action="{% url 'instances:delete_network' instance.id %}" method="post" name="delete_network" role="form">{% csrf_token %}
 | 
			
		||||
                                    <button class="btn btn-sm btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete Device" %}"
 | 
			
		||||
                                        onclick="return confirm('{% trans "Are you sure?" %}')">
 | 
			
		||||
                                        <i class="fa fa-trash"></i>
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                </form>
 | 
			
		||||
                            </td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <th scope="row">{% trans 'IPv4' %}</th>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                {% for ipv4 in network.ipv4|default:"unknown" %}{{ ipv4 }}{% endfor %}
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <th scope="row">{% trans 'IPv6' %}</th>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                {% for ipv6 in network.ipv6|default:"unknown" %}{{ ipv6 }}{% endfor %}
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <th scope="row">{% trans 'Model' %}</th>
 | 
			
		||||
                            <td>{{ network.model }}</td>
 | 
			
		||||
                            <th>{% trans 'QoS' %}</th>
 | 
			
		||||
                            <td class="d-flex justify-content-end">
 | 
			
		||||
                                <form action="{% url 'instances:set_qos' instance.id %}" method="post" name="add_qos{{ forloop.counter0 }}" role="form" aria-label="Add network qos form">
 | 
			
		||||
                                    {% csrf_token %}
 | 
			
		||||
                                    <input type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}" hidden/>
 | 
			
		||||
                                    {% include 'add_network_qos.html' with id=forloop.counter0 %}
 | 
			
		||||
                                </form>
 | 
			
		||||
                            </td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <td class="bg-primary" colspan="9"></td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                    </tbody>
 | 
			
		||||
                </table>
 | 
			
		||||
            </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            {% if instance.qos %}
 | 
			
		||||
            <div class="row mt-3">
 | 
			
		||||
                <div class="col-sm-10">
 | 
			
		||||
                    <p><strong>{% trans "QoS Configuration" %}</strong></p>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="col-12 col-sm-12">
 | 
			
		||||
                    <table class="table table-hover">
 | 
			
		||||
                        <thead>
 | 
			
		||||
                        <tr class="d-flex">
 | 
			
		||||
                            <th scope="col" class="col-2">{% trans "MAC" %}/{% trans "Direction" %}</th>
 | 
			
		||||
                            <th scope="col" class="col-2">{% trans "Average" %}</th>
 | 
			
		||||
                            <th scope="col" class="col-3">{% trans "Peak" %}</th>
 | 
			
		||||
                            <th scope="col" class="col-3">{% trans "Burst" %}</th>
 | 
			
		||||
                            <th scope="col" class="col-2">{% trans "Actions" %}</th>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                        </thead>
 | 
			
		||||
                        <tbody>
 | 
			
		||||
                        {% for q, attrs in instance.qos.items %}
 | 
			
		||||
                            {% for att in attrs  %}
 | 
			
		||||
                                <tr class="d-flex">
 | 
			
		||||
                                    <form action="{% url 'instances:set_qos' instance.id %}" method="post" role="form" aria-label="Instance QoS configuration form">
 | 
			
		||||
                                        {% csrf_token %}
 | 
			
		||||
                                        <td class="col-2"><label class="col-form-label">{{ q }} {{ att.direction | capfirst }}</label></td>
 | 
			
		||||
                                        <td class="col-2"><input id="qos_average" class="form-control" name="qos_average"
 | 
			
		||||
                                                value="{{ att.average|default:'' }}"/>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                        <td class="col-3"><input id="qos_peak" class="form-control" name="qos_peak"
 | 
			
		||||
                                                value="{{ att.peak|default:'' }}"/>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                        <td class="col-3"><input id="qos_burst" class="form-control" name="qos_burst"
 | 
			
		||||
                                                value="{{ att.burst|default:'' }}"/>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                        <td class="col-sm-2">
 | 
			
		||||
                                            <input name="qos_direction" value="{{ att.direction }}" hidden/>
 | 
			
		||||
                                            <input name="net-mac" value="{{ q }}" hidden/>
 | 
			
		||||
                                            <button type="submit" class="btn btn-sm btn-primary"
 | 
			
		||||
                                                    name="set_qos" data-toggle="modal"
 | 
			
		||||
                                                    title="{% trans "Edit QoS" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
 | 
			
		||||
                                                <i class="fa fa-save"></i>
 | 
			
		||||
                                            </button>
 | 
			
		||||
                                    </form>
 | 
			
		||||
                                            <form action="{% url 'instances:unset_qos' instance.id %}" method="post" role="form" aria-label="Instance QoS configuration form">
 | 
			
		||||
                                                {% csrf_token %}
 | 
			
		||||
                                                <input name="qos_direction" value="{{ att.direction }}" hidden/>
 | 
			
		||||
                                                <input name="net-mac" value="{{ q }}" hidden/>
 | 
			
		||||
                                                <button type="submit" class="btn btn-sm btn-danger"
 | 
			
		||||
                                                        name="unset_qos"
 | 
			
		||||
                                                        title="{% trans "Delete QoS" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
 | 
			
		||||
                                                    <i class="fa fa-trash"></i>
 | 
			
		||||
                                                </button>
 | 
			
		||||
                                            </form>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                        </tbody>
 | 
			
		||||
                    </table>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="migrate">
 | 
			
		||||
            <p>{% trans "For migration both host servers must have equal settings and OS type" %}</p>
 | 
			
		||||
            <form action="{% url 'instances:migrate' instance.id %}" class="ml-3 form" method="post" role="form" aria-label="Migrate instance form">
 | 
			
		||||
                {% csrf_token %}
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <label class="col-sm-3 col-form-label">{% trans "Original host" %}</label>
 | 
			
		||||
                    <div class="col-sm-6">
 | 
			
		||||
                        <label class="form-control" readonly="readonly">{{ compute.name }}</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <label class="col-sm-3 col-form-label">{% trans "Host migration" %}</label>
 | 
			
		||||
                    <div class="col-sm-6">
 | 
			
		||||
                        <select name="compute_id" class="custom-select">
 | 
			
		||||
                            {% if computes_count != 1 %}
 | 
			
		||||
                                {% for comp in computes %}
 | 
			
		||||
                                    {% if comp.id != compute.id %}
 | 
			
		||||
                                        <option value="{{ comp.id }}">{{ comp.name }}</option>
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <div class="col-sm-6 offset-3">
 | 
			
		||||
                        <div class="custom-control custom-switch">
 | 
			
		||||
                            <input class="custom-control-input" type="checkbox" name="live_migrate" value="true" id="vm_live_migrate" {% if instance.status != 5 %}checked{% else %}disabled{% endif %}>
 | 
			
		||||
                            <label class="custom-control-label" for="vm_live_migrate">{% trans "Live migration" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <div class="col-sm-6 offset-3">
 | 
			
		||||
                        <div class="custom-control custom-switch">
 | 
			
		||||
                            <input class="custom-control-input" type="checkbox" name="unsafe_migrate" value="true" id="vm_unsafe_migrate">
 | 
			
		||||
                            <label class="custom-control-label" for="vm_unsafe_migrate">{% trans "Unsafe migration" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <div class="col-sm-6 offset-3">
 | 
			
		||||
                        <div class="custom-control custom-switch">
 | 
			
		||||
                            <input class="custom-control-input" type="checkbox" name="xml_delete" value="true" id="xml_delete" checked>
 | 
			
		||||
                            <label class="custom-control-label" for="xml_delete">{% trans "Delete original" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <div class="col-sm-6 offset-3">
 | 
			
		||||
                        <div class="custom-control custom-switch">
 | 
			
		||||
                            <input class="custom-control-input" type="checkbox" name="offline_migrate" value="true" id="offline_migrate" {% if instance.status == 5 %}checked{% else %}disabled{% endif %}>
 | 
			
		||||
                            <label class="custom-control-label" for="offline_migrate">{% trans "Offline migration" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <div class="col-sm-6 offset-3">
 | 
			
		||||
                        <div class="custom-control custom-switch">
 | 
			
		||||
                            <input class="custom-control-input" type="checkbox" name="postcopy" value="true" id="postcopy" {% if instance.status != 1 %}disabled{% endif %}>
 | 
			
		||||
                            <label class="custom-control-label" for="postcopy">{% trans "Post copy" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <div class="col-sm-6 offset-3">
 | 
			
		||||
                        <div class="custom-control custom-switch">
 | 
			
		||||
                            <input class="custom-control-input" type="checkbox" name="autoconverge" value="true" id="autoconverge" {% if instance.status != 1 %}disabled{% endif %}>
 | 
			
		||||
                            <label class="custom-control-label" for="autoconverge" title="{% trans 'Forces CPU convergence during live migration' %}">{% trans "Auto converge" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="form-group row">
 | 
			
		||||
                    <div class="col-sm-6 offset-3">
 | 
			
		||||
                        <div class="custom-control custom-switch">
 | 
			
		||||
                            <input class="custom-control-input" type="checkbox" name="compress" value="true" id="compress" {% if instance.status != 1 %}disabled{% endif %}>
 | 
			
		||||
                            <label class="custom-control-label" for="compress" title="{% trans 'Compress instance memory for fast migration' %}">{% trans "Compressed" %}</label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                {% if computes_count != 1 %}
 | 
			
		||||
                    <button type="submit" class="btn btn-lg btn-success float-right" name="migrate" onclick="showPleaseWaitDialog();">{% trans "Migrate" %}</button>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <button class="btn btn-lg btn-success float-right disabled">{% trans "Migrate" %}</button>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </form>
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="xmledit">
 | 
			
		||||
            <p>{% trans "If you need to edit XML please Power Off the instance" %}</p>
 | 
			
		||||
            <form action="{% url 'instances:change_xml' instance.id %}" method="post" role="form" aria-label="Edit instance XML form">
 | 
			
		||||
                {% csrf_token %}
 | 
			
		||||
                <div class="col-sm-12" id="xmlheight">
 | 
			
		||||
                    <textarea id="editor">{{ instance.inst_xml }}</textarea>
 | 
			
		||||
                </div>
 | 
			
		||||
                {% if instance.status == 5 %}
 | 
			
		||||
                    <input type="hidden" name="inst_xml">
 | 
			
		||||
                    <button type="submit" class="btn btn-lg btn-success float-right" name="change_xml">
 | 
			
		||||
                        {% trans "Change" %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <button class="btn btn-lg btn-success float-right disabled">
 | 
			
		||||
                        {% trans "Change" %}
 | 
			
		||||
                    </button>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </form>
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="users">
 | 
			
		||||
            <div>
 | 
			
		||||
            <p class="font-weight-bold">
 | 
			
		||||
                {% trans "Instance owners" %}
 | 
			
		||||
                {% include 'add_instance_owner_block.html' %}
 | 
			
		||||
            </p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="table-responsive">
 | 
			
		||||
                <table class="table table-striped sortable-theme-bootstrap mt-3" data-sortable>
 | 
			
		||||
                    <tbody class="searchable">
 | 
			
		||||
                    {% for userinstance in userinstances %}
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <td><a href="{% url 'account' userinstance.user.id %}">{{ userinstance.user }}</a></td>
 | 
			
		||||
                            <td style="width:30px;">
 | 
			
		||||
                                <a href="{% url 'user_instance_delete' userinstance.id %}?next={% url 'instances:instance' instance.id %}#users">
 | 
			
		||||
                                    {% icon 'trash' %}
 | 
			
		||||
                                </a>
 | 
			
		||||
                            </td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                    </tbody>
 | 
			
		||||
                </table>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% if request.user.is_superuser or request.user.is_staff or userinstance.is_vnc %}
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="vncsettings">
 | 
			
		||||
            <form method="post" action="{% url 'instances:update_console' instance.id %}">
 | 
			
		||||
                {% csrf_token %}
 | 
			
		||||
                {% if instance.status != 5 %}
 | 
			
		||||
                    <div class="alert alert-warning">
 | 
			
		||||
                        {% trans "To change console settings, shutdown the instance." %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                {% bootstrap_form console_form layout='horizontal' %}
 | 
			
		||||
                <div class="float-right">
 | 
			
		||||
                    {% if instance.status != 5 %}
 | 
			
		||||
                        <button class="btn btn-success" name="set_console_type" disabled>{% trans "Update" %}</button>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <button type="submit" class="btn btn-success ">{% trans "Update" %}</button>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </div>
 | 
			
		||||
            </form>
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
        {% if perms.instances.clone_instances %}
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered" id="clone">
 | 
			
		||||
                <p class="font-weight-bold">{% trans "Create a clone" %}</p>
 | 
			
		||||
                <form class="form" action="{% url 'instances:clone' instance.id %}" method="post" role="form" aria-label="Create clone form">{% csrf_token %}
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label class="col-sm-3 col-form-label font-weight-normal">{% trans "Clone Name" %}</label>
 | 
			
		||||
                        {% if request.user.is_superuser %}
 | 
			
		||||
                            <div class="col-sm-6">
 | 
			
		||||
                            <div class="input-group">
 | 
			
		||||
                                    <input id="clone_name" type="text" class="form-control" name="name" value="{{ instance.name }}-clone"/>
 | 
			
		||||
                                    <div class="input-group-append">
 | 
			
		||||
                                        <button type="button" class="btn btn-success" name="guess-clone-name"
 | 
			
		||||
                                                onclick="guess_clone_name()">{% trans "Guess" %}</button>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        {% elif app_settings.CLONE_INSTANCE_AUTO_NAME == 'True'%}
 | 
			
		||||
                            <div class="col-sm-4">
 | 
			
		||||
                                <input id="clone_instance_auto_name" type="text" class="form-control" name="auto_name" value="Automatic" disabled/>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            <div class="col-sm-4">
 | 
			
		||||
                                <select id="select_clone_name" class="form-control" name="name" size="1">
 | 
			
		||||
                                {% for name in clone_free_names %}
 | 
			
		||||
                                    <option value="{{ name }}">{{ name }}</option>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% if request.user.is_superuser %}
 | 
			
		||||
                        <label>{% trans "Network devices" %}:</label>
 | 
			
		||||
                        {% for network in instance.networks %}
 | 
			
		||||
                        <p>
 | 
			
		||||
                            <div class="form-group row">
 | 
			
		||||
                                <label class="col-sm-2 col-form-label offset-1">eth{{ forloop.counter0 }} ({{ network.nic }})</label>
 | 
			
		||||
                                <div class="col-sm-6">
 | 
			
		||||
                                <div class="input-group">
 | 
			
		||||
                                        <input type="text" class="form-control" name="clone-net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
 | 
			
		||||
                                        <div class="input-group-append">
 | 
			
		||||
                                            <button type="button" class="btn btn-success" name="random-mac-{{ forloop.counter0 }}"
 | 
			
		||||
                                                    onclick="random_mac('clone-net-mac-{{ forloop.counter0 }}')">{% trans "Random" %}</button>
 | 
			
		||||
                                            <button type="button" class="btn btn-success" name="guess-mac-{{ forloop.counter0 }}"
 | 
			
		||||
                                                    onclick="guess_mac_address('#clone_name', {{ forloop.counter0 }})">{% trans "Guess" %}</button>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </p>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        {% for network in instance.networks %}
 | 
			
		||||
                            <input type="hidden" class="form-control" name="clone-net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    {% if request.user.is_superuser %}
 | 
			
		||||
                        <label>{% trans "Storage devices" %}:</label>
 | 
			
		||||
                        {% for disk in instance.disks %}
 | 
			
		||||
                            <div class="form-group row">
 | 
			
		||||
                                <label class="col-sm-2 col-form-label offset-1">{{ disk.dev }} ({{ disk.storage }})</label>
 | 
			
		||||
                                <div class="col-sm-6">
 | 
			
		||||
                                <div class="input-group">
 | 
			
		||||
                                        <input id="disk_name-{{ disk.dev }}" type="text" class="form-control" name="disk-{{ disk.dev }}" value="{{ disk.image }}"/>
 | 
			
		||||
                                        {% if disk.format == 'qcow2' %}
 | 
			
		||||
                                            <div class="input-group-append">
 | 
			
		||||
                                                <span class="input-group-text" >{% trans 'Metadata' %}</span>
 | 
			
		||||
                                                <div class="input-group-text">
 | 
			
		||||
                                                    <input type="checkbox" name="meta-{{ disk.dev }}" value="true">
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        {% for disk in instance.disks %}
 | 
			
		||||
                            <input id="disk_name-{{ disk.dev }}" type="hidden" class="form-control" name="disk-{{ disk.dev }}" value="{{ disk.image }}"/>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label class="col-sm-3 col-form-label">{% trans "Title" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <input type="text" name="clone-title" class="form-control">
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label class="col-sm-3 col-form-label">{% trans "Description" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <textarea name="clone-description" class="form-control"></textarea>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% if instance.status == 5 %}
 | 
			
		||||
                        <button type="submit" class="btn btn-lg btn-success float-right" name="clone" onclick="showPleaseWaitDialog();">{% trans "Clone" %}</button>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <button class="btn btn-lg btn-success float-right disabled" name="clone">{% trans "Clone" %}</button>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </form>
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered" id="options">
 | 
			
		||||
                <p>{% trans "To set instance template name description, shutdown the instance." %}</p>
 | 
			
		||||
                <form action="{% url 'instances:change_options' instance.id %}" method="post" role="form" aria-label="Set instance template name description form">{% csrf_token %}
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label class="col-sm-3 col-form-label">{% trans "Title" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <input type="text" name="title" class="form-control" value="{{ instance.title }}">
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label class="col-sm-3 col-form-label">{% trans "Description" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <textarea name="description" class="form-control">{{ instance.description }}</textarea>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label class="col-sm-3 col-form-label">{% trans "Is template" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <input type="checkbox"
 | 
			
		||||
                                name="is_template"
 | 
			
		||||
                                value="True"
 | 
			
		||||
                                id="is_template"
 | 
			
		||||
                                {% if instance.is_template %}checked{% endif %}
 | 
			
		||||
                                {% if not request.user.is_superuser and not request.user.is_staff %}disabled{% endif %}>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <div class="offset-3 col-sm-6">
 | 
			
		||||
                        {% if instance.status == 5 %}
 | 
			
		||||
                            <button type="submit" class="btn btn-block btn-success" name="change_options">{% trans "Change" %}</button>
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            <button class="btn btn-block btn-success disabled" name="change_options">{% trans "Change" %}</button>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </form>
 | 
			
		||||
            
 | 
			
		||||
                <p class="font-weight-bold">{% trans "To set instance video model, shutdown the instance." %}</p>
 | 
			
		||||
                <form action="{% url 'instances:set_video_model' instance.id %}" method="post" role="form" aria-label="Set instance video model form">{% csrf_token %}
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label for="video_model_select" class="col-sm-3 col-form-label">{% trans "Primary Video Model" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <div class="input-group">
 | 
			
		||||
                            <select id="video_model_select" class="custom-select" name="video_model">
 | 
			
		||||
                                <option class="font-weight-bold" value="">{% trans "please choose" %}</option>
 | 
			
		||||
                                {% for vmodel in instance.video_models %}
 | 
			
		||||
                                    <option value="{{ vmodel }}"{% if vmodel == instance.video_model %} selected{% endif %}>{{ vmodel }}</option>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            </select>
 | 
			
		||||
                            <span class="input-group-btn">
 | 
			
		||||
                                {% if instance.status == 5 %}
 | 
			
		||||
                                <button type="submit" class="btn btn-success" name="set_video_model">{% trans "Set" %}</button>
 | 
			
		||||
                                {% else %}
 | 
			
		||||
                                <button class="btn btn-success disabled" name="set_video_model">{% trans "Set" %}</button>
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            </span>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </form>
 | 
			
		||||
        
 | 
			
		||||
                <p class="font-weight-bold">{% trans "To set instance vCPUs hotpluggable" %}</p>
 | 
			
		||||
                <form  action="{% url 'instances:set_vcpu_hotplug' instance.id %}" method="post" role="form" aria-label="Set instance vCPUs hotpluggable form">{% csrf_token %}
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label for="vcpu_hotplug" class="col-sm-3 col-form-label">{% trans "vCPU Hot Plug" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <div class="input-group">
 | 
			
		||||
                            <select id="vcpu_hotplug" class="form-control" name="vcpu_hotplug">
 | 
			
		||||
                                <option value="True" {% if instance.vcpus %} selected {% endif %}>{% trans 'Enabled' %}</option>
 | 
			
		||||
                                <option value="False" {% if not instance.vcpus %} selected {% endif %}>{% trans 'Disabled' %}</option>
 | 
			
		||||
                            </select>
 | 
			
		||||
                            <span class="input-group-btn">
 | 
			
		||||
                            {% if instance.status == 5 %}
 | 
			
		||||
                                <button type="submit" class="btn btn-success" name="set_vcpu_hotplug">{% trans "Set" %}</button>
 | 
			
		||||
                            {% else %}
 | 
			
		||||
                                <button class="btn btn-success" name="set_vcpu_hotplug" disabled>{% trans "Set" %}</button>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                            </span>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </form>
 | 
			
		||||
            
 | 
			
		||||
                <p class="font-weight-bold">{% trans "To Enable/Disable Qemu Guest Agent. Status" %}:
 | 
			
		||||
                {% if instance.status == 1 %}
 | 
			
		||||
                    {% if instance.guest_agent_ready %}
 | 
			
		||||
                        <label class="badge badge-success">{% trans 'Connected' %}</label>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <label class="badge badge-danger">{% trans 'Disconnected' %}</label>
 | 
			
		||||
                    {% endif %}</p>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <label class="badge badge-default">{% trans 'Unknown' %}</label>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                <form action="{% url 'instances:set_guest_agent' instance.id %}" method="post" role="form" aria-label="Enable/Disable Qemu Guest Agent form">{% csrf_token %}
 | 
			
		||||
                    <div class="form-group row">
 | 
			
		||||
                        <label for="guest_agent" class="col-sm-3 col-form-label">{% trans "Qemu Guest Agent" %}</label>
 | 
			
		||||
                        <div class="col-sm-6">
 | 
			
		||||
                            <div class="input-group">
 | 
			
		||||
                            <select id="guest_agent" class="custom-select" name="guest_agent">
 | 
			
		||||
                                <option value="True" {% if instance.guest_agent %} selected {% endif %}>{% trans 'Enabled' %}</option>
 | 
			
		||||
                                <option value="False" {% if not instance.guest_agent %} selected {% endif %}>{% trans 'Disabled' %}</option>
 | 
			
		||||
                            </select>
 | 
			
		||||
                            <span class="input-group-btn">
 | 
			
		||||
                                <button type="submit" class="btn btn-success" name="set_guest_agent">{% trans "Set" %}</button>
 | 
			
		||||
                            </span>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </form>
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										91
									
								
								instances/templates/instances/snapshots_tab.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								instances/templates/instances/snapshots_tab.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load icons %}
 | 
			
		||||
<div role="tabpanel" class="tab-pane" id="snapshots">
 | 
			
		||||
    <div role="tabpanel">
 | 
			
		||||
        <!-- Nav tabs -->
 | 
			
		||||
        <ul class="nav nav-tabs" role="tablist" aria-label="Instance snapshot menu">
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary active" href="#takesnapshot" aria-controls="takesnapshot" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "Take Snapshot" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary" href="#managesnapshot" aria-controls="managesnapshot" role="tab" data-toggle="tab">
 | 
			
		||||
                    {% trans "Manage Snapshots" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- Tab panes -->
 | 
			
		||||
        <div class="tab-content">
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="takesnapshot">
 | 
			
		||||
                {% if instance.status == 5 %}
 | 
			
		||||
                    <p>{% trans "This may take more than an hour, depending on how much content is on your droplet and how large the disk is." %}</p>
 | 
			
		||||
                    <form action="{% url 'instances:snapshot' instance.id %}" class="form-inline" method="post" role="form" aria-label="Create snapshot form">
 | 
			
		||||
                        {% csrf_token %}
 | 
			
		||||
                        <div class="form-group row">
 | 
			
		||||
                            <div class="col-sm-12">
 | 
			
		||||
                                <input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Enter Snapshot Name" %}" maxlength="14">
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {% if instance.status == 5 %}
 | 
			
		||||
                            <input type="submit" class="btn btn-lg btn-success float-right" name="snapshot" value="{% trans "Take Snapshot" %}">
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            <button class="btn btn-lg btn-success float-right disabled">{% trans "Take Snapshot" %}</button>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </form>
 | 
			
		||||
                    <div class="clearfix"></div>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <p>{% trans "To take a snapshot please Power Off the instance." %}</p>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered" id="managesnapshot">
 | 
			
		||||
                {% if instance.snapshots %}
 | 
			
		||||
                    <p>{% trans "Choose a snapshot for restore/delete" %}</p>
 | 
			
		||||
                    <div class="table-responsive">
 | 
			
		||||
                        <table class="table">
 | 
			
		||||
                            <thead>
 | 
			
		||||
                                <th scope="col">{% trans "Name" %}</th>
 | 
			
		||||
                                <th scope="col">{% trans "Date" %}</th>
 | 
			
		||||
                                <th scope="colgroup" colspan="2">{% trans "Action" %}</th>
 | 
			
		||||
                            </thead>
 | 
			
		||||
                            <tbody>
 | 
			
		||||
                            {% for snap in instance.snapshots %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td><strong>{{ snap.name }}</strong></td>
 | 
			
		||||
                                    <td>{{ snap.date|date:"M d H:i:s" }}</td>
 | 
			
		||||
                                    <td style="width:30px;">
 | 
			
		||||
                                        <form action="{% url 'instances:revert_snapshot' instance.id %}" method="post" role="form" aria-label="Restore snapshot form">
 | 
			
		||||
                                            {% csrf_token %}
 | 
			
		||||
                                            <input type="hidden" name="name" value="{{ snap.name }}">
 | 
			
		||||
                                            {% if instance.status == 5 %}
 | 
			
		||||
                                                <button type="submit" class="btn btn-sm btn-secondary" name="revert_snapshot" title="{% trans 'Revert to this Snapshot' %}" onclick="return confirm('Are you sure?')">
 | 
			
		||||
                                                        <span class="fa fa-download"></span>
 | 
			
		||||
                                                </button>
 | 
			
		||||
                                            {% else %}
 | 
			
		||||
                                                <button type="button" class="btn btn-sm btn-secondary disabled"
 | 
			
		||||
                                                        title="{% trans "To restore snapshots you need Power Off the instance." %}">
 | 
			
		||||
                                                        <span class="fa fa-download"></span>
 | 
			
		||||
                                                </button>
 | 
			
		||||
                                            {% endif %}
 | 
			
		||||
                                        </form>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td style="width:30px;">
 | 
			
		||||
                                        <form action="{% url 'instances:delete_snapshot' instance.id %}" method="post" role="form" aria-label="Delete snapshot form">{% csrf_token %}
 | 
			
		||||
                                            <input type="hidden" name="name" value="{{ snap.name }}">
 | 
			
		||||
                                            <button type="submit" class="btn btn-sm btn-danger" title="{% trans 'Delete Snapshot' %}" onclick="return confirm('{% trans "Are you sure?" %}')">
 | 
			
		||||
                                                {% icon 'trash' %}
 | 
			
		||||
                                            </button>
 | 
			
		||||
                                        </form>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                            </tbody>
 | 
			
		||||
                        </table>
 | 
			
		||||
                    </div>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <p>{% trans "You do not have any snapshots" %}</p>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										116
									
								
								instances/templates/instances/stats_tab.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								instances/templates/instances/stats_tab.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,116 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
<div role="tabpanel" class="tab-pane" id="graphics">
 | 
			
		||||
    <div role="tabpanel">
 | 
			
		||||
        <!-- Nav tabs -->
 | 
			
		||||
        <ul class="nav nav-tabs" role="tablist" aria-label="Instance graphs and logs menu">
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary active" href="#graphs" id="graphtab" aria-controls="graphs" role="tab" data-toggle="tab" aria-controls="graphs" aria-selected="true">
 | 
			
		||||
                    {% trans "Real Time" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link text-secondary" href="#logs" id="logtab" aria-controls="logs" role="tab" data-toggle="tab" aria-controls="logs" onclick='update_logs_table("{{ instance.name }}");'>
 | 
			
		||||
                    {% trans "Logs" %}
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- Tab panes -->
 | 
			
		||||
        <div class="tab-content">
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="graphs">
 | 
			
		||||
                <div class="mb-1 card border-success">
 | 
			
		||||
                    <div class="card-header">
 | 
			
		||||
                        <h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
 | 
			
		||||
                            {% trans "CPU Usage" %}</h5>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="card-body">
 | 
			
		||||
                        <div class="flot-chart">
 | 
			
		||||
                            <div class="flot-chart-content" id="flot-moving-line-chart">
 | 
			
		||||
                                <canvas id="cpuChart" width="735" height="160"></canvas>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="mb-1 card border-danger">
 | 
			
		||||
                    <div class="card-header">
 | 
			
		||||
                        <h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
 | 
			
		||||
                            {% trans "Memory Usage" %}</h5>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="card-body">
 | 
			
		||||
                        <div class="flot-chart">
 | 
			
		||||
                            <div class="flot-chart-content" id="flot-moving-line-chart">
 | 
			
		||||
                                <canvas id="memChart" width="735" height="160"></canvas>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                {% for net in instance.networks %}
 | 
			
		||||
                <div class="mb-1 card border-info">
 | 
			
		||||
                    <div class="card-header">
 | 
			
		||||
                        <h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
 | 
			
		||||
                            {% trans "Bandwidth Device" %}: eth{{ forloop.counter0 }}</h5>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="card-body">
 | 
			
		||||
                        <div class="flot-chart">
 | 
			
		||||
                            <div class="flot-chart-content" id="flot-moving-line-chart">
 | 
			
		||||
                                <canvas id="netEth{{ forloop.counter0 }}Chart" width="735" height="160"></canvas>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
                {% for disk in instance.disks %}
 | 
			
		||||
                <div class="mb-1 card border-warning">
 | 
			
		||||
                    <div class="card-header">
 | 
			
		||||
                        <h5 class="card-title"><i class="fa fa-long-arrow-right"></i>
 | 
			
		||||
                            {% trans "Disk I/O device" %}: {{ disk.dev }}</h5>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="card-body">
 | 
			
		||||
                        <div class="flot-chart">
 | 
			
		||||
                            <div class="flot-chart-content" id="flot-moving-line-chart">
 | 
			
		||||
                                <canvas id="blk{{ disk.dev }}Chart" width="735" height="160"></canvas>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div role="tabpanel" class="tab-pane tab-pane-bordered" id="logs">
 | 
			
		||||
                <div class="table-responsive">
 | 
			
		||||
                    <table class="table table-striped sortable-theme-bootstrap" id="logs_table" data-sortable>
 | 
			
		||||
                        <thead>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <th scope="col">{% trans "Date" %}</th>
 | 
			
		||||
                                <th scope="col">{% trans "User" %}</th>
 | 
			
		||||
                                <th scope="col">{% trans "Message" %}</th>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        </thead>
 | 
			
		||||
                        <tbody class="searchable">
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td colspan="3"><i>{% trans 'None' %}...</i></td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        </tbody>
 | 
			
		||||
                    </table>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="clearfix"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<script>
 | 
			
		||||
    function update_logs_table(vname) {
 | 
			
		||||
        // TODO
 | 
			
		||||
        logurl = "{% url 'vm_logs' 1 %}".replace(1, vname);
 | 
			
		||||
        $.getJSON(logurl, function(data) {
 | 
			
		||||
            var logs = "";
 | 
			
		||||
            $.each(data, function(id) {
 | 
			
		||||
                row = data[id];
 | 
			
		||||
                // console.log(row);
 | 
			
		||||
                logs += '<tr><td style="width:150px">'+row['date']+'</td>';
 | 
			
		||||
                logs += '<td>'+row['user']+'</td>';
 | 
			
		||||
                logs += '<td>'+row['message']+'</td></tr>';
 | 
			
		||||
            });
 | 
			
		||||
            $("#logs_table > tbody").html(logs);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,478 @@
 | 
			
		|||
import tempfile
 | 
			
		||||
 | 
			
		||||
from django.shortcuts import reverse
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
from computes.models import Compute
 | 
			
		||||
 | 
			
		||||
from .models import Instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InstancesTestCase(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.client.login(username='admin', password='admin')
 | 
			
		||||
        Compute(
 | 
			
		||||
            name='local',
 | 
			
		||||
            hostname='localhost',
 | 
			
		||||
            login='',
 | 
			
		||||
            password='',
 | 
			
		||||
            details='local',
 | 
			
		||||
            type=4,
 | 
			
		||||
        ).save()
 | 
			
		||||
 | 
			
		||||
    def test_index(self):
 | 
			
		||||
        response = self.client.get(reverse('instances:index'))
 | 
			
		||||
        # with open('index.html', 'wb') as f:
 | 
			
		||||
        #     f.write(response.content)
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
    def test_create_select_type(self):
 | 
			
		||||
        response = self.client.get(reverse('instances:create_instance_select_type', args=[1]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
    def test_instance(self):
 | 
			
		||||
        compute = Compute.objects.get(pk=1)
 | 
			
		||||
 | 
			
		||||
        # create volume
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('create_volume', args=[compute.id, 'default']),
 | 
			
		||||
            {
 | 
			
		||||
                'name': 'test',
 | 
			
		||||
                'format': 'qcow2',
 | 
			
		||||
                'size': '1',
 | 
			
		||||
                'meta_prealloc': False,
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        self.assertRedirects(response, reverse('storage', args=[compute.id, 'default']))
 | 
			
		||||
 | 
			
		||||
        # create instance
 | 
			
		||||
        response = self.client.get(reverse('instances:create_instance', args=[compute.id, 'x86_64', 'q35']))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:create_instance', args=[compute.id, 'x86_64', 'q35']),
 | 
			
		||||
            {
 | 
			
		||||
                'name': 'test',
 | 
			
		||||
                'firmware': 'BIOS',
 | 
			
		||||
                'vcpu': 1,
 | 
			
		||||
                'vcpu_mode': 'host-model',
 | 
			
		||||
                'memory': 512,
 | 
			
		||||
                'device0': 'disk',
 | 
			
		||||
                'bus0': 'virtio',
 | 
			
		||||
                'images': 'test.qcow2',
 | 
			
		||||
                'storage-control': 'default',
 | 
			
		||||
                'image-control': 'test.qcow2',
 | 
			
		||||
                'networks': 'default',
 | 
			
		||||
                'network-control': 'default',
 | 
			
		||||
                'cache_mode': 'directsync',
 | 
			
		||||
                'nwfilter': '',
 | 
			
		||||
                'graphics': 'spice',
 | 
			
		||||
                'video': 'vga',
 | 
			
		||||
                'listener_addr': '0.0.0.0',
 | 
			
		||||
                'console_pass': '',
 | 
			
		||||
                'qemu_ga': False,
 | 
			
		||||
                'virtio': True,
 | 
			
		||||
                'create': True,
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        instance: Instance = Instance.objects.get(pk=1)
 | 
			
		||||
        self.assertEqual(instance.name, 'test')
 | 
			
		||||
 | 
			
		||||
        # get instance page
 | 
			
		||||
        response = self.client.get(reverse('instances:instance', args=[instance.id]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        # resize cpu
 | 
			
		||||
        self.assertEqual(instance.vcpu, 1)
 | 
			
		||||
        self.assertEqual(instance.cur_vcpu, 1)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(reverse('instances:resizevm_cpu', args=[instance.id]), {'vcpu': 4, 'cur_vcpu': 2})
 | 
			
		||||
        self.assertRedirects(response, reverse('instances:instance', args=[instance.id]) + '#resize')
 | 
			
		||||
 | 
			
		||||
        # reset cached properties
 | 
			
		||||
        del instance.vcpu
 | 
			
		||||
        del instance.cur_vcpu
 | 
			
		||||
        self.assertEqual(instance.vcpu, 4)
 | 
			
		||||
        self.assertEqual(instance.cur_vcpu, 2)
 | 
			
		||||
 | 
			
		||||
        # resize memory
 | 
			
		||||
        self.assertEqual(instance.memory, 512)
 | 
			
		||||
        self.assertEqual(instance.cur_memory, 512)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(reverse('instances:resize_memory', args=[instance.id]), {
 | 
			
		||||
            'memory': 2048,
 | 
			
		||||
            'cur_memory': 1024
 | 
			
		||||
        })
 | 
			
		||||
        self.assertRedirects(response, reverse('instances:instance', args=[instance.id]) + '#resize')
 | 
			
		||||
 | 
			
		||||
        del instance.memory
 | 
			
		||||
        del instance.cur_memory
 | 
			
		||||
        self.assertEqual(instance.memory, 2048)
 | 
			
		||||
        self.assertEqual(instance.cur_memory, 1024)
 | 
			
		||||
 | 
			
		||||
        # resize disk
 | 
			
		||||
        self.assertEqual(instance.disks[0]['size'], 1024**3)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(reverse('instances:resize_disk', args=[instance.id]), {
 | 
			
		||||
            'disk_size_vda': '2.0 GB',
 | 
			
		||||
        })
 | 
			
		||||
        self.assertRedirects(response, reverse('instances:instance', args=[instance.id]) + '#resize')
 | 
			
		||||
 | 
			
		||||
        del instance.disks
 | 
			
		||||
        self.assertEqual(instance.disks[0]['size'], 2 * 1024**3)
 | 
			
		||||
 | 
			
		||||
        # add new volume
 | 
			
		||||
        self.assertEqual(len(instance.disks), 1)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:add_new_vol', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'storage': 'default',
 | 
			
		||||
                'name': 'test2',
 | 
			
		||||
                'size': 1,
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.disks
 | 
			
		||||
        self.assertEqual(len(instance.disks), 2)
 | 
			
		||||
 | 
			
		||||
        # delete volume from instance
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:delete_vol', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'storage': 'default',
 | 
			
		||||
                'dev': 'vdb',
 | 
			
		||||
                'name': 'test2.qcow2',
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.disks
 | 
			
		||||
        self.assertEqual(len(instance.disks), 1)
 | 
			
		||||
 | 
			
		||||
        # detach volume
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:detach_vol', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'dev': 'vda',
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.disks
 | 
			
		||||
        self.assertEqual(len(instance.disks), 0)
 | 
			
		||||
 | 
			
		||||
        # add existing volume
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:add_existing_vol', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'selected_storage': 'default',
 | 
			
		||||
                'vols': 'test.qcow2',
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.disks
 | 
			
		||||
        self.assertEqual(len(instance.disks), 1)
 | 
			
		||||
 | 
			
		||||
        # edit volume
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:edit_volume', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'vol_path': '/var/lib/libvirt/images/test.qcow2',
 | 
			
		||||
                # 'vol_shareable': False,
 | 
			
		||||
                # 'vol_readonly': False,
 | 
			
		||||
                'vol_bus': 'virtio',
 | 
			
		||||
                'vol_bus_old': 'virtio',
 | 
			
		||||
                'vol_format': 'qcow2',
 | 
			
		||||
                'dev': 'vda',
 | 
			
		||||
                'edit_volume': True
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        # add media device
 | 
			
		||||
        self.assertEqual(len(instance.media), 1)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:add_cdrom', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'bus': 'sata',
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.media
 | 
			
		||||
        self.assertEqual(len(instance.media), 2)
 | 
			
		||||
 | 
			
		||||
        # create dummy iso
 | 
			
		||||
        # with tempfile.NamedTemporaryFile() as f:
 | 
			
		||||
        #     f.write(b'\x00' * 1024**2)
 | 
			
		||||
 | 
			
		||||
        #     response = self.client.post(
 | 
			
		||||
        #         reverse('storage', args=[instance.compute.id, 'default']),
 | 
			
		||||
        #         {
 | 
			
		||||
        #             'iso_upload': True,
 | 
			
		||||
        #             'file': f
 | 
			
		||||
        #         },
 | 
			
		||||
        #     )
 | 
			
		||||
 | 
			
		||||
        # remove media device
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:detach_cdrom', args=[instance.id, 'sda']),
 | 
			
		||||
            {},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.media
 | 
			
		||||
        self.assertEqual(len(instance.media), 1)
 | 
			
		||||
 | 
			
		||||
        # snapshots
 | 
			
		||||
        self.assertEqual(len(instance.snapshots), 0)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:snapshot', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'name': 'test',
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.snapshots
 | 
			
		||||
        self.assertEqual(len(instance.snapshots), 1)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:revert_snapshot', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'name': 'test',
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:delete_snapshot', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'name': 'test',
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.snapshots
 | 
			
		||||
        self.assertEqual(len(instance.snapshots), 0)
 | 
			
		||||
 | 
			
		||||
        # autostart
 | 
			
		||||
        self.assertEqual(instance.autostart, 0)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:set_autostart', args=[instance.id]),
 | 
			
		||||
            {},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.autostart
 | 
			
		||||
        self.assertEqual(instance.autostart, 1)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:unset_autostart', args=[instance.id]),
 | 
			
		||||
            {},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.autostart
 | 
			
		||||
        self.assertEqual(instance.autostart, 0)
 | 
			
		||||
 | 
			
		||||
        # bootmenu
 | 
			
		||||
        self.assertEqual(instance.bootmenu, True)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:unset_bootmenu', args=[instance.id]),
 | 
			
		||||
            {},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.bootmenu
 | 
			
		||||
        self.assertEqual(instance.bootmenu, False)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:set_bootmenu', args=[instance.id]),
 | 
			
		||||
            {},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.bootmenu
 | 
			
		||||
        self.assertEqual(instance.bootmenu, True)
 | 
			
		||||
 | 
			
		||||
        # guest agent
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(instance.guest_agent, False)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:set_guest_agent', args=[instance.id]),
 | 
			
		||||
            {'guest_agent': True},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.guest_agent
 | 
			
		||||
        self.assertEqual(instance.guest_agent, True)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:set_guest_agent', args=[instance.id]),
 | 
			
		||||
            {'guest_agent': False},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.guest_agent
 | 
			
		||||
        self.assertEqual(instance.guest_agent, False)
 | 
			
		||||
 | 
			
		||||
        # video model
 | 
			
		||||
        self.assertEqual(instance.video_model, 'vga')
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:set_video_model', args=[instance.id]),
 | 
			
		||||
            {'video_model': 'virtio'},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.video_model
 | 
			
		||||
        self.assertEqual(instance.video_model, 'virtio')
 | 
			
		||||
 | 
			
		||||
        # console
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:update_console', args=[instance.id]),
 | 
			
		||||
            {
 | 
			
		||||
                'type': 'spice',
 | 
			
		||||
                'listen_on': '0.0.0.0',
 | 
			
		||||
                'password': '',
 | 
			
		||||
                'keymap': 'auto'
 | 
			
		||||
            },
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        # poweron
 | 
			
		||||
        self.assertEqual(instance.status, 5)
 | 
			
		||||
 | 
			
		||||
        response = self.client.get(reverse('instances:poweron', args=[instance.id]), HTTP_REFERER=reverse('index'))
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.status
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(instance.status, 1)
 | 
			
		||||
 | 
			
		||||
        # status
 | 
			
		||||
        response = self.client.get(reverse('instances:status', args=[instance.id]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        # stats
 | 
			
		||||
        response = self.client.get(reverse('instances:stats', args=[instance.id]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        # guess_mac_address
 | 
			
		||||
        response = self.client.get(reverse('instances:guess_mac_address', args=[instance.name]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        # random_mac_address
 | 
			
		||||
        response = self.client.get(reverse('instances:random_mac_address'))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        # random_mac_address
 | 
			
		||||
        response = self.client.get(reverse('instances:guess_clone_name'))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        # guess_mac_address
 | 
			
		||||
        response = self.client.get(reverse('instances:check_instance', args=[instance.name]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        # sshkeys
 | 
			
		||||
        response = self.client.get(reverse('instances:sshkeys', args=[instance.name]))
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
 | 
			
		||||
        # suspend
 | 
			
		||||
        response = self.client.get(reverse('instances:suspend', args=[instance.id]), HTTP_REFERER=reverse('index'))
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.status
 | 
			
		||||
        self.assertEqual(instance.status, 3)
 | 
			
		||||
 | 
			
		||||
        # resume
 | 
			
		||||
        response = self.client.get(reverse('instances:resume', args=[instance.id]), HTTP_REFERER=reverse('index'))
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.status
 | 
			
		||||
        self.assertEqual(instance.status, 1)
 | 
			
		||||
 | 
			
		||||
        # poweroff
 | 
			
		||||
        response = self.client.get(reverse('instances:poweroff', args=[instance.id]), HTTP_REFERER=reverse('index'))
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        # as no OS is installed ACPI won't work
 | 
			
		||||
        del instance.status
 | 
			
		||||
        self.assertEqual(instance.status, 1)
 | 
			
		||||
 | 
			
		||||
        # powercycle
 | 
			
		||||
        response = self.client.get(reverse('instances:powercycle', args=[instance.id]), HTTP_REFERER=reverse('index'))
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
        self.assertEqual(instance.status, 1)
 | 
			
		||||
 | 
			
		||||
        # force_off
 | 
			
		||||
        response = self.client.get(reverse('instances:force_off', args=[instance.id]), HTTP_REFERER=reverse('index'))
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
 | 
			
		||||
        del instance.status
 | 
			
		||||
        self.assertEqual(instance.status, 5)
 | 
			
		||||
 | 
			
		||||
        # delete started instance with disks
 | 
			
		||||
        instance.proxy.start()
 | 
			
		||||
        del instance.status
 | 
			
		||||
        self.assertEqual(instance.status, 1)
 | 
			
		||||
 | 
			
		||||
        response = self.client.post(
 | 
			
		||||
            reverse('instances:destroy', args=[instance.id]),
 | 
			
		||||
            {'delete_disk': True},
 | 
			
		||||
            HTTP_REFERER=reverse('index'),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertRedirects(response, reverse('instances', args=[compute.id]))
 | 
			
		||||
 | 
			
		||||
        # # create volume
 | 
			
		||||
        # response = self.client.post(
 | 
			
		||||
        #     reverse('create_volume', args=[compute.id, 'default']),
 | 
			
		||||
        #     {
 | 
			
		||||
        #         'name': 'test3',
 | 
			
		||||
        #         'format': 'qcow2',
 | 
			
		||||
        #         'size': '1',
 | 
			
		||||
        #         'meta_prealloc': False,
 | 
			
		||||
        #     },
 | 
			
		||||
        # )
 | 
			
		||||
        # self.assertRedirects(response, reverse('storage', args=[compute.id, 'default']))
 | 
			
		||||
 | 
			
		||||
        # # delete volume
 | 
			
		||||
        # response = self.client.post(
 | 
			
		||||
        #     reverse('instances:delete_vol', args=[instance.id]),
 | 
			
		||||
        #     {
 | 
			
		||||
        #         'storage': 'default',
 | 
			
		||||
        #         'dev': 'vdb',
 | 
			
		||||
        #         'name': 'test3.qcow2',
 | 
			
		||||
        #     },
 | 
			
		||||
        #     HTTP_REFERER=reverse('index'),
 | 
			
		||||
        # )
 | 
			
		||||
        # self.assertEqual(response.status_code, 302)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,10 +5,62 @@ from . import views
 | 
			
		|||
app_name = 'instances'
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('', views.allinstances, name='index'),
 | 
			
		||||
    path('<int:compute_id>/<vname>/', views.instance, name='instance'),
 | 
			
		||||
    path('statistics/<int:compute_id>/<vname>/', views.inst_graph, name='inst_graph'),
 | 
			
		||||
    path('status/<int:compute_id>/<vname>/', views.inst_status, name='inst_status'),
 | 
			
		||||
    path('', views.index, name='index'),
 | 
			
		||||
    path('flavor/create/', views.flavor_create, name='flavor_create'),
 | 
			
		||||
    path('flavor/<int:pk>/update/', views.flavor_update, name='flavor_update'),
 | 
			
		||||
    path('flavor/<int:pk>/delete/', views.flavor_delete, name='flavor_delete'),
 | 
			
		||||
    path('<int:pk>/', views.instance, name='instance'),
 | 
			
		||||
    path('<int:pk>/poweron/', views.poweron, name='poweron'),
 | 
			
		||||
    path('<int:pk>/powercycle/', views.powercycle, name='powercycle'),
 | 
			
		||||
    path('<int:pk>/poweroff/', views.poweroff, name='poweroff'),
 | 
			
		||||
    path('<int:pk>/suspend/', views.suspend, name='suspend'),
 | 
			
		||||
    path('<int:pk>/resume/', views.resume, name='resume'),
 | 
			
		||||
    path('<int:pk>/force_off/', views.force_off, name='force_off'),
 | 
			
		||||
    path('<int:pk>/destroy/', views.destroy, name='destroy'),
 | 
			
		||||
    path('<int:pk>/migrate/', views.migrate, name='migrate'),
 | 
			
		||||
    path('<int:pk>/status/', views.status, name='status'),
 | 
			
		||||
    path('<int:pk>/stats/', views.stats, name='stats'),
 | 
			
		||||
    path('<int:pk>/rootpasswd/', views.set_root_pass, name='rootpasswd'),
 | 
			
		||||
    path('<int:pk>/add_public_key/', views.add_public_key, name='add_public_key'),
 | 
			
		||||
    path('<int:pk>/resizevm_cpu/', views.resizevm_cpu, name='resizevm_cpu'),
 | 
			
		||||
    path('<int:pk>/resize_memory/', views.resize_memory, name='resize_memory'),
 | 
			
		||||
    path('<int:pk>/resize_disk/', views.resize_disk, name='resize_disk'),
 | 
			
		||||
    path('<int:pk>/add_new_vol/', views.add_new_vol, name='add_new_vol'),
 | 
			
		||||
    path('<int:pk>/delete_vol/', views.delete_vol, name='delete_vol'),
 | 
			
		||||
    path('<int:pk>/add_owner/', views.add_owner, name='add_owner'),
 | 
			
		||||
    path('<int:pk>/add_existing_vol/', views.add_existing_vol, name='add_existing_vol'),
 | 
			
		||||
    path('<int:pk>/edit_volume/', views.edit_volume, name='edit_volume'),
 | 
			
		||||
    path('<int:pk>/detach_vol/', views.detach_vol, name='detach_vol'),
 | 
			
		||||
    path('<int:pk>/add_cdrom/', views.add_cdrom, name='add_cdrom'),
 | 
			
		||||
    path('<int:pk>/detach_cdrom/<str:dev>/', views.detach_cdrom, name='detach_cdrom'),
 | 
			
		||||
    path('<int:pk>/unmount_iso/', views.unmount_iso, name='unmount_iso'),
 | 
			
		||||
    path('<int:pk>/mount_iso/', views.mount_iso, name='mount_iso'),
 | 
			
		||||
    path('<int:pk>/snapshot/', views.snapshot, name='snapshot'),
 | 
			
		||||
    path('<int:pk>/delete_snapshot/', views.delete_snapshot, name='delete_snapshot'),
 | 
			
		||||
    path('<int:pk>/revert_snapshot/', views.revert_snapshot, name='revert_snapshot'),
 | 
			
		||||
    path('<int:pk>/set_vcpu/', views.set_vcpu, name='set_vcpu'),
 | 
			
		||||
    path('<int:pk>/set_vcpu_hotplug/', views.set_vcpu_hotplug, name='set_vcpu_hotplug'),
 | 
			
		||||
    path('<int:pk>/set_autostart/', views.set_autostart, name='set_autostart'),
 | 
			
		||||
    path('<int:pk>/unset_autostart/', views.unset_autostart, name='unset_autostart'),
 | 
			
		||||
    path('<int:pk>/set_bootmenu/', views.set_bootmenu, name='set_bootmenu'),
 | 
			
		||||
    path('<int:pk>/unset_bootmenu/', views.unset_bootmenu, name='unset_bootmenu'),
 | 
			
		||||
    path('<int:pk>/set_bootorder/', views.set_bootorder, name='set_bootorder'),
 | 
			
		||||
    path('<int:pk>/change_xml/', views.change_xml, name='change_xml'),
 | 
			
		||||
    path('<int:pk>/set_guest_agent/', views.set_guest_agent, name='set_guest_agent'),
 | 
			
		||||
    path('<int:pk>/set_video_model/', views.set_video_model, name='set_video_model'),
 | 
			
		||||
    path('<int:pk>/change_network/', views.change_network, name='change_network'),
 | 
			
		||||
    path('<int:pk>/add_network/', views.add_network, name='add_network'),
 | 
			
		||||
    path('<int:pk>/delete_network/', views.delete_network, name='delete_network'),
 | 
			
		||||
    path('<int:pk>/set_link_state/', views.set_link_state, name='set_link_state'),
 | 
			
		||||
    path('<int:pk>/set_qos/', views.set_qos, name='set_qos'),
 | 
			
		||||
    path('<int:pk>/unset_qos/', views.unset_qos, name='unset_qos'),
 | 
			
		||||
    path('<int:pk>/del_owner/', views.del_owner, name='del_owner'),  # no links to this one???
 | 
			
		||||
    path('<int:pk>/clone/', views.clone, name='clone'),
 | 
			
		||||
    path('<int:pk>/update_console/', views.update_console, name='update_console'),
 | 
			
		||||
    path('<int:pk>/change_options/', views.change_options, name='change_options'),
 | 
			
		||||
    path('<int:pk>/getvvfile/', views.getvvfile, name='getvvfile'),  # no links to this one???
 | 
			
		||||
    path('create/<int:compute_id>/', views.create_instance_select_type, name='create_instance_select_type'),
 | 
			
		||||
    path('create/<int:compute_id>/<str:arch>/<str:machine>/', views.create_instance, name='create_instance'),
 | 
			
		||||
    path('guess_mac_address/<vname>/', views.guess_mac_address, name='guess_mac_address'),
 | 
			
		||||
    path('guess_clone_name/', views.guess_clone_name, name='guess_clone_name'),
 | 
			
		||||
    path('random_mac_address/', views.random_mac_address, name='random_mac_address'),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										361
									
								
								instances/utils.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								instances/utils.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,361 @@
 | 
			
		|||
import os
 | 
			
		||||
import random
 | 
			
		||||
import string
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from accounts.models import UserInstance
 | 
			
		||||
from appsettings.settings import app_settings
 | 
			
		||||
from logs.views import addlogmsg
 | 
			
		||||
from vrtManager.connection import connection_manager
 | 
			
		||||
from vrtManager.hostdetails import wvmHostDetails
 | 
			
		||||
from vrtManager.instance import wvmInstance, wvmInstances
 | 
			
		||||
 | 
			
		||||
from .models import Instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def filesizefstr(size_str):
 | 
			
		||||
    if size_str == '':
 | 
			
		||||
        return 0
 | 
			
		||||
    size_str = size_str.upper().replace("B", "")
 | 
			
		||||
    if size_str[-1] == 'K':
 | 
			
		||||
        return int(float(size_str[:-1])) << 10
 | 
			
		||||
    elif size_str[-1] == 'M':
 | 
			
		||||
        return int(float(size_str[:-1])) << 20
 | 
			
		||||
    elif size_str[-1] == 'G':
 | 
			
		||||
        return int(float(size_str[:-1])) << 30
 | 
			
		||||
    elif size_str[-1] == 'T':
 | 
			
		||||
        return int(float(size_str[:-1])) << 40
 | 
			
		||||
    elif size_str[-1] == 'P':
 | 
			
		||||
        return int(float(size_str[:-1])) << 50
 | 
			
		||||
    else:
 | 
			
		||||
        return int(float(size_str))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_clone_free_names(size=10):
 | 
			
		||||
    prefix = app_settings.CLONE_INSTANCE_DEFAULT_PREFIX
 | 
			
		||||
    free_names = []
 | 
			
		||||
    existing_names = [i.name for i in Instance.objects.filter(name__startswith=prefix)]
 | 
			
		||||
    index = 1
 | 
			
		||||
    while len(free_names) < size:
 | 
			
		||||
        new_name = prefix + str(index)
 | 
			
		||||
        if new_name not in existing_names:
 | 
			
		||||
            free_names.append(new_name)
 | 
			
		||||
        index += 1
 | 
			
		||||
    return free_names
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_user_quota(user, instance, cpu, memory, disk_size):
 | 
			
		||||
    ua = user.userattributes
 | 
			
		||||
    msg = ""
 | 
			
		||||
 | 
			
		||||
    if user.is_superuser:
 | 
			
		||||
        return msg
 | 
			
		||||
 | 
			
		||||
    quota_debug = app_settings.QUOTA_DEBUG
 | 
			
		||||
 | 
			
		||||
    user_instances = UserInstance.objects.filter(user=user, instance__is_template=False)
 | 
			
		||||
    instance += user_instances.count()
 | 
			
		||||
    for usr_inst in user_instances:
 | 
			
		||||
        if connection_manager.host_is_up(
 | 
			
		||||
                usr_inst.instance.compute.type,
 | 
			
		||||
                usr_inst.instance.compute.hostname,
 | 
			
		||||
        ):
 | 
			
		||||
            conn = wvmInstance(
 | 
			
		||||
                usr_inst.instance.compute.hostname,
 | 
			
		||||
                usr_inst.instance.compute.login,
 | 
			
		||||
                usr_inst.instance.compute.password,
 | 
			
		||||
                usr_inst.instance.compute.type,
 | 
			
		||||
                usr_inst.instance.name,
 | 
			
		||||
            )
 | 
			
		||||
            cpu += int(conn.get_vcpu())
 | 
			
		||||
            memory += int(conn.get_memory())
 | 
			
		||||
            for disk in conn.get_disk_devices():
 | 
			
		||||
                if disk['size']:
 | 
			
		||||
                    disk_size += int(disk['size']) >> 30
 | 
			
		||||
 | 
			
		||||
    if ua.max_instances > 0 and instance > ua.max_instances:
 | 
			
		||||
        msg = "instance"
 | 
			
		||||
        if quota_debug:
 | 
			
		||||
            msg += f" ({instance} > {ua.max_instances})"
 | 
			
		||||
    if ua.max_cpus > 0 and cpu > ua.max_cpus:
 | 
			
		||||
        msg = "cpu"
 | 
			
		||||
        if quota_debug:
 | 
			
		||||
            msg += f" ({cpu} > {ua.max_cpus})"
 | 
			
		||||
    if ua.max_memory > 0 and memory > ua.max_memory:
 | 
			
		||||
        msg = "memory"
 | 
			
		||||
        if quota_debug:
 | 
			
		||||
            msg += f" ({memory} > {ua.max_memory})"
 | 
			
		||||
    if ua.max_disk_size > 0 and disk_size > ua.max_disk_size:
 | 
			
		||||
        msg = "disk"
 | 
			
		||||
        if quota_debug:
 | 
			
		||||
            msg += f" ({disk_size} > {ua.max_disk_size})"
 | 
			
		||||
    return msg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_new_disk_dev(media, disks, bus):
 | 
			
		||||
    existing_disk_devs = []
 | 
			
		||||
    existing_media_devs = []
 | 
			
		||||
    if bus == "virtio":
 | 
			
		||||
        dev_base = "vd"
 | 
			
		||||
    elif bus == "ide":
 | 
			
		||||
        dev_base = "hd"
 | 
			
		||||
    elif bus == "fdc":
 | 
			
		||||
        dev_base = "fd"
 | 
			
		||||
    else:
 | 
			
		||||
        dev_base = "sd"
 | 
			
		||||
 | 
			
		||||
    if disks:
 | 
			
		||||
        existing_disk_devs = [disk['dev'] for disk in disks]
 | 
			
		||||
 | 
			
		||||
    # cd-rom bus could be virtio/sata, because of that we should check it also
 | 
			
		||||
    if media:
 | 
			
		||||
        existing_media_devs = [m['dev'] for m in media]
 | 
			
		||||
 | 
			
		||||
    for al in string.ascii_lowercase:
 | 
			
		||||
        dev = dev_base + al
 | 
			
		||||
        if dev not in existing_disk_devs and dev not in existing_media_devs:
 | 
			
		||||
            return dev
 | 
			
		||||
    raise Exception(_('None available device name'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_network_tuple(network_source_str):
 | 
			
		||||
    network_source_pack = network_source_str.split(":", 1)
 | 
			
		||||
    if len(network_source_pack) > 1:
 | 
			
		||||
        return network_source_pack[1], network_source_pack[0]
 | 
			
		||||
    else:
 | 
			
		||||
        return network_source_pack[0], 'net'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def migrate_instance(
 | 
			
		||||
    new_compute,
 | 
			
		||||
    instance,
 | 
			
		||||
    user,
 | 
			
		||||
    live=False,
 | 
			
		||||
    unsafe=False,
 | 
			
		||||
    xml_del=False,
 | 
			
		||||
    offline=False,
 | 
			
		||||
    autoconverge=False,
 | 
			
		||||
    compress=False,
 | 
			
		||||
    postcopy=False,
 | 
			
		||||
):
 | 
			
		||||
    status = connection_manager.host_is_up(new_compute.type, new_compute.hostname)
 | 
			
		||||
    if not status:
 | 
			
		||||
        return
 | 
			
		||||
    if new_compute == instance.compute:
 | 
			
		||||
        return
 | 
			
		||||
    try:
 | 
			
		||||
        conn_migrate = wvmInstances(
 | 
			
		||||
            new_compute.hostname,
 | 
			
		||||
            new_compute.login,
 | 
			
		||||
            new_compute.password,
 | 
			
		||||
            new_compute.type,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        autostart = instance.autostart
 | 
			
		||||
        conn_migrate.moveto(
 | 
			
		||||
            instance.proxy,
 | 
			
		||||
            instance.name,
 | 
			
		||||
            live,
 | 
			
		||||
            unsafe,
 | 
			
		||||
            xml_del,
 | 
			
		||||
            offline,
 | 
			
		||||
            autoconverge,
 | 
			
		||||
            compress,
 | 
			
		||||
            postcopy,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        conn_new = wvmInstance(
 | 
			
		||||
            new_compute.hostname,
 | 
			
		||||
            new_compute.login,
 | 
			
		||||
            new_compute.password,
 | 
			
		||||
            new_compute.type,
 | 
			
		||||
            instance.name,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if autostart:
 | 
			
		||||
            conn_new.set_autostart(1)
 | 
			
		||||
    finally:
 | 
			
		||||
        conn_migrate.close()
 | 
			
		||||
        conn_new.close()
 | 
			
		||||
 | 
			
		||||
    instance.compute = new_compute
 | 
			
		||||
    instance.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_hosts_status(computes):
 | 
			
		||||
    """
 | 
			
		||||
        Function return all hosts all vds on host
 | 
			
		||||
        """
 | 
			
		||||
    compute_data = []
 | 
			
		||||
    for compute in computes:
 | 
			
		||||
        compute_data.append({
 | 
			
		||||
            'id': compute.id,
 | 
			
		||||
            'name': compute.name,
 | 
			
		||||
            'hostname': compute.hostname,
 | 
			
		||||
            'status': connection_manager.host_is_up(compute.type, compute.hostname),
 | 
			
		||||
            'type': compute.type,
 | 
			
		||||
            'login': compute.login,
 | 
			
		||||
            'password': compute.password,
 | 
			
		||||
            'details': compute.details,
 | 
			
		||||
        })
 | 
			
		||||
    return compute_data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_userinstances_info(instance):
 | 
			
		||||
    info = {}
 | 
			
		||||
    uis = UserInstance.objects.filter(instance=instance)
 | 
			
		||||
    info['count'] = uis.count()
 | 
			
		||||
    if info['count'] > 0:
 | 
			
		||||
        info['first_user'] = uis[0]
 | 
			
		||||
    else:
 | 
			
		||||
        info['first_user'] = None
 | 
			
		||||
    return info
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def refr(compute):
 | 
			
		||||
    if compute.status is True:
 | 
			
		||||
        domains = compute.proxy.wvm.listAllDomains()
 | 
			
		||||
        domain_names = [d.name() for d in domains]
 | 
			
		||||
        # Delete instances that're not on host
 | 
			
		||||
        Instance.objects.filter(compute=compute).exclude(name__in=domain_names).delete()
 | 
			
		||||
        # Create instances that're not in DB
 | 
			
		||||
        names = Instance.objects.filter(compute=compute).values_list('name', flat=True)
 | 
			
		||||
        for domain in domains:
 | 
			
		||||
            if domain.name() not in names:
 | 
			
		||||
                Instance(compute=compute, name=domain.name(), uuid=domain.UUIDString()).save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def refresh_instance_database(comp, inst_name, info, all_host_vms, user):
 | 
			
		||||
    # Multiple Instance Name Check
 | 
			
		||||
    instances = Instance.objects.filter(name=inst_name)
 | 
			
		||||
    if instances.count() > 1:
 | 
			
		||||
        for i in instances:
 | 
			
		||||
            user_instances_count = UserInstance.objects.filter(instance=i).count()
 | 
			
		||||
            if user_instances_count == 0:
 | 
			
		||||
                addlogmsg(user.username, i.name, _("Deleting due to multiple(Instance Name) records."))
 | 
			
		||||
                i.delete()
 | 
			
		||||
 | 
			
		||||
    # Multiple UUID Check
 | 
			
		||||
    instances = Instance.objects.filter(uuid=info['uuid'])
 | 
			
		||||
    if instances.count() > 1:
 | 
			
		||||
        for i in instances:
 | 
			
		||||
            if i.name != inst_name:
 | 
			
		||||
                addlogmsg(user.username, i.name, _("Deleting due to multiple(UUID) records."))
 | 
			
		||||
                i.delete()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        inst_on_db = Instance.objects.get(compute_id=comp["id"], name=inst_name)
 | 
			
		||||
        if inst_on_db.uuid != info['uuid']:
 | 
			
		||||
            inst_on_db.save()
 | 
			
		||||
 | 
			
		||||
        all_host_vms[comp["id"], comp["name"], comp["status"], comp["cpu"], comp["mem_size"],
 | 
			
		||||
                     comp["mem_perc"]][inst_name]['is_template'] = inst_on_db.is_template
 | 
			
		||||
        all_host_vms[comp["id"], comp["name"], comp["status"], comp["cpu"], comp["mem_size"],
 | 
			
		||||
                     comp["mem_perc"]][inst_name]['userinstances'] = get_userinstances_info(inst_on_db)
 | 
			
		||||
    except Instance.DoesNotExist:
 | 
			
		||||
        inst_on_db = Instance(compute_id=comp["id"], name=inst_name, uuid=info['uuid'])
 | 
			
		||||
        inst_on_db.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_user_instances(user):
 | 
			
		||||
    all_user_vms = {}
 | 
			
		||||
    user_instances = UserInstance.objects.filter(user=user)
 | 
			
		||||
    for usr_inst in user_instances:
 | 
			
		||||
        if connection_manager.host_is_up(usr_inst.instance.compute.type, usr_inst.instance.compute.hostname):
 | 
			
		||||
            conn = wvmHostDetails(
 | 
			
		||||
                usr_inst.instance.compute.hostname,
 | 
			
		||||
                usr_inst.instance.compute.login,
 | 
			
		||||
                usr_inst.instance.compute.password,
 | 
			
		||||
                usr_inst.instance.compute.type,
 | 
			
		||||
            )
 | 
			
		||||
            all_user_vms[usr_inst] = conn.get_user_instances(usr_inst.instance.name)
 | 
			
		||||
            all_user_vms[usr_inst].update({'compute_id': usr_inst.instance.compute.id})
 | 
			
		||||
    return all_user_vms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_host_instances(compute):
 | 
			
		||||
    all_host_vms = OrderedDict()
 | 
			
		||||
 | 
			
		||||
    # if compute.status:
 | 
			
		||||
    comp_node_info = compute.proxy.get_node_info()
 | 
			
		||||
    comp_mem = compute.proxy.get_memory_usage()
 | 
			
		||||
    comp_instances = compute.proxy.get_host_instances(True)
 | 
			
		||||
 | 
			
		||||
    # if comp_instances:
 | 
			
		||||
    comp_info = {
 | 
			
		||||
        "id": compute.id,
 | 
			
		||||
        "name": compute.name,
 | 
			
		||||
        "status": compute.status,
 | 
			
		||||
        "cpu": comp_node_info[3],
 | 
			
		||||
        "mem_size": comp_node_info[2],
 | 
			
		||||
        "mem_perc": comp_mem['percent'],
 | 
			
		||||
    }
 | 
			
		||||
    # refr(compute)
 | 
			
		||||
    all_host_vms[comp_info["id"], comp_info["name"], comp_info["status"], comp_info["cpu"], comp_info["mem_size"],
 | 
			
		||||
                 comp_info["mem_perc"]] = comp_instances
 | 
			
		||||
    for vm, info in comp_instances.items():
 | 
			
		||||
        # TODO: Delete this function completely
 | 
			
		||||
        refresh_instance_database(comp_info, vm, info, all_host_vms, User.objects.get(pk=1))
 | 
			
		||||
 | 
			
		||||
    # else:
 | 
			
		||||
    #     raise libvirtError(_(f"Problem occurred with host: {compute.name} - {status}"))
 | 
			
		||||
    return all_host_vms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_dhcp_mac_address(vname):
 | 
			
		||||
    dhcp_file = settings.BASE_DIR + '/dhcpd.conf'
 | 
			
		||||
    mac = ''
 | 
			
		||||
    if os.path.isfile(dhcp_file):
 | 
			
		||||
        with open(dhcp_file, 'r') as f:
 | 
			
		||||
            name_found = False
 | 
			
		||||
            for line in f:
 | 
			
		||||
                if "host %s." % vname in line:
 | 
			
		||||
                    name_found = True
 | 
			
		||||
                if name_found and "hardware ethernet" in line:
 | 
			
		||||
                    mac = line.split(' ')[-1].strip().strip(';')
 | 
			
		||||
                    break
 | 
			
		||||
    return mac
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_random_mac_address():
 | 
			
		||||
    mac = '52:54:00:%02x:%02x:%02x' % (
 | 
			
		||||
        random.randint(0x00, 0xff),
 | 
			
		||||
        random.randint(0x00, 0xff),
 | 
			
		||||
        random.randint(0x00, 0xff),
 | 
			
		||||
    )
 | 
			
		||||
    return mac
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_clone_disk_name(disk, prefix, clone_name=''):
 | 
			
		||||
    if not disk['image']:
 | 
			
		||||
        return None
 | 
			
		||||
    if disk['image'].startswith(prefix) and clone_name:
 | 
			
		||||
        suffix = disk['image'][len(prefix):]
 | 
			
		||||
        image = f"{clone_name}{suffix}"
 | 
			
		||||
    elif "." in disk['image'] and len(disk['image'].rsplit(".", 1)[1]) <= 7:
 | 
			
		||||
        name, suffix = disk['image'].rsplit(".", 1)
 | 
			
		||||
        image = f"{name}-clone.{suffix}"
 | 
			
		||||
    else:
 | 
			
		||||
        image = f"{disk['image']}-clone"
 | 
			
		||||
    return image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# TODO: this function is not used
 | 
			
		||||
def _get_clone_disks(disks, vname=''):
 | 
			
		||||
    clone_disks = []
 | 
			
		||||
    for disk in disks:
 | 
			
		||||
        new_image = get_clone_disk_name(disk, vname)
 | 
			
		||||
        if not new_image:
 | 
			
		||||
            continue
 | 
			
		||||
        new_disk = {
 | 
			
		||||
            'dev': disk['dev'],
 | 
			
		||||
            'storage': disk['storage'],
 | 
			
		||||
            'image': new_image,
 | 
			
		||||
            'format': disk['format'],
 | 
			
		||||
        }
 | 
			
		||||
        clone_disks.append(new_disk)
 | 
			
		||||
    return clone_disks
 | 
			
		||||
							
								
								
									
										2665
									
								
								instances/views.py
									
										
									
									
									
								
							
							
						
						
									
										2665
									
								
								instances/views.py
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -4,8 +4,14 @@ function filter_table() {
 | 
			
		|||
    $('.searchable tr').filter(function () {
 | 
			
		||||
        return rex.test($(this).text());
 | 
			
		||||
    }).show();
 | 
			
		||||
    Cookies.set("instances_filter", $(this).val(), { expires: 1 });
 | 
			
		||||
}
 | 
			
		||||
$(document).ready(function () {
 | 
			
		||||
    instances_filter_cookie = Cookies.get("instances_filter");
 | 
			
		||||
    if (instances_filter_cookie) {
 | 
			
		||||
        $('#filter').val(instances_filter_cookie);
 | 
			
		||||
        $('#filter').each(filter_table);
 | 
			
		||||
    }
 | 
			
		||||
    (function ($) {
 | 
			
		||||
        $('#filter').keyup(filter_table)
 | 
			
		||||
    }(jQuery));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,12 +4,9 @@ from django.utils.translation import ugettext_lazy as _
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class AddStgPool(forms.Form):
 | 
			
		||||
    name = forms.CharField(error_messages={'required': _('No pool name has been entered')},
 | 
			
		||||
                           max_length=20)
 | 
			
		||||
    name = forms.CharField(error_messages={'required': _('No pool name has been entered')}, max_length=20)
 | 
			
		||||
    stg_type = forms.CharField(max_length=10)
 | 
			
		||||
    target = forms.CharField(error_messages={'required': _('No path has been entered')},
 | 
			
		||||
                             max_length=100,
 | 
			
		||||
                             required=False)
 | 
			
		||||
    target = forms.CharField(error_messages={'required': _('No path has been entered')}, max_length=100, required=False)
 | 
			
		||||
    source = forms.CharField(max_length=100, required=False)
 | 
			
		||||
    ceph_user = forms.CharField(required=False)
 | 
			
		||||
    ceph_host = forms.CharField(required=False)
 | 
			
		||||
| 
						 | 
				
			
			@ -51,11 +48,9 @@ class AddStgPool(forms.Form):
 | 
			
		|||
        return source
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AddImage(forms.Form):
 | 
			
		||||
class CreateVolumeForm(forms.Form):
 | 
			
		||||
    name = forms.CharField(max_length=120)
 | 
			
		||||
    format = forms.ChoiceField(required=True, choices=(('qcow2', 'qcow2 (recommended)'),
 | 
			
		||||
                                                       ('qcow', 'qcow'),
 | 
			
		||||
                                                       ('raw', 'raw')))
 | 
			
		||||
    format = forms.ChoiceField(required=True, choices=(('qcow2', 'qcow2 (recommended)'), ('qcow', 'qcow'), ('raw', 'raw')))
 | 
			
		||||
    size = forms.IntegerField()
 | 
			
		||||
    meta_prealloc = forms.BooleanField(required=False)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +59,6 @@ class AddImage(forms.Form):
 | 
			
		|||
        have_symbol = re.match('^[a-zA-Z0-9._-]+$', name)
 | 
			
		||||
        if not have_symbol:
 | 
			
		||||
            raise forms.ValidationError(_('The image name must not contain any special characters'))
 | 
			
		||||
        elif len(name) > 120:
 | 
			
		||||
            raise forms.ValidationError(_('The image name must not exceed 120 characters'))
 | 
			
		||||
        return name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -73,9 +66,7 @@ class CloneImage(forms.Form):
 | 
			
		|||
    name = forms.CharField(max_length=120)
 | 
			
		||||
    image = forms.CharField(max_length=120)
 | 
			
		||||
    convert = forms.BooleanField(required=False)
 | 
			
		||||
    format = forms.ChoiceField(required=False, choices=(('qcow2', 'qcow2 (recommended)'),
 | 
			
		||||
                                                        ('qcow', 'qcow'),
 | 
			
		||||
                                                        ('raw', 'raw')))
 | 
			
		||||
    format = forms.ChoiceField(required=False, choices=(('qcow2', 'qcow2 (recommended)'), ('qcow', 'qcow'), ('raw', 'raw')))
 | 
			
		||||
    meta_prealloc = forms.BooleanField(required=False)
 | 
			
		||||
 | 
			
		||||
    def clean_name(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load bootstrap4 %}
 | 
			
		||||
{% if request.user.is_superuser %}
 | 
			
		||||
    {% if state != 0 %}
 | 
			
		||||
        {% if pool == "iso" %}
 | 
			
		||||
| 
						 | 
				
			
			@ -46,42 +47,15 @@
 | 
			
		|||
                            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="modal-body">
 | 
			
		||||
                            <form method="post" role="form" aria-label="Create volume to storage form">{% csrf_token %}
 | 
			
		||||
                                <div class="form-group row">
 | 
			
		||||
                                    <label class="col-sm-3 col-form-label">{% trans "Name" %}</label>
 | 
			
		||||
                                    <div class="col-sm-6">
 | 
			
		||||
                                        <input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+">
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="form-group row" id="image_format">
 | 
			
		||||
                                    <label class="col-sm-3 col-form-label">{% trans "Format" %}</label>
 | 
			
		||||
                                    <div class="col-sm-6">
 | 
			
		||||
                                        <select name="format" class="form-control image-format">
 | 
			
		||||
                                            <option value="qcow2">{% trans "qcow2" %}</option>
 | 
			
		||||
                                            <option value="qcow">{% trans "qcow" %}</option>
 | 
			
		||||
                                            <option value="raw">{% trans "raw" %}</option>
 | 
			
		||||
                                        </select>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="form-group row">
 | 
			
		||||
                                    <label class="col-sm-3 col-form-label">{% trans "Size" %}</label>
 | 
			
		||||
                                    <div class="col-sm-6">
 | 
			
		||||
                                        <input type="text" class="form-control" name="size" value="10" maxlength="3" required pattern="[0-9]+">
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                    <label class="col-sm-1 col-form-label">{% trans "GB" %}</label>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="form-group row meta-prealloc">
 | 
			
		||||
                                    <label class="col-sm-3 col-form-label">{% trans "Metadata" %}</label>
 | 
			
		||||
                                    <div class="col-sm-6">
 | 
			
		||||
                                        <input type="checkbox" name="meta_prealloc" value="true">
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            <form action="{% url 'create_volume' compute_id pool %}" id="create-volume" method="post" role="form" aria-label="Create volume to storage form">
 | 
			
		||||
                                {% csrf_token %}
 | 
			
		||||
                                {% bootstrap_form form layout='horizontal' %}
 | 
			
		||||
                            </form>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="modal-footer">
 | 
			
		||||
                            <button type="button" class="btn btn-secondary" data-dismiss="modal">{% trans "Close" %}</button>
 | 
			
		||||
                            <button type="submit" class="btn btn-primary" name="add_volume">{% trans "Create" %}</button>
 | 
			
		||||
                            <button type="submit" form="create-volume" class="btn btn-primary">{% trans "Create" %}</button>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        </form>
 | 
			
		||||
                    </div> <!-- /.modal-content -->
 | 
			
		||||
                </div> <!-- /.modal-dialog -->
 | 
			
		||||
            </div> <!-- /.modal -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,6 @@
 | 
			
		|||
    <!-- /.row -->
 | 
			
		||||
 | 
			
		||||
    {% include 'errors_block.html' %}
 | 
			
		||||
    {% include 'messages_block.html' %}
 | 
			
		||||
 | 
			
		||||
    <dl class="ml-3 row">
 | 
			
		||||
        <dt class="col-6">{% trans "Pool name" %}</dt>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,6 @@
 | 
			
		|||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
 | 
			
		||||
class StoragesTestCase(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        pass
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,17 @@
 | 
			
		|||
import json
 | 
			
		||||
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.http import HttpResponseRedirect, HttpResponse
 | 
			
		||||
from django.shortcuts import render, get_object_or_404
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from django.http import HttpResponse, HttpResponseRedirect
 | 
			
		||||
from django.shortcuts import get_object_or_404, redirect, render
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from libvirt import libvirtError
 | 
			
		||||
 | 
			
		||||
from admin.decorators import superuser_only
 | 
			
		||||
from computes.models import Compute
 | 
			
		||||
from appsettings.models import AppSettings
 | 
			
		||||
from storages.forms import AddStgPool, AddImage, CloneImage
 | 
			
		||||
from appsettings.settings import app_settings
 | 
			
		||||
from computes.models import Compute
 | 
			
		||||
from storages.forms import AddStgPool, CloneImage, CreateVolumeForm
 | 
			
		||||
from vrtManager.storage import wvmStorage, wvmStorages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -79,97 +81,58 @@ def storage(request, compute_id, pool):
 | 
			
		|||
            destination.write(chunk)
 | 
			
		||||
        destination.close()
 | 
			
		||||
 | 
			
		||||
    error_messages = []
 | 
			
		||||
    compute = get_object_or_404(Compute, pk=compute_id)
 | 
			
		||||
    meta_prealloc = False
 | 
			
		||||
    form = CreateVolumeForm()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        conn = wvmStorage(compute.hostname, compute.login, compute.password, compute.type, pool)
 | 
			
		||||
    conn = wvmStorage(compute.hostname, compute.login, compute.password, compute.type, pool)
 | 
			
		||||
 | 
			
		||||
        storages = conn.get_storages()
 | 
			
		||||
        state = conn.is_active()
 | 
			
		||||
        size, free = conn.get_size()
 | 
			
		||||
        used = (size - free)
 | 
			
		||||
        if state:
 | 
			
		||||
            percent = (used * 100) // size
 | 
			
		||||
        else:
 | 
			
		||||
            percent = 0
 | 
			
		||||
        status = conn.get_status()
 | 
			
		||||
        path = conn.get_target_path()
 | 
			
		||||
        type = conn.get_type()
 | 
			
		||||
        autostart = conn.get_autostart()
 | 
			
		||||
    storages = conn.get_storages()
 | 
			
		||||
    state = conn.is_active()
 | 
			
		||||
    size, free = conn.get_size()
 | 
			
		||||
    used = (size - free)
 | 
			
		||||
    if state:
 | 
			
		||||
        percent = (used * 100) // size
 | 
			
		||||
    else:
 | 
			
		||||
        percent = 0
 | 
			
		||||
    status = conn.get_status()
 | 
			
		||||
    path = conn.get_target_path()
 | 
			
		||||
    type = conn.get_type()
 | 
			
		||||
    autostart = conn.get_autostart()
 | 
			
		||||
 | 
			
		||||
        if state:
 | 
			
		||||
            conn.refresh()
 | 
			
		||||
            volumes = conn.update_volumes()
 | 
			
		||||
        else:
 | 
			
		||||
            volumes = None
 | 
			
		||||
    except libvirtError as lib_err:
 | 
			
		||||
        error_messages.append(lib_err)
 | 
			
		||||
    if state:
 | 
			
		||||
        conn.refresh()
 | 
			
		||||
        volumes = conn.update_volumes()
 | 
			
		||||
    else:
 | 
			
		||||
        volumes = None
 | 
			
		||||
 | 
			
		||||
    if request.method == 'POST':
 | 
			
		||||
        if 'start' in request.POST:
 | 
			
		||||
            try:
 | 
			
		||||
                conn.start()
 | 
			
		||||
                return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
            except libvirtError as lib_err:
 | 
			
		||||
                error_messages.append(lib_err)
 | 
			
		||||
            conn.start()
 | 
			
		||||
            return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
        if 'stop' in request.POST:
 | 
			
		||||
            try:
 | 
			
		||||
                conn.stop()
 | 
			
		||||
                return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
            except libvirtError as lib_err:
 | 
			
		||||
                error_messages.append(lib_err)
 | 
			
		||||
            conn.stop()
 | 
			
		||||
            return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
        if 'delete' in request.POST:
 | 
			
		||||
            try:
 | 
			
		||||
                conn.delete()
 | 
			
		||||
                return HttpResponseRedirect(reverse('storages', args=[compute_id]))
 | 
			
		||||
            except libvirtError as lib_err:
 | 
			
		||||
                error_messages.append(lib_err)
 | 
			
		||||
            conn.delete()
 | 
			
		||||
            return HttpResponseRedirect(reverse('storages', args=[compute_id]))
 | 
			
		||||
        if 'set_autostart' in request.POST:
 | 
			
		||||
            try:
 | 
			
		||||
                conn.set_autostart(1)
 | 
			
		||||
                return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
            except libvirtError as lib_err:
 | 
			
		||||
                error_messages.append(lib_err)
 | 
			
		||||
            conn.set_autostart(1)
 | 
			
		||||
            return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
        if 'unset_autostart' in request.POST:
 | 
			
		||||
            try:
 | 
			
		||||
                conn.set_autostart(0)
 | 
			
		||||
                return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
            except libvirtError as lib_err:
 | 
			
		||||
                error_messages.append(lib_err)
 | 
			
		||||
        if 'add_volume' in request.POST:
 | 
			
		||||
            form = AddImage(request.POST)
 | 
			
		||||
            if form.is_valid():
 | 
			
		||||
                data = form.cleaned_data
 | 
			
		||||
                if data['meta_prealloc'] and data['format'] == 'qcow2':
 | 
			
		||||
                    meta_prealloc = True
 | 
			
		||||
                try:
 | 
			
		||||
                    disk_owner = AppSettings.objects.filter(key__startswith="INSTANCE_VOLUME_DEFAULT_OWNER")
 | 
			
		||||
                    disk_owner_uid = int(disk_owner.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_UID").value)
 | 
			
		||||
                    disk_owner_gid = int(disk_owner.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_GID").value)
 | 
			
		||||
 | 
			
		||||
                    name = conn.create_volume(data['name'], data['size'], data['format'], meta_prealloc, disk_owner_uid, disk_owner_gid)
 | 
			
		||||
                    messages.success(request, _(f"Image file {name} is created successfully"))
 | 
			
		||||
                    return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
                except libvirtError as lib_err:
 | 
			
		||||
                    error_messages.append(lib_err)
 | 
			
		||||
            else:
 | 
			
		||||
                for msg_err in form.errors.values():
 | 
			
		||||
                    error_messages.append(msg_err.as_text())
 | 
			
		||||
            conn.set_autostart(0)
 | 
			
		||||
            return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
        if 'del_volume' in request.POST:
 | 
			
		||||
            volname = request.POST.get('volname', '')
 | 
			
		||||
            try:
 | 
			
		||||
                vol = conn.get_volume(volname)
 | 
			
		||||
                vol.delete(0)
 | 
			
		||||
                messages.success(request, _(f"Volume: {volname} is deleted."))
 | 
			
		||||
                return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
            except libvirtError as lib_err:
 | 
			
		||||
                error_messages.append(lib_err)
 | 
			
		||||
            vol = conn.get_volume(volname)
 | 
			
		||||
            vol.delete(0)
 | 
			
		||||
            messages.success(request, _(f"Volume: {volname} is deleted."))
 | 
			
		||||
            return redirect(reverse('storage', args=[compute.id, pool]))
 | 
			
		||||
            # return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
        if 'iso_upload' in request.POST:
 | 
			
		||||
            if str(request.FILES['file']) in conn.update_volumes():
 | 
			
		||||
                error_msg = _("ISO image already exist")
 | 
			
		||||
                error_messages.append(error_msg)
 | 
			
		||||
                messages.error(request, error_msg)
 | 
			
		||||
            else:
 | 
			
		||||
                handle_uploaded_file(path, request.FILES['file'])
 | 
			
		||||
                messages.success(request, _(f"ISO: {request.FILES['file']} is uploaded."))
 | 
			
		||||
| 
						 | 
				
			
			@ -182,29 +145,62 @@ def storage(request, compute_id, pool):
 | 
			
		|||
                meta_prealloc = 0
 | 
			
		||||
                if img_name in conn.update_volumes():
 | 
			
		||||
                    msg = _("Name of volume already in use")
 | 
			
		||||
                    error_messages.append(msg)
 | 
			
		||||
                if not error_messages:
 | 
			
		||||
                    if data['convert']:
 | 
			
		||||
                        format = data['format']
 | 
			
		||||
                        if data['meta_prealloc'] and data['format'] == 'qcow2':
 | 
			
		||||
                            meta_prealloc = True
 | 
			
		||||
                    else:
 | 
			
		||||
                        format = None
 | 
			
		||||
                    try:
 | 
			
		||||
                        name = conn.clone_volume(data['image'], data['name'], format, meta_prealloc)
 | 
			
		||||
                        messages.success(request, _(f"{data['image']} image cloned as {name} successfully"))
 | 
			
		||||
                        return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
                    except libvirtError as lib_err:
 | 
			
		||||
                        error_messages.append(lib_err)
 | 
			
		||||
                    messages.error(request, msg)
 | 
			
		||||
                if data['convert']:
 | 
			
		||||
                    format = data['format']
 | 
			
		||||
                    if data['meta_prealloc'] and data['format'] == 'qcow2':
 | 
			
		||||
                        meta_prealloc = True
 | 
			
		||||
                else:
 | 
			
		||||
                    format = None
 | 
			
		||||
                try:
 | 
			
		||||
                    name = conn.clone_volume(data['image'], data['name'], format, meta_prealloc)
 | 
			
		||||
                    messages.success(request, _(f"{data['image']} image cloned as {name} successfully"))
 | 
			
		||||
                    return HttpResponseRedirect(request.get_full_path())
 | 
			
		||||
                except libvirtError as lib_err:
 | 
			
		||||
                    messages.error(request, lib_err)
 | 
			
		||||
            else:
 | 
			
		||||
                for msg_err in form.errors.values():
 | 
			
		||||
                    error_messages.append(msg_err.as_text())
 | 
			
		||||
                    messages.error(request, msg_err.as_text())
 | 
			
		||||
 | 
			
		||||
    conn.close()
 | 
			
		||||
 | 
			
		||||
    return render(request, 'storage.html', locals())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@superuser_only
 | 
			
		||||
def create_volume(request, compute_id, pool):
 | 
			
		||||
    compute = get_object_or_404(Compute, pk=compute_id)
 | 
			
		||||
    meta_prealloc = False
 | 
			
		||||
 | 
			
		||||
    conn = wvmStorage(compute.hostname, compute.login, compute.password, compute.type, pool)
 | 
			
		||||
 | 
			
		||||
    storages = conn.get_storages()
 | 
			
		||||
 | 
			
		||||
    form = CreateVolumeForm(request.POST or None)
 | 
			
		||||
    if form.is_valid():
 | 
			
		||||
        data = form.cleaned_data
 | 
			
		||||
        if data['meta_prealloc'] and data['format'] == 'qcow2':
 | 
			
		||||
            meta_prealloc = True
 | 
			
		||||
 | 
			
		||||
        disk_owner_uid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
 | 
			
		||||
        disk_owner_gid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
 | 
			
		||||
 | 
			
		||||
        name = conn.create_volume(
 | 
			
		||||
            data['name'],
 | 
			
		||||
            data['size'],
 | 
			
		||||
            data['format'],
 | 
			
		||||
            meta_prealloc,
 | 
			
		||||
            disk_owner_uid,
 | 
			
		||||
            disk_owner_gid,
 | 
			
		||||
        )
 | 
			
		||||
        messages.success(request, _(f"Image file {name} is created successfully"))
 | 
			
		||||
    else:
 | 
			
		||||
        for msg_err in form.errors.values():
 | 
			
		||||
            messages.error(request, msg_err.as_text())
 | 
			
		||||
 | 
			
		||||
    return redirect(reverse('storage', args=[compute.id, pool]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_volumes(request, compute_id, pool):
 | 
			
		||||
    """
 | 
			
		||||
    :param request:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,11 +37,21 @@
 | 
			
		|||
    {% include 'navbar.html' %}
 | 
			
		||||
 | 
			
		||||
    <div role="main" class="container">
 | 
			
		||||
    {% bootstrap_messages %}
 | 
			
		||||
    {% block content %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
    </div> <!-- /container -->
 | 
			
		||||
 | 
			
		||||
      <div class"row">
 | 
			
		||||
        <div class="col-sm-12">
 | 
			
		||||
          <div class="float-right">
 | 
			
		||||
            {% block page_header_extra %}
 | 
			
		||||
            {% endblock page_header_extra %}
 | 
			
		||||
          </div>
 | 
			
		||||
          <h3 class="page-header">
 | 
			
		||||
            {% block page_header %}
 | 
			
		||||
            {% endblock page_header %}
 | 
			
		||||
          </h3>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      {% bootstrap_messages %}
 | 
			
		||||
      {% block content %}{% endblock %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Bootstrap core JavaScript
 | 
			
		||||
    ================================================== -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,20 +5,18 @@
 | 
			
		|||
 | 
			
		||||
{% block title %}{{ title }}{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block page_header %}{{ title }}{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
<div class="row">
 | 
			
		||||
    <div class="col-lg-12">
 | 
			
		||||
        <h2 class="page-header">{{ title }}</h2>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
{% bootstrap_messages %}
 | 
			
		||||
<div class="row">
 | 
			
		||||
    <div class="thumbnail col-sm-10 offset-1">
 | 
			
		||||
<div class="card">
 | 
			
		||||
    <div class="card-body">
 | 
			
		||||
        <form id="create-update" action="" method="post">
 | 
			
		||||
            {% csrf_token %}
 | 
			
		||||
            {% bootstrap_form form layout='horizontal' %}
 | 
			
		||||
        </form>
 | 
			
		||||
        <div class="form-group float-right">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="card-footer">
 | 
			
		||||
        <div class="form-group mb-0 float-right">
 | 
			
		||||
            <a class="btn btn-primary" href="javascript:history.back()">{% icon 'times' %} {% trans "Cancel" %}</a>
 | 
			
		||||
            <button type="submit" form="create-update" class="btn btn-success">
 | 
			
		||||
                {% icon 'check' %} {% trans "Save" %}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,6 @@ from vrtManager.rwlock import ReadWriteLock
 | 
			
		|||
from django.conf import settings
 | 
			
		||||
from libvirt import libvirtError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONN_SOCKET = 4
 | 
			
		||||
CONN_TLS = 3
 | 
			
		||||
CONN_SSH = 2
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +18,6 @@ TCP_PORT = 16509
 | 
			
		|||
 | 
			
		||||
class wvmEventLoop(threading.Thread):
 | 
			
		||||
    """ Event Loop Class"""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
 | 
			
		||||
        # register the default event implementation
 | 
			
		||||
        # of libvirt, as we do not have an existing
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +46,6 @@ class wvmConnection(object):
 | 
			
		|||
    class representing a single connection stored in the Connection Manager
 | 
			
		||||
    # to-do: may also need some locking to ensure to not connect simultaniously in 2 threads
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, host, login, passwd, conn):
 | 
			
		||||
        """
 | 
			
		||||
        Sets all class attributes and tries to open the connection
 | 
			
		||||
| 
						 | 
				
			
			@ -208,7 +205,7 @@ class wvmConnection(object):
 | 
			
		|||
            except:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        if self.type == CONN_TCP:
 | 
			
		||||
            type_str = 'tcp'
 | 
			
		||||
        elif self.type == CONN_SSH:
 | 
			
		||||
| 
						 | 
				
			
			@ -323,7 +320,7 @@ class wvmConnectionManager(object):
 | 
			
		|||
                socket_host.connect((hostname, TLS_PORT))
 | 
			
		||||
            if conn_type == CONN_SOCKET:
 | 
			
		||||
                socket_host = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 | 
			
		||||
                socket_host.connect('/var/run/libvirt/libvirt-sock')    
 | 
			
		||||
                socket_host.connect('/var/run/libvirt/libvirt-sock')
 | 
			
		||||
            socket_host.close()
 | 
			
		||||
            return True
 | 
			
		||||
        except Exception as err:
 | 
			
		||||
| 
						 | 
				
			
			@ -332,7 +329,7 @@ class wvmConnectionManager(object):
 | 
			
		|||
 | 
			
		||||
connection_manager = wvmConnectionManager(
 | 
			
		||||
    settings.LIBVIRT_KEEPALIVE_INTERVAL if hasattr(settings, 'LIBVIRT_KEEPALIVE_INTERVAL') else 5,
 | 
			
		||||
    settings.LIBVIRT_KEEPALIVE_COUNT if hasattr(settings, 'LIBVIRT_KEEPALIVE_COUNT') else 5
 | 
			
		||||
    settings.LIBVIRT_KEEPALIVE_COUNT if hasattr(settings, 'LIBVIRT_KEEPALIVE_COUNT') else 5,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -374,9 +371,11 @@ class wvmConnect(object):
 | 
			
		|||
 | 
			
		||||
                result["machines"] = []
 | 
			
		||||
                for m in arch_el.xpath("machine"):
 | 
			
		||||
                    result["machines"].append({"machine": m.text,
 | 
			
		||||
                                               "max_cpu": m.get("maxCpus"),
 | 
			
		||||
                                               "canonical": m.get("canonical")})
 | 
			
		||||
                    result["machines"].append({
 | 
			
		||||
                        "machine": m.text,
 | 
			
		||||
                        "max_cpu": m.get("maxCpus"),
 | 
			
		||||
                        "canonical": m.get("canonical")
 | 
			
		||||
                    })
 | 
			
		||||
 | 
			
		||||
                guest_el = arch_el.getparent()
 | 
			
		||||
                for f in guest_el.xpath("features"):
 | 
			
		||||
| 
						 | 
				
			
			@ -385,6 +384,7 @@ class wvmConnect(object):
 | 
			
		|||
                result["os_type"] = guest_el.find("os_type").text
 | 
			
		||||
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_cap_xml(), func=guests)
 | 
			
		||||
 | 
			
		||||
    def get_dom_capabilities(self, arch, machine):
 | 
			
		||||
| 
						 | 
				
			
			@ -560,6 +560,7 @@ class wvmConnect(object):
 | 
			
		|||
                arch_name = arch.xpath('@name')[0]
 | 
			
		||||
                result[arch_name] = domain_types
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_cap_xml(), func=hypervisors)
 | 
			
		||||
 | 
			
		||||
    def get_hypervisors_machines(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -573,6 +574,7 @@ class wvmConnect(object):
 | 
			
		|||
 | 
			
		||||
                result[arch] = self.get_machine_types(arch)
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_cap_xml(), func=machines)
 | 
			
		||||
 | 
			
		||||
    def get_emulator(self, arch):
 | 
			
		||||
| 
						 | 
				
			
			@ -607,6 +609,7 @@ class wvmConnect(object):
 | 
			
		|||
                arch_name = arch.xpath('@name')[0]
 | 
			
		||||
                result[arch_name] = emulator
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_cap_xml(), func=emulators)
 | 
			
		||||
 | 
			
		||||
    def get_os_loaders(self, arch='x86_64', machine='pc'):
 | 
			
		||||
| 
						 | 
				
			
			@ -617,6 +620,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        def get_os_loaders(ctx):
 | 
			
		||||
            return [v.text for v in ctx.xpath("/domainCapabilities/os/loader[@supported='yes']/value")]
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_os_loaders)
 | 
			
		||||
 | 
			
		||||
    def get_os_loader_enums(self, arch, machine):
 | 
			
		||||
| 
						 | 
				
			
			@ -632,6 +636,7 @@ class wvmConnect(object):
 | 
			
		|||
                path = "/domainCapabilities/os/loader[@supported='yes']/enum[@name='{}']/value".format(enum)
 | 
			
		||||
                result[enum] = [v.text for v in ctx.xpath(path)]
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_os_loader_enums)
 | 
			
		||||
 | 
			
		||||
    def get_disk_bus_types(self, arch, machine):
 | 
			
		||||
| 
						 | 
				
			
			@ -642,6 +647,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        def get_bus_list(ctx):
 | 
			
		||||
            return [v.text for v in ctx.xpath("/domainCapabilities/devices/disk/enum[@name='bus']/value")]
 | 
			
		||||
 | 
			
		||||
        # return [ 'ide', 'scsi', 'usb', 'virtio' ]
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_bus_list)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -653,6 +659,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        def get_device_list(ctx):
 | 
			
		||||
            return [v.text for v in ctx.xpath("/domainCapabilities/devices/disk/enum[@name='diskDevice']/value")]
 | 
			
		||||
 | 
			
		||||
        # return [ 'disk', 'cdrom', 'floppy', 'lun' ]
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_device_list)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -664,6 +671,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        def get_graphics_list(ctx):
 | 
			
		||||
            return [v.text for v in ctx.xpath("/domainCapabilities/devices/graphics/enum[@name='type']/value")]
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_graphics_list)
 | 
			
		||||
 | 
			
		||||
    def get_cpu_modes(self, arch, machine):
 | 
			
		||||
| 
						 | 
				
			
			@ -674,6 +682,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        def get_cpu_modes(ctx):
 | 
			
		||||
            return [v for v in ctx.xpath("/domainCapabilities/cpu/mode[@supported='yes']/@name")]
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_cpu_modes)
 | 
			
		||||
 | 
			
		||||
    def get_cpu_custom_types(self, arch, machine):
 | 
			
		||||
| 
						 | 
				
			
			@ -688,6 +697,7 @@ class wvmConnect(object):
 | 
			
		|||
            result = [v.text for v in ctx.xpath(usable_yes)]
 | 
			
		||||
            result += [v.text for v in ctx.xpath(usable_unknown)]
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_custom_list)
 | 
			
		||||
 | 
			
		||||
    def get_hostdev_modes(self, arch, machine):
 | 
			
		||||
| 
						 | 
				
			
			@ -698,6 +708,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        def get_hostdev_list(ctx):
 | 
			
		||||
            return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='mode']/value")]
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
 | 
			
		||||
 | 
			
		||||
    def get_hostdev_startup_policies(self, arch, machine):
 | 
			
		||||
| 
						 | 
				
			
			@ -708,6 +719,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        def get_hostdev_list(ctx):
 | 
			
		||||
            return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='startupPolicy']/value")]
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
 | 
			
		||||
 | 
			
		||||
    def get_hostdev_subsys_types(self, arch, machine):
 | 
			
		||||
| 
						 | 
				
			
			@ -718,6 +730,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        def get_hostdev_list(ctx):
 | 
			
		||||
            return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='subsysType']/value")]
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
 | 
			
		||||
 | 
			
		||||
    def get_network_models(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -748,9 +761,10 @@ class wvmConnect(object):
 | 
			
		|||
            result = []
 | 
			
		||||
            for video_enum in ctx.xpath('/domainCapabilities/devices/video/enum'):
 | 
			
		||||
                if video_enum.xpath("@name")[0] == "modelType":
 | 
			
		||||
                    for values in video_enum: 
 | 
			
		||||
                    for values in video_enum:
 | 
			
		||||
                        result.append(values.text)
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_video_list)
 | 
			
		||||
 | 
			
		||||
    def get_iface(self, name):
 | 
			
		||||
| 
						 | 
				
			
			@ -829,7 +843,7 @@ class wvmConnect(object):
 | 
			
		|||
            mem = util.get_xpath(doc, "/domain/currentMemory")
 | 
			
		||||
            mem = int(mem) / 1024
 | 
			
		||||
            if raw_mem_size:
 | 
			
		||||
                mem = int(mem) * (1024*1024)
 | 
			
		||||
                mem = int(mem) * (1024 * 1024)
 | 
			
		||||
            cur_vcpu = util.get_xpath(doc, "/domain/vcpu/@current")
 | 
			
		||||
            if cur_vcpu:
 | 
			
		||||
                vcpu = cur_vcpu
 | 
			
		||||
| 
						 | 
				
			
			@ -840,6 +854,7 @@ class wvmConnect(object):
 | 
			
		|||
            description = util.get_xpath(doc, "/domain/description")
 | 
			
		||||
            description = description if description else ''
 | 
			
		||||
            return mem, vcpu, title, description
 | 
			
		||||
 | 
			
		||||
        for name in self.get_instances():
 | 
			
		||||
            dom = self.get_instance(name)
 | 
			
		||||
            xml = dom.XMLDesc(0)
 | 
			
		||||
| 
						 | 
				
			
			@ -871,6 +886,7 @@ class wvmConnect(object):
 | 
			
		|||
            description = util.get_xpath(ctx, "/domain/description")
 | 
			
		||||
            description = description if description else ''
 | 
			
		||||
            return mem, vcpu, title, description
 | 
			
		||||
 | 
			
		||||
        (mem, vcpu, title, description) = util.get_xml_path(xml, func=get_info)
 | 
			
		||||
        return {
 | 
			
		||||
            'name': dom.name(),
 | 
			
		||||
| 
						 | 
				
			
			@ -929,8 +945,7 @@ class wvmConnect(object):
 | 
			
		|||
        """
 | 
			
		||||
        Return True if libvirt advertises support for proper UEFI setup
 | 
			
		||||
        """
 | 
			
		||||
        return ("readonly" in loader_enums and
 | 
			
		||||
                "yes" in loader_enums.get("readonly"))
 | 
			
		||||
        return ("readonly" in loader_enums and "yes" in loader_enums.get("readonly"))
 | 
			
		||||
 | 
			
		||||
    def is_supports_virtio(self, arch, machine):
 | 
			
		||||
        if not self.is_qemu():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -206,6 +206,8 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
        cur_vcpu = util.get_xml_path(self._XMLDesc(0), "/domain/vcpu/@current")
 | 
			
		||||
        if cur_vcpu:
 | 
			
		||||
            return int(cur_vcpu)
 | 
			
		||||
        else:
 | 
			
		||||
            return self.get_vcpu()
 | 
			
		||||
 | 
			
		||||
    def get_arch(self):
 | 
			
		||||
        return util.get_xml_path(self._XMLDesc(0), "/domain/os/type/@arch")
 | 
			
		||||
| 
						 | 
				
			
			@ -251,7 +253,6 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
        return title if title else ''
 | 
			
		||||
 | 
			
		||||
    def get_filterrefs(self):
 | 
			
		||||
 | 
			
		||||
        def filterrefs(ctx):
 | 
			
		||||
            result = []
 | 
			
		||||
            for net in ctx.xpath('/domain/devices/interface'):
 | 
			
		||||
| 
						 | 
				
			
			@ -295,8 +296,7 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
                for addr in addrs["addrs"]:
 | 
			
		||||
                    if addr["type"] == 0:
 | 
			
		||||
                        ipv4.append(addr["addr"])
 | 
			
		||||
                    elif (addr["type"] == 1 and
 | 
			
		||||
                          not str(addr["addr"]).startswith("fe80")):
 | 
			
		||||
                    elif (addr["type"] == 1 and not str(addr["addr"]).startswith("fe80")):
 | 
			
		||||
                        ipv6.append(addr["addr"] + "/" + str(addr["prefix"]))
 | 
			
		||||
            return ipv4, ipv6
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -335,14 +335,12 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
            return
 | 
			
		||||
 | 
			
		||||
        if self.is_agent_ready():
 | 
			
		||||
            self._ip_cache["qemuga"] = self._get_interface_addresses(
 | 
			
		||||
                VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT)
 | 
			
		||||
            self._ip_cache["qemuga"] = self._get_interface_addresses(VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT)
 | 
			
		||||
 | 
			
		||||
        arp_flag = 3  # libvirt."VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP"
 | 
			
		||||
        self._ip_cache["arp"] = self._get_interface_addresses(arp_flag)
 | 
			
		||||
 | 
			
		||||
    def get_net_devices(self):
 | 
			
		||||
 | 
			
		||||
        def networks(ctx):
 | 
			
		||||
            result = []
 | 
			
		||||
            inbound = outbound = []
 | 
			
		||||
| 
						 | 
				
			
			@ -370,17 +368,18 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
                    ipv4, ipv6 = self.get_interface_addresses(mac_inst)
 | 
			
		||||
                except libvirtError:
 | 
			
		||||
                    ipv4, ipv6 = None, None
 | 
			
		||||
                result.append({'mac': mac_inst,
 | 
			
		||||
                               'nic': nic_inst,
 | 
			
		||||
                               'target': target_inst,
 | 
			
		||||
                               'state': link_state,
 | 
			
		||||
                               'model': model_type,
 | 
			
		||||
                               'ipv4': ipv4,
 | 
			
		||||
                               'ipv6': ipv6,
 | 
			
		||||
                               'filterref': filterref_inst,
 | 
			
		||||
                               'inbound': inbound,
 | 
			
		||||
                               'outbound': outbound,
 | 
			
		||||
                               })
 | 
			
		||||
                result.append({
 | 
			
		||||
                    'mac': mac_inst,
 | 
			
		||||
                    'nic': nic_inst,
 | 
			
		||||
                    'target': target_inst,
 | 
			
		||||
                    'state': link_state,
 | 
			
		||||
                    'model': model_type,
 | 
			
		||||
                    'ipv4': ipv4,
 | 
			
		||||
                    'ipv6': ipv6,
 | 
			
		||||
                    'filterref': filterref_inst,
 | 
			
		||||
                    'inbound': inbound,
 | 
			
		||||
                    'outbound': outbound,
 | 
			
		||||
                })
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self._XMLDesc(0), func=networks)
 | 
			
		||||
| 
						 | 
				
			
			@ -388,7 +387,7 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
    def get_disk_devices(self):
 | 
			
		||||
        def disks(doc):
 | 
			
		||||
            result = []
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            for disk in doc.xpath('/domain/devices/disk'):
 | 
			
		||||
                dev = volume = storage = src_file = bus = None
 | 
			
		||||
                disk_format = used_size = disk_size = None
 | 
			
		||||
| 
						 | 
				
			
			@ -400,7 +399,13 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
                    try:
 | 
			
		||||
                        dev = disk.xpath('target/@dev')[0]
 | 
			
		||||
                        bus = disk.xpath('target/@bus')[0]
 | 
			
		||||
                        src_file = disk.xpath('source/@file|source/@dev|source/@name|source/@volume')[0]
 | 
			
		||||
                        try:
 | 
			
		||||
                            src_file = disk.xpath('source/@file|source/@dev|source/@name')[0]
 | 
			
		||||
                        except Exception as e:
 | 
			
		||||
                            v = disk.xpath('source/@volume')[0]
 | 
			
		||||
                            s_name = disk.xpath('source/@pool')[0]
 | 
			
		||||
                            s = self.wvm.storagePoolLookupByName(s_name)
 | 
			
		||||
                            src_file = s.storageVolLookupByName(v).path()
 | 
			
		||||
                        try:
 | 
			
		||||
                            disk_format = disk.xpath('driver/@type')[0]
 | 
			
		||||
                        except:
 | 
			
		||||
| 
						 | 
				
			
			@ -436,14 +441,26 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
                            storage = stg.name()
 | 
			
		||||
                        except libvirtError:
 | 
			
		||||
                            volume = src_file
 | 
			
		||||
                    except:
 | 
			
		||||
                        pass
 | 
			
		||||
                    except Exception as e:
 | 
			
		||||
                        print(f'Exception: {e}')
 | 
			
		||||
                    finally:
 | 
			
		||||
                        result.append(
 | 
			
		||||
                            {'dev': dev, 'bus': bus, 'image': volume, 'storage': storage, 'path': src_file,
 | 
			
		||||
                             'format': disk_format, 'size': disk_size, 'used': used_size,
 | 
			
		||||
                             'cache': disk_cache, 'io': disk_io, 'discard': disk_discard, 'detect_zeroes': disk_zeroes,
 | 
			
		||||
                             'readonly': readonly, 'shareable': shareable, 'serial': serial})
 | 
			
		||||
                        result.append({
 | 
			
		||||
                            'dev': dev,
 | 
			
		||||
                            'bus': bus,
 | 
			
		||||
                            'image': volume,
 | 
			
		||||
                            'storage': storage,
 | 
			
		||||
                            'path': src_file,
 | 
			
		||||
                            'format': disk_format,
 | 
			
		||||
                            'size': disk_size,
 | 
			
		||||
                            'used': used_size,
 | 
			
		||||
                            'cache': disk_cache,
 | 
			
		||||
                            'io': disk_io,
 | 
			
		||||
                            'discard': disk_discard,
 | 
			
		||||
                            'detect_zeroes': disk_zeroes,
 | 
			
		||||
                            'readonly': readonly,
 | 
			
		||||
                            'shareable': shareable,
 | 
			
		||||
                            'serial': serial
 | 
			
		||||
                        })
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
        return util.get_xml_path(self._XMLDesc(0), func=disks)
 | 
			
		||||
| 
						 | 
				
			
			@ -544,7 +561,7 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
                elif dev_type == 'usb':
 | 
			
		||||
                    pass
 | 
			
		||||
 | 
			
		||||
                boot_order[int(idx)-1] = {"type": dev_type, "dev": dev_device, "target": dev_target}
 | 
			
		||||
                boot_order[int(idx) - 1] = {"type": dev_type, "dev": dev_device, "target": dev_target}
 | 
			
		||||
 | 
			
		||||
        return boot_order
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -642,14 +659,25 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
            xmldom = ElementTree.tostring(tree).decode()
 | 
			
		||||
        self._defineXML(xmldom)
 | 
			
		||||
 | 
			
		||||
    def attach_disk(self, target_dev, source, target_bus='ide', disk_type='file',
 | 
			
		||||
                    disk_device='disk', driver_name='qemu', driver_type='raw',
 | 
			
		||||
                    readonly=False, shareable=False, serial=None,
 | 
			
		||||
                    cache_mode=None, io_mode=None, discard_mode=None, detect_zeroes_mode=None):
 | 
			
		||||
    def attach_disk(self,
 | 
			
		||||
                    target_dev,
 | 
			
		||||
                    source,
 | 
			
		||||
                    target_bus='ide',
 | 
			
		||||
                    disk_type='file',
 | 
			
		||||
                    disk_device='disk',
 | 
			
		||||
                    driver_name='qemu',
 | 
			
		||||
                    driver_type='raw',
 | 
			
		||||
                    readonly=False,
 | 
			
		||||
                    shareable=False,
 | 
			
		||||
                    serial=None,
 | 
			
		||||
                    cache_mode=None,
 | 
			
		||||
                    io_mode=None,
 | 
			
		||||
                    discard_mode=None,
 | 
			
		||||
                    detect_zeroes_mode=None):
 | 
			
		||||
 | 
			
		||||
        additionals = ''
 | 
			
		||||
        if cache_mode is not None and cache_mode != 'default' and disk_device != 'cdrom':
 | 
			
		||||
            additionals += f"cache='{cache_mode}' " 
 | 
			
		||||
            additionals += f"cache='{cache_mode}' "
 | 
			
		||||
        if io_mode is not None and io_mode != 'default':
 | 
			
		||||
            additionals += f"io='{io_mode}' "
 | 
			
		||||
        if discard_mode is not None and discard_mode != 'default':
 | 
			
		||||
| 
						 | 
				
			
			@ -691,7 +719,8 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
        if self.get_status() == 5:
 | 
			
		||||
            self.instance.detachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
 | 
			
		||||
 | 
			
		||||
    def edit_disk(self, target_dev, source, readonly, shareable, target_bus, serial, format, cache_mode, io_mode, discard_mode, detect_zeroes_mode):
 | 
			
		||||
    def edit_disk(self, target_dev, source, readonly, shareable, target_bus, serial, format, cache_mode, io_mode, discard_mode,
 | 
			
		||||
                  detect_zeroes_mode):
 | 
			
		||||
        tree = etree.fromstring(self._XMLDesc(0))
 | 
			
		||||
        disk_el = tree.xpath("./devices/disk/target[@dev='{}']".format(target_dev))[0].getparent()
 | 
			
		||||
        old_disk_type = disk_el.get('type')
 | 
			
		||||
| 
						 | 
				
			
			@ -735,7 +764,7 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
            time.sleep(1)
 | 
			
		||||
            cpu_use_now = self.instance.info()[4]
 | 
			
		||||
            diff_usage = cpu_use_now - cpu_use_ago
 | 
			
		||||
            cpu_usage['cpu'] = 100 * diff_usage / (1 * nbcore * 10 ** 9)
 | 
			
		||||
            cpu_usage['cpu'] = 100 * diff_usage / (1 * nbcore * 10**9)
 | 
			
		||||
        else:
 | 
			
		||||
            cpu_usage['cpu'] = 0
 | 
			
		||||
        return cpu_usage
 | 
			
		||||
| 
						 | 
				
			
			@ -928,8 +957,7 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
 | 
			
		||||
    def get_console_websocket_port(self):
 | 
			
		||||
        console_type = self.get_console_type()
 | 
			
		||||
        websocket_port = util.get_xml_path(self._XMLDesc(0),
 | 
			
		||||
                                           "/domain/devices/graphics[@type='%s']/@websocket" % console_type)
 | 
			
		||||
        websocket_port = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics[@type='%s']/@websocket" % console_type)
 | 
			
		||||
        return websocket_port
 | 
			
		||||
 | 
			
		||||
    def get_console_passwd(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -1124,7 +1152,7 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
            return mac
 | 
			
		||||
        # if mac does not contain ":", try to split into tuples and join with ":"
 | 
			
		||||
        n = 2
 | 
			
		||||
        mac_tuples = [mac[i:i+n] for i in range(0, len(mac), n)]
 | 
			
		||||
        mac_tuples = [mac[i:i + n] for i in range(0, len(mac), n)]
 | 
			
		||||
        return ':'.join(mac_tuples)
 | 
			
		||||
 | 
			
		||||
    def clone_instance(self, clone_data):
 | 
			
		||||
| 
						 | 
				
			
			@ -1215,7 +1243,7 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
 | 
			
		||||
                    stg = vol.storagePoolLookupByVolume()
 | 
			
		||||
                    stg.createXMLFrom(vol_clone_xml, vol, meta_prealloc)
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                source_protocol = elm.get('protocol')
 | 
			
		||||
                if source_protocol == 'rbd':
 | 
			
		||||
                    source_name = elm.get('name')
 | 
			
		||||
| 
						 | 
				
			
			@ -1241,13 +1269,13 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
                if source_dev:
 | 
			
		||||
                    clone_path = os.path.join(os.path.dirname(source_dev), target_file)
 | 
			
		||||
                    elm.set('dev', clone_path)
 | 
			
		||||
                    
 | 
			
		||||
 | 
			
		||||
                    vol = self.get_volume_by_path(source_dev)
 | 
			
		||||
                    stg = vol.storagePoolLookupByVolume()
 | 
			
		||||
                    
 | 
			
		||||
 | 
			
		||||
                    vol_name = util.get_xml_path(vol.XMLDesc(0), "/volume/name")
 | 
			
		||||
                    pool_name = util.get_xml_path(stg.XMLDesc(0), "/pool/name")
 | 
			
		||||
                    
 | 
			
		||||
 | 
			
		||||
                    storage = self.get_wvmStorage(pool_name)
 | 
			
		||||
                    storage.clone_volume(vol_name, target_file)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1388,7 +1416,7 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
                    tree.remove(option)
 | 
			
		||||
            else:
 | 
			
		||||
                if option is None:
 | 
			
		||||
                    option = etree.SubElement(tree , o)
 | 
			
		||||
                    option = etree.SubElement(tree, o)
 | 
			
		||||
                option.text = option_value
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
| 
						 | 
				
			
			@ -1421,7 +1449,13 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
                    in_peak = in_qos.get('peak')
 | 
			
		||||
                    in_burst = in_qos.get('burst')
 | 
			
		||||
                    in_floor = in_qos.get('floor')
 | 
			
		||||
                    bound_list.append({'direction': 'inbound', 'average': in_av, 'peak': in_peak, 'floor': in_floor, 'burst': in_burst})
 | 
			
		||||
                    bound_list.append({
 | 
			
		||||
                        'direction': 'inbound',
 | 
			
		||||
                        'average': in_av,
 | 
			
		||||
                        'peak': in_peak,
 | 
			
		||||
                        'floor': in_floor,
 | 
			
		||||
                        'burst': in_burst
 | 
			
		||||
                    })
 | 
			
		||||
 | 
			
		||||
                out_qos = band.find('outbound')
 | 
			
		||||
                if out_qos is not None:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										24
									
								
								webvirtcloud/middleware.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								webvirtcloud/middleware.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
import json
 | 
			
		||||
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.http import HttpResponse
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from libvirt import libvirtError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExceptionMiddleware:
 | 
			
		||||
    def __init__(self, get_response):
 | 
			
		||||
        self.get_response = get_response
 | 
			
		||||
 | 
			
		||||
    def __call__(self, request):
 | 
			
		||||
        return self.get_response(request)
 | 
			
		||||
 | 
			
		||||
    def process_exception(self, request, exception):
 | 
			
		||||
        if isinstance(exception, libvirtError):
 | 
			
		||||
            messages.error(
 | 
			
		||||
                request,
 | 
			
		||||
                _('libvirt Error - %(exception)s') % {'exception': exception},
 | 
			
		||||
            )
 | 
			
		||||
            return render(request, '500.html', status=500)
 | 
			
		||||
            # TODO: check connecting to host via VPN
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,6 @@ INSTALLED_APPS = [
 | 
			
		|||
    'appsettings',
 | 
			
		||||
    'computes',
 | 
			
		||||
    'console',
 | 
			
		||||
    'create',
 | 
			
		||||
    'datasource',
 | 
			
		||||
    'networks',
 | 
			
		||||
    'instances',
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +50,7 @@ MIDDLEWARE = [
 | 
			
		|||
    'django.contrib.messages.middleware.MessageMiddleware',
 | 
			
		||||
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
			
		||||
    'appsettings.middleware.AppSettingsMiddleware',
 | 
			
		||||
    'webvirtcloud.middleware.ExceptionMiddleware',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
ROOT_URLCONF = 'webvirtcloud.urls'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue