mirror of
https://github.com/retspen/webvirtcloud
synced 2025-01-12 08:25:18 +00:00
More instances tests
This commit is contained in:
parent
9e56378682
commit
0f66187e80
7 changed files with 769 additions and 390 deletions
|
@ -1,7 +1,6 @@
|
||||||
<domain type="kvm">
|
<domain type="kvm">
|
||||||
<name>test-vm</name>
|
<name>test-vm</name>
|
||||||
<uuid>1bd3c1f2-dd12-4b8d-a298-dff387cb9f93</uuid>
|
<uuid>1bd3c1f2-dd12-4b8d-a298-dff387cb9f93</uuid>
|
||||||
<description>None</description>
|
|
||||||
<memory unit="KiB">131072</memory>
|
<memory unit="KiB">131072</memory>
|
||||||
<currentMemory unit="KiB">131072</currentMemory>
|
<currentMemory unit="KiB">131072</currentMemory>
|
||||||
<vcpu placement="static">1</vcpu>
|
<vcpu placement="static">1</vcpu>
|
||||||
|
|
|
@ -6,9 +6,7 @@ def migrate_can_clone_instances(sender, **kwargs):
|
||||||
'''
|
'''
|
||||||
Migrate can clone instances user attribute to permission
|
Migrate can clone instances user attribute to permission
|
||||||
'''
|
'''
|
||||||
from django.conf import settings
|
from django.contrib.auth.models import Permission, User
|
||||||
from django.contrib.auth.models import User, Permission
|
|
||||||
from accounts.models import UserAttributes
|
|
||||||
|
|
||||||
plan = kwargs['plan']
|
plan = kwargs['plan']
|
||||||
for migration, rolled_back in plan:
|
for migration, rolled_back in plan:
|
||||||
|
@ -22,22 +20,23 @@ def migrate_can_clone_instances(sender, **kwargs):
|
||||||
user.user_permissions.add(permission)
|
user.user_permissions.add(permission)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def apply_passwordless_console(sender, **kwargs):
|
def apply_passwordless_console(sender, **kwargs):
|
||||||
'''
|
'''
|
||||||
Apply new passwordless_console permission for all users
|
Apply new passwordless_console permission for all users
|
||||||
'''
|
'''
|
||||||
from django.conf import settings
|
from django.contrib.auth.models import Permission, User
|
||||||
from django.contrib.auth.models import User, Permission
|
|
||||||
|
|
||||||
print('\033[92mApplying permission passwordless_console for all users\033[0m')
|
print('\033[92mApplying permission passwordless_console for all users\033[0m')
|
||||||
users = User.objects.all()
|
users = User.objects.all()
|
||||||
permission = Permission.objects.get(codename='passwordless_console')
|
permission = Permission.objects.get(codename='passwordless_console')
|
||||||
for user in users:
|
for user in users:
|
||||||
user.user_permissions.add(permission)
|
user.user_permissions.add(permission)
|
||||||
|
|
||||||
|
|
||||||
class InstancesConfig(AppConfig):
|
class InstancesConfig(AppConfig):
|
||||||
name = 'instances'
|
name = 'instances'
|
||||||
verbose_name = 'instances'
|
verbose_name = 'Instances'
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
post_migrate.connect(migrate_can_clone_instances, sender=self)
|
post_migrate.connect(migrate_can_clone_instances, sender=self)
|
||||||
|
|
|
@ -61,6 +61,4 @@ class NewVMForm(forms.Form):
|
||||||
have_symbol = re.match('^[a-zA-Z0-9._-]+$', name)
|
have_symbol = re.match('^[a-zA-Z0-9._-]+$', name)
|
||||||
if not have_symbol:
|
if not have_symbol:
|
||||||
raise forms.ValidationError(_('The name of the virtual machine must not contain any special characters'))
|
raise forms.ValidationError(_('The name of the virtual machine must not contain any special characters'))
|
||||||
elif len(name) > 64:
|
|
||||||
raise forms.ValidationError(_('The name of the virtual machine must not exceed 20 characters'))
|
|
||||||
return name
|
return name
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -65,5 +65,5 @@ urlpatterns = [
|
||||||
path('guess_clone_name/', views.guess_clone_name, name='guess_clone_name'),
|
path('guess_clone_name/', views.guess_clone_name, name='guess_clone_name'),
|
||||||
path('random_mac_address/', views.random_mac_address, name='random_mac_address'),
|
path('random_mac_address/', views.random_mac_address, name='random_mac_address'),
|
||||||
path('check_instance/<vname>/', views.check_instance, name='check_instance'),
|
path('check_instance/<vname>/', views.check_instance, name='check_instance'),
|
||||||
path('sshkeys/<vname>/', views.sshkeys, name='sshkeys'),
|
path('<int:pk>/sshkeys/', views.sshkeys, name='sshkeys'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,40 +1,17 @@
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from accounts.models import UserInstance
|
from accounts.models import UserInstance
|
||||||
from appsettings.settings import app_settings
|
from appsettings.settings import app_settings
|
||||||
from logs.views import addlogmsg
|
from django.conf import settings
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
from vrtManager.connection import connection_manager
|
from vrtManager.connection import connection_manager
|
||||||
from vrtManager.hostdetails import wvmHostDetails
|
|
||||||
from vrtManager.instance import wvmInstance, wvmInstances
|
from vrtManager.instance import wvmInstance, wvmInstances
|
||||||
|
|
||||||
from .models import Instance
|
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):
|
def get_clone_free_names(size=10):
|
||||||
prefix = app_settings.CLONE_INSTANCE_DEFAULT_PREFIX
|
prefix = app_settings.CLONE_INSTANCE_DEFAULT_PREFIX
|
||||||
free_names = []
|
free_names = []
|
||||||
|
@ -186,36 +163,6 @@ def migrate_instance(
|
||||||
instance.save()
|
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):
|
def refr(compute):
|
||||||
if compute.status is True:
|
if compute.status is True:
|
||||||
domains = compute.proxy.wvm.listAllDomains()
|
domains = compute.proxy.wvm.listAllDomains()
|
||||||
|
@ -229,83 +176,6 @@ def refr(compute):
|
||||||
Instance(compute=compute, name=domain.name(), uuid=domain.UUIDString()).save()
|
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):
|
def get_dhcp_mac_address(vname):
|
||||||
dhcp_file = settings.BASE_DIR + '/dhcpd.conf'
|
dhcp_file = settings.BASE_DIR + '/dhcpd.conf'
|
||||||
mac = ''
|
mac = ''
|
||||||
|
@ -342,20 +212,3 @@ def get_clone_disk_name(disk, prefix, clone_name=''):
|
||||||
else:
|
else:
|
||||||
image = f"{disk['image']}-clone"
|
image = f"{disk['image']}-clone"
|
||||||
return image
|
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
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ def index(request):
|
||||||
|
|
||||||
|
|
||||||
def instance(request, pk):
|
def instance(request, pk):
|
||||||
instance: Instance = get_object_or_404(Instance, pk=pk)
|
instance: Instance = get_instance(request.user, pk)
|
||||||
compute: Compute = instance.compute
|
compute: Compute = instance.compute
|
||||||
computes = Compute.objects.all().order_by('name')
|
computes = Compute.objects.all().order_by('name')
|
||||||
computes_count = computes.count()
|
computes_count = computes.count()
|
||||||
|
@ -59,7 +59,6 @@ def instance(request, pk):
|
||||||
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
|
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
|
||||||
keymaps = settings.QEMU_KEYMAPS
|
keymaps = settings.QEMU_KEYMAPS
|
||||||
console_types = AppSettings.objects.get(key="QEMU_CONSOLE_DEFAULT_TYPE").choices_as_list()
|
console_types = AppSettings.objects.get(key="QEMU_CONSOLE_DEFAULT_TYPE").choices_as_list()
|
||||||
#
|
|
||||||
console_form = ConsoleForm(
|
console_form = ConsoleForm(
|
||||||
initial={
|
initial={
|
||||||
'type': instance.console_type,
|
'type': instance.console_type,
|
||||||
|
@ -67,7 +66,6 @@ def instance(request, pk):
|
||||||
'password': instance.console_passwd,
|
'password': instance.console_passwd,
|
||||||
'keymap': instance.console_keymap,
|
'keymap': instance.console_keymap,
|
||||||
})
|
})
|
||||||
#
|
|
||||||
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
|
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||||
bottom_bar = app_settings.VIEW_INSTANCE_DETAIL_BOTTOM_BAR
|
bottom_bar = app_settings.VIEW_INSTANCE_DETAIL_BOTTOM_BAR
|
||||||
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
||||||
|
@ -78,15 +76,6 @@ def instance(request, pk):
|
||||||
except UserInstance.DoesNotExist:
|
except UserInstance.DoesNotExist:
|
||||||
userinstance = None
|
userinstance = None
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
if not userinstance:
|
|
||||||
return redirect(reverse('index'))
|
|
||||||
|
|
||||||
if len(instance.media) != 0:
|
|
||||||
media_iso = sorted(instance.proxy.get_iso_media())
|
|
||||||
else:
|
|
||||||
media_iso = []
|
|
||||||
|
|
||||||
memory_range = [256, 512, 768, 1024, 2048, 3072, 4096, 6144, 8192, 16384]
|
memory_range = [256, 512, 768, 1024, 2048, 3072, 4096, 6144, 8192, 16384]
|
||||||
if instance.memory not in memory_range:
|
if instance.memory not in memory_range:
|
||||||
insort(memory_range, instance.memory)
|
insort(memory_range, instance.memory)
|
||||||
|
@ -127,18 +116,11 @@ def instance(request, pk):
|
||||||
vcpu_host = len(instance.vcpu_range)
|
vcpu_host = len(instance.vcpu_range)
|
||||||
memory_host = instance.proxy.get_max_memory()
|
memory_host = instance.proxy.get_max_memory()
|
||||||
bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine)
|
bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine)
|
||||||
# videos_host = instance.proxy.get_video_models(instance.arch, instance.machine)
|
|
||||||
networks_host = sorted(instance.proxy.get_networks())
|
networks_host = sorted(instance.proxy.get_networks())
|
||||||
nwfilters_host = instance.proxy.get_nwfilters()
|
nwfilters_host = instance.proxy.get_nwfilters()
|
||||||
storages_host = sorted(instance.proxy.get_storages(True))
|
storages_host = sorted(instance.proxy.get_storages(True))
|
||||||
net_models_host = instance.proxy.get_network_models()
|
net_models_host = instance.proxy.get_network_models()
|
||||||
|
|
||||||
try:
|
|
||||||
interfaces_host = sorted(instance.proxy.get_ifaces())
|
|
||||||
except Exception as e:
|
|
||||||
addlogmsg(request.user.username, instance.name, e)
|
|
||||||
messages.error(request, e)
|
|
||||||
|
|
||||||
return render(request, 'instance.html', locals())
|
return render(request, 'instance.html', locals())
|
||||||
|
|
||||||
|
|
||||||
|
@ -210,18 +192,18 @@ def check_instance(request, vname):
|
||||||
data = {'vname': vname, 'exists': False}
|
data = {'vname': vname, 'exists': False}
|
||||||
if instance:
|
if instance:
|
||||||
data['exists'] = True
|
data['exists'] = True
|
||||||
return HttpResponse(json.dumps(data))
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
def sshkeys(request, vname):
|
def sshkeys(request, pk):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
:param vname:
|
:param vname:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
instance = get_instance(request.user, pk)
|
||||||
instance_keys = []
|
instance_keys = []
|
||||||
userinstances = UserInstance.objects.filter(instance__name=vname)
|
userinstances = UserInstance.objects.filter(instance=instance)
|
||||||
|
|
||||||
for ui in userinstances:
|
for ui in userinstances:
|
||||||
keys = UserSSHKey.objects.filter(user=ui.user)
|
keys = UserSSHKey.objects.filter(user=ui.user)
|
||||||
|
@ -239,7 +221,7 @@ def get_instance(user, pk):
|
||||||
'''
|
'''
|
||||||
Check that instance is available for user, if not raise 404
|
Check that instance is available for user, if not raise 404
|
||||||
'''
|
'''
|
||||||
instance = Instance.objects.get(pk=pk)
|
instance = get_object_or_404(Instance, pk=pk)
|
||||||
user_instances = user.userinstance_set.all().values_list('instance', flat=True)
|
user_instances = user.userinstance_set.all().values_list('instance', flat=True)
|
||||||
|
|
||||||
if user.is_superuser or instance.id in user_instances:
|
if user.is_superuser or instance.id in user_instances:
|
||||||
|
@ -498,7 +480,6 @@ def resize_disk(request, pk):
|
||||||
if request.user.is_superuser or request.user.is_staff or userinstance.is_change:
|
if request.user.is_superuser or request.user.is_staff or userinstance.is_change:
|
||||||
disks_new = list()
|
disks_new = list()
|
||||||
for disk in disks:
|
for disk in disks:
|
||||||
# input_disk_size = utils.filesizefstr(request.POST.get('disk_size_' + disk['dev'], ''))
|
|
||||||
input_disk_size = int(request.POST.get('disk_size_' + disk['dev'], '0')) * 1073741824
|
input_disk_size = int(request.POST.get('disk_size_' + disk['dev'], '0')) * 1073741824
|
||||||
if input_disk_size > disk['size'] + (64 << 20):
|
if input_disk_size > disk['size'] + (64 << 20):
|
||||||
disk['size_new'] = input_disk_size
|
disk['size_new'] = input_disk_size
|
||||||
|
@ -973,7 +954,7 @@ def set_qos(request, pk):
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("%(qos_dir)s QoS is set. Network XML is changed. \
|
_("%(qos_dir)s QoS is set. Network XML is changed. \
|
||||||
Stop and start network to activate new config." ) % {'qos_dir': qos_dir.capitalize()})
|
Stop and start network to activate new config.") % {'qos_dir': qos_dir.capitalize()})
|
||||||
|
|
||||||
return redirect(request.META.get('HTTP_REFERER') + '#network')
|
return redirect(request.META.get('HTTP_REFERER') + '#network')
|
||||||
|
|
||||||
|
@ -991,7 +972,7 @@ def unset_qos(request, pk):
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_("%(qos_dir)s QoS is deleted. Network XML is changed. \
|
_("%(qos_dir)s QoS is deleted. Network XML is changed. \
|
||||||
Stop and start network to activate new config." ) % {'qos_dir': qos_dir.capitalize()})
|
Stop and start network to activate new config.") % {'qos_dir': qos_dir.capitalize()})
|
||||||
return redirect(request.META.get('HTTP_REFERER') + '#network')
|
return redirect(request.META.get('HTTP_REFERER') + '#network')
|
||||||
|
|
||||||
|
|
||||||
|
@ -1005,7 +986,7 @@ def add_owner(request, pk):
|
||||||
if app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER == 'False':
|
if app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER == 'False':
|
||||||
check_inst = UserInstance.objects.filter(instance=instance).count()
|
check_inst = UserInstance.objects.filter(instance=instance).count()
|
||||||
|
|
||||||
if check_inst > 1:
|
if check_inst > 0:
|
||||||
messages.error(request, _("Only one owner is allowed and the one already added"))
|
messages.error(request, _("Only one owner is allowed and the one already added"))
|
||||||
else:
|
else:
|
||||||
add_user_inst = UserInstance(instance=instance, user_id=user_id)
|
add_user_inst = UserInstance(instance=instance, user_id=user_id)
|
||||||
|
@ -1027,7 +1008,7 @@ def del_owner(request, pk):
|
||||||
return redirect(request.META.get('HTTP_REFERER') + '#users')
|
return redirect(request.META.get('HTTP_REFERER') + '#users')
|
||||||
|
|
||||||
|
|
||||||
@permission_required('instances.clone_instances')
|
@permission_required('instances.clone_instances', raise_exception=True)
|
||||||
def clone(request, pk):
|
def clone(request, pk):
|
||||||
instance = get_instance(request.user, pk)
|
instance = get_instance(request.user, pk)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue