mirror of
				https://github.com/retspen/webvirtcloud
				synced 2025-07-31 12:41:08 +00:00 
			
		
		
		
	experimental: external snapshot fixes
This commit is contained in:
		
							parent
							
								
									68b0494350
								
							
						
					
					
						commit
						33d49a68a7
					
				
					 5 changed files with 156 additions and 207 deletions
				
			
		| 
						 | 
				
			
			@ -173,6 +173,10 @@ class Instance(models.Model):
 | 
			
		|||
    def snapshots(self):
 | 
			
		||||
        return sorted(self.proxy.get_snapshot(), reverse=True, key=lambda k: k["date"])
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def external_snapshots(self):
 | 
			
		||||
        return sorted(self.proxy.get_external_snapshots(), reverse=True, key=lambda k: k["date"])
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def inst_xml(self):
 | 
			
		||||
        return self.proxy._XMLDesc(VIR_DOMAIN_XML_SECURE)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -679,7 +679,7 @@
 | 
			
		|||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    if (~$.inArray(hash, ['#resize', "resizevm_cpu", "resizevm_mem", "resizevm_disk"])) {
 | 
			
		||||
    if (~$.inArray(hash, ['#resize', "#resizevm_cpu", "#resizevm_mem", "#resizevm_disk"])) {
 | 
			
		||||
        var btnsect = $('#navbtn>li>a');
 | 
			
		||||
        $(btnsect).each(function () {
 | 
			
		||||
            if ($(this).attr('href') === '#resize') {
 | 
			
		||||
| 
						 | 
				
			
			@ -702,7 +702,7 @@
 | 
			
		|||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    if (~$.inArray(hash, ['#takesnapshot', '#managesnapshot'])) {
 | 
			
		||||
    if (~$.inArray(hash, ['#takesnapshot', "#takeextsnapshot", "#managesnapshot"])) {
 | 
			
		||||
        var btnsect = $('#navbtn>li>a');
 | 
			
		||||
        $(btnsect).each(function () {
 | 
			
		||||
            if ($(this).attr('href') === '#snapshots') {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,12 @@
 | 
			
		|||
    <ul class="nav nav-tabs" role="tablist" aria-label="Instance snapshot menu">
 | 
			
		||||
        <li class="nav-item" role="presentation">
 | 
			
		||||
            <button class="nav-link active" data-bs-toggle="tab" data-bs-target="#takesnapshot" type="button" role="tab" aria-controls="takesnapshot" aria-selected="true">
 | 
			
		||||
                {% trans "Take Snapshot" %}
 | 
			
		||||
                {% trans "Take Snapshot - Internal" %}
 | 
			
		||||
            </button>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li class="nav-item" role="presentation">
 | 
			
		||||
            <button class="nav-link" data-bs-toggle="tab" data-bs-target="#takeextsnapshot" type="button" role="tab" aria-controls="externalSnapshot" aria-selected="false">
 | 
			
		||||
                {% trans "Take Snapshot - External" %}
 | 
			
		||||
            </button>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li class="nav-item" role="presentation">
 | 
			
		||||
| 
						 | 
				
			
			@ -13,16 +18,6 @@
 | 
			
		|||
                {% trans "Manage Snapshots" %}
 | 
			
		||||
            </button>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li class="nav-item" role="presentation">
 | 
			
		||||
            <button class="nav-link" data-bs-toggle="tab" data-bs-target="#externalSnapshot" type="button" role="tab" aria-controls="externalSnapshot" aria-selected="false">
 | 
			
		||||
                External Snapshot
 | 
			
		||||
            </button>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li class="nav-item" role="presentation">
 | 
			
		||||
            <button class="nav-link" data-bs-toggle="tab" data-bs-target="#manageExternalSnapshots" type="button" role="tab" aria-controls="manageExternalSnapshots" aria-selected="false">
 | 
			
		||||
                Manage External Snapshots
 | 
			
		||||
            </button>
 | 
			
		||||
        </li>
 | 
			
		||||
    </ul>
 | 
			
		||||
    <!-- Tab panes -->
 | 
			
		||||
    <div class="tab-content">
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +34,7 @@
 | 
			
		|||
                        <input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Snapshot Name" %}" maxlength="14">
 | 
			
		||||
                        <span class="input-group-text">|</span>
 | 
			
		||||
                        <input type="text" class="form-control form-control-lg" name="description" placeholder="{% trans "Snapshot Description" %}" maxlength="45">
 | 
			
		||||
                        {% if external_snapshots|length > 0 %}
 | 
			
		||||
                        {% if instance.external_snapshots|length > 0 %}
 | 
			
		||||
                            <input type="submit" class="btn btn-lg btn-success disabled float-end" name="snapshot" value="{% trans "Take Snapshot" %}">
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            <input type="submit" class="btn btn-lg btn-success float-end" name="snapshot" value="{% trans "Take Snapshot" %}" onclick="showPleaseWaitDialog();">
 | 
			
		||||
| 
						 | 
				
			
			@ -47,137 +42,109 @@
 | 
			
		|||
                    </div>
 | 
			
		||||
            </form>
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        </div> <!-- Tab pane - takesnapshot -->
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="takeextsnapshot">
 | 
			
		||||
            {% if instance.status != 5 %}
 | 
			
		||||
            <p>{% trans "You can get external snapshots within this tab." %}</p>
 | 
			
		||||
            <p class="text-primary">{% trans "External snapshots are experimental in this stage, use it if you know what you are doing. It may require manual intervention." %}</p>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <p>{% trans "Create an external snapshot" %}</p>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <p class="text-danger">{% trans "Give your External Snapshot a <b>distinctive description</b> so it wouldn't get mixed with other snapshots." %}</p>
 | 
			
		||||
            <form action="{% url 'instances:create_external_snapshot' instance.id %}" method="post" role="form" aria-label="Create snapshot form">
 | 
			
		||||
                {% csrf_token %}
 | 
			
		||||
                <div class="input-group mb-3">
 | 
			
		||||
                    <input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Snapshot Name" %}" maxlength="14">
 | 
			
		||||
                    <span class="input-group-text">|</span>
 | 
			
		||||
                    <input type="text" class="form-control form-control-lg" name="description" placeholder="{% trans "Snapshot Description" %}" maxlength="45">
 | 
			
		||||
                    {% if instance.external_snapshots|length > 0 or instance.snapshots|length > 0 %}
 | 
			
		||||
                        <input type="submit" class="btn btn-lg btn-success disabled float-end" name="snapshot" value="{% trans "Take Snapshot" %}">
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <input type="submit" class="btn btn-lg btn-success float-end" name="snapshot" value="{% trans "Take Snapshot" %}" onclick="showPleaseWaitDialog();">
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </div>
 | 
			
		||||
                <p class="text-danger">{% trans "WebVirtCloud supports only one external snapshot at the moment." %}</p>
 | 
			
		||||
            </form>
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div> <!--tab pane takeextsnapshot-->
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="managesnapshot">
 | 
			
		||||
            {% if instance.snapshots %}
 | 
			
		||||
            {% if instance.snapshots or instance.external_snapshots %}
 | 
			
		||||
                <p>{% trans "Choose a snapshot for restore/delete" %}</p>
 | 
			
		||||
                <div class="table-responsive">
 | 
			
		||||
                    <table class="table">
 | 
			
		||||
                        <thead>
 | 
			
		||||
                            <th scope="col">{% trans "Date" %}</th>
 | 
			
		||||
                            <th scope="col">{% trans "Name" %}</th>
 | 
			
		||||
                            <th scope="col">{% trans "Description" %}</th>
 | 
			
		||||
                            <th scope="col">{% trans "Type - Description" %}</th>
 | 
			
		||||
                            <th scope="colgroup" colspan="2">{% trans "Action" %}</th>
 | 
			
		||||
                        </thead>
 | 
			
		||||
                        <tbody>
 | 
			
		||||
                        {% for snap in instance.snapshots %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>{{ snap.date|date:"M d H:i:s" }}</td>
 | 
			
		||||
                                <td><strong>{{ snap.name }}</strong></td>
 | 
			
		||||
                                <td>{{ snap.description }}</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-primary" 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-primary 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 %}
 | 
			
		||||
                            {% if instance.snapshots %}
 | 
			
		||||
                                {% for snap in instance.snapshots %}
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>{{ snap.date|date:"M d H:i:s" }}</td>
 | 
			
		||||
                                        <td><strong>{{ snap.name }}</strong></td>
 | 
			
		||||
                                        <td>({% trans "Internal" %}) - {{ snap.description }}</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-primary" 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-primary 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 %}
 | 
			
		||||
                            {% elif instance.external_snapshots %}
 | 
			
		||||
                                {% for ext_snap in instance.external_snapshots %}
 | 
			
		||||
                                    <tr>
 | 
			
		||||
                                        <td>{{ ext_snap.date|date:"M d H:i:s" }}</td>
 | 
			
		||||
                                        <td><strong>{{ ext_snap.name }}</strong></td>
 | 
			
		||||
                                        <td>({% trans "External" %}) - {{ ext_snap.description }}</td>
 | 
			
		||||
                                        <td style="width:30px;">
 | 
			
		||||
                                            <form action="{% url 'instances:revert_external_snapshot' instance.id %}" method="post" role="form" aria-label="Restore external snapshot form">
 | 
			
		||||
                                                {% csrf_token %}
 | 
			
		||||
                                                <input type="hidden" name="name" value="{{ ext_snap.name }}">
 | 
			
		||||
                                                <input type="hidden" name="date" value="{{ ext_snap.date }}">
 | 
			
		||||
                                                <input type="hidden" name="desc" value="{{ ext_snap.description }}">
 | 
			
		||||
                                                <button type="submit" class="btn btn-sm btn-primary" name="revert_external_snapshot" title="{% trans 'Revert to this Snapshot' %}" onclick="return confirm('You are going to lose your unsaved work by reverting to this snapshot state. Are you sure?')">
 | 
			
		||||
                                                        <span class="fa fa-download"></span>
 | 
			
		||||
                                                </button>
 | 
			
		||||
                                            </form>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                        <td style="width:30px;">
 | 
			
		||||
                                            <form action="{% url 'instances:delete_external_snapshot' instance.id %}" method="post" role="form" aria-label="Delete external snapshot form">{% csrf_token %}
 | 
			
		||||
                                                <input type="hidden" name="name" value="{{ ext_snap.name }}">
 | 
			
		||||
                                                <button type="submit" class="btn btn-sm btn-danger" name="delete_external_snapshot" title="{% trans 'Delete Snapshot' %}" onclick="return confirm('You are about to delete this snapshot and merge it with base image. Are you sure?')">
 | 
			
		||||
                                                    {% icon 'trash' %}
 | 
			
		||||
                                                </button>
 | 
			
		||||
                                            </form>
 | 
			
		||||
                                        </td>
 | 
			
		||||
                                    </tr>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </tbody>
 | 
			
		||||
                    </table>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <p>{% trans "You do not have any snapshots" %}</p>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="externalSnapshot">
 | 
			
		||||
            {% if instance.status != 5 %}
 | 
			
		||||
            <p>You can get external snapshots within this tab.</p>
 | 
			
		||||
            <p class="text-primary">External snapshots are experimental in this stage, use it if you know what you are doing.</p>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <p>Create an external snapshot</p>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <p class="text-danger">Give your External Snapshot a <b>distinctive description</b> so it wouldn't get mixed with other snapshots.</p>
 | 
			
		||||
            <form action="{% url 'instances:create_external_snapshot' instance.id %}" method="post" role="form" aria-label="Create snapshot form">
 | 
			
		||||
                {% csrf_token %}
 | 
			
		||||
                <div class="mb-3" style="white-space:pre-line">
 | 
			
		||||
                    <input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Snapshot Name" %}" maxlength="14">
 | 
			
		||||
                    <input type="text" class="form-control form-control-lg" name="description" placeholder="{% trans "Snapshot Description" %}" maxlength="45">
 | 
			
		||||
                    {% if external_snapshots|length > 0 or instance.snapshots|length > 0 %}
 | 
			
		||||
                        <p class="text-danger">WebVirtCloud supports only one External Snapshot at the moment.</p>
 | 
			
		||||
                        <input type="submit" class="btn btn-lg btn-success disabled float-end" name="snapshot" value="{% trans "Take Snapshot" %}">
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <input type="submit" class="btn btn-lg btn-success float-end" name="snapshot" value="{% trans "Take Snapshot" %}" onclick="showPleaseWaitDialog();">
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    
 | 
			
		||||
                </div>
 | 
			
		||||
            </form>
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div role="tabpanel" class="tab-pane tab-pane-bordered" id="manageExternalSnapshots">
 | 
			
		||||
            {% if external_snapshots %}
 | 
			
		||||
            <div class="table-responsive">
 | 
			
		||||
                <table class="table">
 | 
			
		||||
                    <thead>
 | 
			
		||||
                        <th scope="col">Name</th>
 | 
			
		||||
                        <th scope="col">Date</th>
 | 
			
		||||
                        <th scope="col">Description</th>
 | 
			
		||||
                        <th scope="colgroup" colspan="2">{% trans "Action" %}</th>
 | 
			
		||||
                    </thead>
 | 
			
		||||
                    <tbody>
 | 
			
		||||
                        {% for external_snapshot in external_snapshots %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                            {% for snapshot_cols in external_snapshot %}
 | 
			
		||||
                                <td>{{snapshot_cols}}</td>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                            <td style="width:30px;">
 | 
			
		||||
                                <form action="{% url 'instances:revert_external_snapshot' instance.id %}" method="post" role="form" aria-label="Restore external snapshot form">
 | 
			
		||||
                                    {% csrf_token %}
 | 
			
		||||
                                    <input type="hidden" name="name" value="{{ external_snapshot.0 }}">
 | 
			
		||||
                                    <input type="hidden" name="date" value="{{ external_snapshot.1 }}">
 | 
			
		||||
                                    <input type="hidden" name="desc" value="{{ external_snapshot.2 }}">
 | 
			
		||||
                            {% if instance.status == 5 %}
 | 
			
		||||
                                    <button type="submit" class="btn btn-sm btn-primary" name="revert_external_snapshot" title="{% trans 'Revert to this Snapshot' %}" onclick="return confirm('You are going to lose your unsaved work by reverting to this snapshot state. Are you sure?')">
 | 
			
		||||
                                            <span class="fa fa-download"></span>
 | 
			
		||||
                                    </button>
 | 
			
		||||
                            {% else %}
 | 
			
		||||
                                    <button type="button" class="btn btn-sm btn-primary 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_external_snapshot' instance.id %}" method="post" role="form" aria-label="Delete external snapshot form">{% csrf_token %}
 | 
			
		||||
                                    <input type="hidden" name="name" value="{{ external_snapshot.0 }}">
 | 
			
		||||
                                    {% if instance.status != 5 %}
 | 
			
		||||
                                    <button type="submit" class="btn btn-sm btn-danger" name="delete_external_snapshot" title="{% trans 'Delete Snapshot' %}" onclick="return confirm('You are about to delete this snapshot and merge it with base image. Are you sure?')">
 | 
			
		||||
                                        {% icon 'trash' %}
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                    {% else %}
 | 
			
		||||
                                    <button type="submit" class="btn btn-sm btn-danger disabled" title="{% trans 'Delete Snapshot' %}">
 | 
			
		||||
                                        {% icon 'trash' %}
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                </form>
 | 
			
		||||
                            </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </tbody>
 | 
			
		||||
                </table>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% else%}
 | 
			
		||||
                    <p>{% trans "You do not have any snapshots" %}</p>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
        </div> <!--tab pane managesnapshot-->
 | 
			
		||||
    </div> <!-- Tab content-->
 | 
			
		||||
</div> <!-- Tab panel-->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,9 @@ from django.http import Http404, HttpResponse, JsonResponse
 | 
			
		|||
from django.shortcuts import get_object_or_404, redirect, render
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from libvirt import (VIR_DOMAIN_UNDEFINE_KEEP_NVRAM, VIR_DOMAIN_UNDEFINE_NVRAM,
 | 
			
		||||
from libvirt import (VIR_DOMAIN_UNDEFINE_KEEP_NVRAM,
 | 
			
		||||
                     VIR_DOMAIN_UNDEFINE_NVRAM,
 | 
			
		||||
                     VIR_DOMAIN_START_PAUSED,
 | 
			
		||||
                     libvirtError)
 | 
			
		||||
from logs.views import addlogmsg
 | 
			
		||||
from vrtManager import util
 | 
			
		||||
| 
						 | 
				
			
			@ -146,8 +148,6 @@ def instance(request, pk):
 | 
			
		|||
        instance.drbd = drbd_status(request, pk)
 | 
			
		||||
        instance.save()
 | 
			
		||||
 | 
			
		||||
    external_snapshots = get_external_snapshots(request,pk)
 | 
			
		||||
 | 
			
		||||
    return render(request, "instance.html", locals(),)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1016,6 +1016,7 @@ def revert_snapshot(request, pk):
 | 
			
		|||
        addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
 | 
			
		||||
    return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_external_snapshot(request, pk):
 | 
			
		||||
    instance = get_instance(request.user, pk)
 | 
			
		||||
    allow_admin_or_not_template = (
 | 
			
		||||
| 
						 | 
				
			
			@ -1027,10 +1028,11 @@ def create_external_snapshot(request, pk):
 | 
			
		|||
    ):
 | 
			
		||||
        name = request.POST.get("name", "")
 | 
			
		||||
        desc = request.POST.get("description", "")
 | 
			
		||||
        instance.proxy.create_external_snapshot(name, instance, desc=desc)
 | 
			
		||||
        instance.proxy.create_external_snapshot("s1." + name, instance, desc=desc)
 | 
			
		||||
        msg = _("Create external snapshot: %(snap)s") % {"snap": name}
 | 
			
		||||
        addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
 | 
			
		||||
    return redirect(request.META.get("HTTP_REFERER") + "#manageExternalSnapshots")
 | 
			
		||||
    return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_external_snapshots(request, pk):
 | 
			
		||||
    instance = get_instance(request.user, pk)
 | 
			
		||||
| 
						 | 
				
			
			@ -1044,6 +1046,7 @@ def get_external_snapshots(request, pk):
 | 
			
		|||
        external_snapshots = instance.proxy.get_external_snapshots()
 | 
			
		||||
    return external_snapshots
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def revert_external_snapshot(request, pk):
 | 
			
		||||
    instance = get_instance(request.user, pk)
 | 
			
		||||
    allow_admin_or_not_template = (
 | 
			
		||||
| 
						 | 
				
			
			@ -1059,10 +1062,12 @@ def revert_external_snapshot(request, pk):
 | 
			
		|||
        instance.proxy.revert_external_snapshot(name, instance, date, desc)
 | 
			
		||||
        msg = _("Revert external snapshot: %(snap)s") % {"snap": name}
 | 
			
		||||
        addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
 | 
			
		||||
    return redirect(request.META.get("HTTP_REFERER") + "#manageExternalSnapshots")
 | 
			
		||||
    return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delete_external_snapshot(request, pk):
 | 
			
		||||
    instance = get_instance(request.user, pk)
 | 
			
		||||
    instance_state = True if instance.proxy.get_status() == 5 else False
 | 
			
		||||
    allow_admin_or_not_template = (
 | 
			
		||||
        request.user.is_superuser or request.user.is_staff or not instance.is_template
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			@ -1071,10 +1076,18 @@ def delete_external_snapshot(request, pk):
 | 
			
		|||
        "instances.snapshot_instances"
 | 
			
		||||
    ):
 | 
			
		||||
        name = request.POST.get("name", "")
 | 
			
		||||
        instance.proxy.delete_external_snapshot(name, instance)
 | 
			
		||||
        msg = _("Delete external snapshot: %(snap)s") % {"snap": name}
 | 
			
		||||
        addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
 | 
			
		||||
    return redirect(request.META.get("HTTP_REFERER") + "#manageExternalSnapshots")
 | 
			
		||||
 | 
			
		||||
        instance.proxy.start(VIR_DOMAIN_START_PAUSED) if instance_state else None
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            instance.proxy.delete_external_snapshot(name)
 | 
			
		||||
            msg = _("Delete external snapshot: %(snap)s") % {"snap": name}
 | 
			
		||||
            addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
 | 
			
		||||
        finally:
 | 
			
		||||
            instance.proxy.force_shutdown() if instance_state else None
 | 
			
		||||
 | 
			
		||||
    return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@superuser_only
 | 
			
		||||
def set_vcpu(request, pk):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -209,8 +209,8 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
 | 
			
		||||
        return info_results
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        self.instance.create()
 | 
			
		||||
    def start(self, flags=0):
 | 
			
		||||
        self.instance.createWithFlags(flags)
 | 
			
		||||
 | 
			
		||||
    def shutdown(self):
 | 
			
		||||
        self.instance.shutdown()
 | 
			
		||||
| 
						 | 
				
			
			@ -1295,24 +1295,22 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
        )
 | 
			
		||||
        self._defineXML(xml_temp)
 | 
			
		||||
 | 
			
		||||
    def create_external_snapshot(self, name, instance, date=None, desc=None):
 | 
			
		||||
        if self.instance.isActive() == False:
 | 
			
		||||
            result = self.instance.create()
 | 
			
		||||
            if result < 0:
 | 
			
		||||
                return 0
 | 
			
		||||
        
 | 
			
		||||
    def create_external_snapshot(self, name, date=None, desc=None):
 | 
			
		||||
        creation_time = time.time()
 | 
			
		||||
        state = "shutoff" if self.get_status() == 5 else "running"
 | 
			
		||||
        xml = """<domainsnapshot>
 | 
			
		||||
                     <name>%s</name>
 | 
			
		||||
                     <description>%s</description>
 | 
			
		||||
                     <state>%s</state>
 | 
			
		||||
                     <creationTime>%d</creationTime>""" % (
 | 
			
		||||
                     <creationTime>%d</creationTime>
 | 
			
		||||
                     <seclabel type='none' model='dac' relabel='no'/>
 | 
			
		||||
                     """ % (
 | 
			
		||||
            name,
 | 
			
		||||
            desc,
 | 
			
		||||
            state,
 | 
			
		||||
            creation_time,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.change_snapshot_xml()
 | 
			
		||||
        xml += self._XMLDesc(VIR_DOMAIN_XML_SECURE)
 | 
			
		||||
        xml += """<active>0</active>
 | 
			
		||||
| 
						 | 
				
			
			@ -1320,87 +1318,54 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
 | 
			
		||||
        self._snapshotCreateXML(xml, VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY)
 | 
			
		||||
 | 
			
		||||
        disk_info = self.get_disk_devices()
 | 
			
		||||
        for disk in disk_info:
 | 
			
		||||
            backing_file = disk["backing_file"]
 | 
			
		||||
            vol_base = self.get_volume_by_path(backing_file)
 | 
			
		||||
            pool = vol_base.storagePoolLookupByVolume()
 | 
			
		||||
            pool.refresh(0)
 | 
			
		||||
    
 | 
			
		||||
    def get_external_snapshots(self):
 | 
			
		||||
        external_snapshots = []
 | 
			
		||||
        temp_snapshots = self.get_snapshot(VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL)
 | 
			
		||||
        for temp_snapshot in temp_snapshots:
 | 
			
		||||
            external_snapshot = []
 | 
			
		||||
            external_snapshot.append(temp_snapshot['name'])
 | 
			
		||||
            external_snapshot.append(temp_snapshot['date'])
 | 
			
		||||
            external_snapshot.append(temp_snapshot['description'])
 | 
			
		||||
            external_snapshots.append(external_snapshot)
 | 
			
		||||
        return external_snapshots
 | 
			
		||||
        return self.get_snapshot(VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL)
 | 
			
		||||
    
 | 
			
		||||
    def delete_external_snapshot(self, name, instance):
 | 
			
		||||
    def delete_external_snapshot(self, name):
 | 
			
		||||
        disk_info = self.get_disk_devices()
 | 
			
		||||
        for disk in disk_info:
 | 
			
		||||
            target_dev = disk["dev"]
 | 
			
		||||
            backing_file = disk["backing_file"]
 | 
			
		||||
            source_file = disk["path"]
 | 
			
		||||
            self.instance.blockCommit(target_dev, backing_file, source_file, 
 | 
			
		||||
                                                  flags=VIR_DOMAIN_BLOCK_COMMIT_DELETE|VIR_DOMAIN_BLOCK_COMMIT_ACTIVE)
 | 
			
		||||
            snap_source_file = disk["path"]
 | 
			
		||||
            self.instance.blockCommit(target_dev, backing_file, snap_source_file,
 | 
			
		||||
                                                  flags=VIR_DOMAIN_BLOCK_COMMIT_DELETE|
 | 
			
		||||
                                                  VIR_DOMAIN_BLOCK_COMMIT_ACTIVE)
 | 
			
		||||
            while True:
 | 
			
		||||
                info = self.instance.blockJobInfo(target_dev, 0)
 | 
			
		||||
                if info.get('cur') == info.get('end'):
 | 
			
		||||
                    self.instance.blockJobAbort(target_dev,flags=VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT)
 | 
			
		||||
                    break
 | 
			
		||||
            # Check again pool for snapshot delta volume; if it exist, remove it manually
 | 
			
		||||
            with contextlib.suppress(libvirtError):
 | 
			
		||||
                vol_snap = self.get_volume_by_path(snap_source_file)
 | 
			
		||||
                pool = vol_snap.storagePoolLookupByVolume()
 | 
			
		||||
                pool.refresh(0)
 | 
			
		||||
                vol_snap.delete(0)
 | 
			
		||||
 | 
			
		||||
        snap = self.instance.snapshotLookupByName(name, 0)
 | 
			
		||||
        snap.delete(VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY)
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    def revert_external_snapshot(self, name, instance, date, desc):
 | 
			
		||||
    def revert_external_snapshot(self, name, date, desc):
 | 
			
		||||
        snap = self.instance.snapshotLookupByName(name, 0)
 | 
			
		||||
        snapXML = ElementTree.fromstring(snap.getXMLDesc(0))
 | 
			
		||||
        disks = []
 | 
			
		||||
        for disk_backup in snapXML.findall('inactiveDomain/devices/disk'):
 | 
			
		||||
            if disk_backup.get('device') == 'disk':
 | 
			
		||||
                disk_dict = {}
 | 
			
		||||
                if disk_backup.find('source') is not None:
 | 
			
		||||
                    disk_dict['backing_file'] = disk_backup.find('source').get('file')
 | 
			
		||||
                if disk_backup.find('driver') is not None:
 | 
			
		||||
                    disk_dict['driver_name'] = disk_backup.find('driver').get('name')
 | 
			
		||||
                    disk_dict['driver_type'] = disk_backup.find('driver').get('type')
 | 
			
		||||
                if disk_backup.find('target') is not None:
 | 
			
		||||
                    disk_dict['target_dev'] = disk_backup.find('target').get('dev')
 | 
			
		||||
                    disk_dict['target_bus'] = disk_backup.find('target').get('bus')
 | 
			
		||||
                if disk_backup.find('boot') is not None:
 | 
			
		||||
                    disk_dict['boot_order'] = disk_backup.find('boot').get('order')     
 | 
			
		||||
                disks.append(disk_dict)
 | 
			
		||||
        snap_xml = snap.getXMLDesc(0)
 | 
			
		||||
        snapXML = ElementTree.fromstring(snap_xml)
 | 
			
		||||
        disks = snapXML.findall('inactiveDomain/devices/disk')
 | 
			
		||||
        if not disks: disks = snapXML.findall('domain/devices/disk')
 | 
			
		||||
        
 | 
			
		||||
        snap.delete(VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY)
 | 
			
		||||
        disk_info = self.get_disk_devices()
 | 
			
		||||
        for disk in disk_info:
 | 
			
		||||
            source_file = disk["path"]
 | 
			
		||||
            backing_file = disk["backing_file"]
 | 
			
		||||
            vol_base = self.get_volume_by_path(backing_file)
 | 
			
		||||
            pool = vol_base.storagePoolLookupByVolume()
 | 
			
		||||
            vol_snap = self.get_volume_by_path(disk["path"])
 | 
			
		||||
            pool = vol_snap.storagePoolLookupByVolume()
 | 
			
		||||
            pool.refresh(0)
 | 
			
		||||
            vol_snap = self.get_volume_by_path(source_file)
 | 
			
		||||
            vol_snap.wipe(0)
 | 
			
		||||
            vol_snap.delete(0)
 | 
			
		||||
 | 
			
		||||
        snap.delete(VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY)
 | 
			
		||||
        for disk in disks:
 | 
			
		||||
            self.instance.updateDeviceFlags(
 | 
			
		||||
                """<disk type='file' device='disk'>
 | 
			
		||||
                    <driver name='{}' type='{}'/>
 | 
			
		||||
                    <source file='{}'/>
 | 
			
		||||
                    <target dev='{}' bus='{}'/>
 | 
			
		||||
                    <boot order='{}'/>
 | 
			
		||||
                    </disk>""".format(disk["driver_name"],
 | 
			
		||||
                               disk["driver_type"],
 | 
			
		||||
                               disk["backing_file"],
 | 
			
		||||
                               disk["target_dev"],
 | 
			
		||||
                               disk["target_bus"],
 | 
			
		||||
                               disk["boot_order"]))
 | 
			
		||||
            self.instance.updateDeviceFlags(ElementTree.tostring(disk).decode("UTF-8"))
 | 
			
		||||
 | 
			
		||||
        self.create_external_snapshot(name, instance, date, desc)
 | 
			
		||||
        self.create_external_snapshot(name, date, desc)
 | 
			
		||||
 | 
			
		||||
    def get_snapshot(self, flag=VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL):
 | 
			
		||||
        snapshots = []
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue