Beispiel #1
0
def clear(request, cluster_slug, job_id):
    """
    Remove a job.
    """

    user = request.user
    cluster = get_object_or_404(Cluster, slug=cluster_slug)
    job = get_object_or_404(Job, cluster__slug=cluster_slug, job_id=job_id)
    obj = job.obj

    # if not a superuser, check permissions on the object itself
    cluster_admin = user.is_superuser or user.has_perm('admin', cluster)

    if not cluster_admin:
        if isinstance(obj, (Cluster, Node)):
            raise Http403(NO_PRIVS)
        elif isinstance(obj, (VirtualMachine, )):
            # object is a virtual machine, check perms on VM and on Cluster
            if not (obj.owner_id == user.get_profile().pk or user.has_perm(
                    'admin', obj) or user.has_perm('admin', obj.cluster)):
                raise Http403(NO_PRIVS)

    # If the job points to an object, and the job is the most recent job on
    # the object, then clear it from the object.
    if obj is not None:
        if obj.last_job == job:
            obj.last_job = None
            obj.ignore_cache = False
            obj.save()

    # "Clear" the job. With extreme prejudice.
    job.delete()

    return HttpResponse('1', mimetype='application/json')
def used_resources(request, rest=False):
    """ view for returning used resources for a given cluster user """
    try:
        cluster_user_id = request.GET['id']
    except KeyError:
        return render_404(request, 'requested user was not found')
    cu = get_object_or_404(ClusterUser, pk=cluster_user_id)

    # must be a super user, the user in question, or a member of the group
    user = request.user
    if not user.is_superuser:
        user_type = ContentType.objects.get_for_model(Profile)
        if cu.real_type_id == user_type.pk:
            if not Profile.objects.filter(clusteruser_ptr=cu.pk, user=user)\
                .exists():
                raise Http403(_('You are not authorized to view this page'))
        else:
            if not Organization.objects.filter(clusteruser_ptr=cu.pk, \
                                               group__user=user).exists():
                raise Http403(_('You are not authorized to view this page'))

    resources = get_used_resources(cu.cast())
    if rest:
        return resources
    else:
        return render_to_response("ganeti/overview/used_resources.html", {
            'resources':resources
        }, context_instance=RequestContext(request))
def replace_disks(request, cluster_slug, instance):
    """
    view used for initiating a Replace Disks job
    """
    vm, cluster = get_vm_and_cluster_or_404(cluster_slug, instance)
    user = request.user
    if not (user.is_superuser or user.has_any_perms(cluster, ['admin','replace_disks'])):
        raise Http403(NO_PRIVS)

    if request.method == 'POST':
        form = ReplaceDisksForm(vm, request.POST)
        if form.is_valid():
            try:
                job = form.save()
                job.refresh()
                content = json.dumps(job.info)

                # log information
                log_action('VM_REPLACE_DISKS', user, vm, job)
            except GanetiApiError, e:
                content = json.dumps({'__all__':[str(e)]})
        else:
            # error in form return ajax response
            content = json.dumps(form.errors)
        return HttpResponse(content, mimetype='application/json')
def reparent(request, cluster_slug, instance):
    """
    update a virtual machine to have a new owner
    """
    vm, cluster = get_vm_and_cluster_or_404(cluster_slug, instance)

    user = request.user
    if not (user.is_superuser or user.has_perm('admin', cluster)):
        raise Http403(
            _('You do not have permissions to change the owner of this virtual machine'))

    if request.method == 'POST':
        form = ChangeOwnerForm(request.POST)
        if form.is_valid():
            data = form.cleaned_data
            vm.owner = data['owner']
            vm.save(force_update=True)

            # log information about creating the machine
            log_action('VM_MODIFY', user, vm)

            return HttpResponseRedirect(reverse('instance-detail', args=[cluster_slug, instance]))

    else:
        form = ChangeOwnerForm()

    return render_to_response(
        'ganeti/virtual_machine/reparent.html', {
        'cluster': cluster,
        'vm': vm,
        'form': form
        },
        context_instance=RequestContext(request),
    )
Beispiel #5
0
def role(request, cluster_slug, host):
    """
    view used for setting node role
    """
    node, cluster = get_node_and_cluster_or_404(cluster_slug, host)

    user = request.user
    if not (user.is_superuser
            or user.has_any_perms(cluster, ['admin', 'migrate'])):
        raise Http403(NO_PRIVS)

    if request.method == 'POST':
        form = RoleForm(request.POST)
        if form.is_valid():
            try:
                job = node.set_role(form.cleaned_data['role'])
                job.refresh()
                msg = job.info

                # log information
                log_action('NODE_ROLE_CHANGE', user, node)
                return HttpResponse(json.dumps(msg),
                                    mimetype='application/json')
            except GanetiApiError, e:
                content = json.dumps({'__all__': [str(e)]})
        else:
            # error in form return ajax response
            content = json.dumps(form.errors)
        return HttpResponse(content, mimetype='application/json')
Beispiel #6
0
def evacuate(request, cluster_slug, host):
    """
    view used for initiating a node evacuate job
    """
    node, cluster = get_node_and_cluster_or_404(cluster_slug, host)

    user = request.user
    if not (user.is_superuser
            or user.has_any_perms(cluster, ['admin', 'migrate'])):
        raise Http403(NO_PRIVS)

    if request.method == 'POST':
        form = EvacuateForm(cluster, node, request.POST)
        if form.is_valid():
            try:
                data = form.cleaned_data
                evacuate_node = data['node']
                iallocator_hostname = data['iallocator_hostname']
                job = node.evacuate(iallocator_hostname, evacuate_node)
                job.refresh()
                msg = job.info

                # log information
                log_action('NODE_EVACUATE', user, node, job)

                return HttpResponse(json.dumps(msg),
                                    mimetype='application/json')
            except GanetiApiError, e:
                content = json.dumps({'__all__': [str(e)]})
        else:
            # error in form return ajax response
            content = json.dumps(form.errors)
        return HttpResponse(content, mimetype='application/json')
def object_log(request, cluster_slug):
    """ displays object log for this cluster """
    cluster = get_object_or_404(Cluster, slug=cluster_slug)
    user = request.user
    if not (user.is_superuser or user.has_perm('admin', cluster)):
        raise Http403(NO_PRIVS)
    return list_for_object(request, cluster)
def migrate(request, cluster_slug, instance):
    """
    view used for initiating a Node Migrate job
    """
    vm, cluster = get_vm_and_cluster_or_404(cluster_slug, instance)

    user = request.user
    if not (user.is_superuser or user.has_any_perms(cluster, ['admin','migrate'])):
        raise Http403(NO_PRIVS)

    if request.method == 'POST':
        form = MigrateForm(request.POST)
        if form.is_valid():
            try:
                job = vm.migrate(form.cleaned_data['mode'])
                job.refresh()
                content = json.dumps(job.info)

                # log information
                log_action('VM_MIGRATE', user, vm, job)
            except GanetiApiError, e:
                content = json.dumps({'__all__':[str(e)]})
        else:
            # error in form return ajax response
            content = json.dumps(form.errors)
        return HttpResponse(content, mimetype='application/json')
def quota(request, cluster_slug, user_id):
    """
    Updates quota for a user
    """
    cluster = get_object_or_404(Cluster, slug=cluster_slug)
    user = request.user
    if not (user.is_superuser or user.has_perm('admin', cluster)):
        raise Http403(NO_PRIVS)

    if request.method == 'POST':
        form = QuotaForm(request.POST)
        if form.is_valid():
            data = form.cleaned_data
            cluster_user = data['user']
            if data['delete']:
                cluster.set_quota(cluster_user, None)
            else:
                cluster.set_quota(cluster_user, data)

            # return updated html
            cluster_user = cluster_user.cast()
            url = reverse('cluster-permissions', args=[cluster.slug])
            if isinstance(cluster_user, (Profile, )):
                return render_to_response(
                    "ganeti/cluster/user_row.html", {
                        'object': cluster,
                        'user_detail': cluster_user.user,
                        'url': url
                    },
                    context_instance=RequestContext(request))
            else:
                return render_to_response(
                    "ganeti/cluster/group_row.html", {
                        'object': cluster,
                        'group': cluster_user.group,
                        'url': url
                    },
                    context_instance=RequestContext(request))

        # error in form return ajax response
        content = json.dumps(form.errors)
        return HttpResponse(content, mimetype='application/json')

    if user_id:
        cluster_user = get_object_or_404(ClusterUser, id=user_id)
        quota = cluster.get_quota(cluster_user)
        data = {'user': user_id}
        if quota:
            data.update(quota)
    else:
        return render_404(request, _('User was not found'))

    form = QuotaForm(data)
    return render_to_response("ganeti/cluster/quota.html", {
        'form': form,
        'cluster': cluster,
        'user_id': user_id
    },
                              context_instance=RequestContext(request))
def missing_ganeti(request):
    """
    View for displaying VirtualMachines missing from the ganeti cluster
    """
    user = request.user
    if user.is_superuser:
        clusters = Cluster.objects.all()
    else:
        clusters = user.get_objects_any_perms(Cluster, ['admin'])
        if not clusters:
            raise Http403(NO_PRIVS)

    vms = []
    for cluster in clusters:
        for vm in cluster.missing_in_ganeti:
            vms.append((vm, vm))

    if request.method == 'POST':
        # process updates if this was a form submission
        form = VirtualMachineForm(vms, request.POST)
        if form.is_valid():
            # update all selected VirtualMachines
            data = form.cleaned_data
            vm_ids = data['virtual_machines']
            q = VirtualMachine.objects.filter(hostname__in=vm_ids)

            missing = defaultdict(lambda:0)
            for i in q:
                missing[ i.cluster_id ] -= 1

            q.delete()

            # remove updated vms from the list
            vms = filter(lambda x: unicode(x[0]) not in vm_ids, vms)

    else:
        form = VirtualMachineForm(vms)

    vms = {}
    for cluster in clusters:
        for vm in cluster.missing_in_ganeti:
            vms[vm] = (cluster.hostname, vm)

    vmhostnames = vms.keys()
    vmhostnames.sort()

    vms_tuplelist = []
    for i in vmhostnames:
        vms_tuplelist.append((i, vms[i][0], vms[i][1]))

    vms = vms_tuplelist

    return render_to_response("ganeti/importing/missing.html", {
        'vms': vms,
        'form':form,
        },
        context_instance=RequestContext(request),
    )
Beispiel #11
0
    def get_queryset(self):
        self.cluster = get_object_or_404(Cluster,
                                         slug=self.kwargs["cluster_slug"])
        user = self.request.user
        admin = user.is_superuser or user.has_perm("admin", self.cluster)
        if not admin:
            raise Http403(NO_PRIVS)

        return self.cluster.virtual_machines.select_related("cluster").all()
def orphans(request):
    """
    displays list of orphaned VirtualMachines, i.e. VirtualMachines without
    an owner.
    """
    user = request.user
    if user.is_superuser:
        clusters = Cluster.objects.all()
    else:
        clusters = user.get_objects_any_perms(Cluster, ['admin'])
        if not clusters:
            raise Http403(NO_PRIVS)

    vms_with_cluster = VirtualMachine.objects.filter(owner=None, cluster__in=clusters) \
                          .order_by('hostname').values_list('id','hostname','cluster')

    if request.method == 'POST':
        # strip cluster from vms
        vms = [(i[0], i[1]) for i in vms_with_cluster]

        # process updates if this was a form submission
        form = OrphanForm(vms, request.POST)
        if form.is_valid():
            # update all selected VirtualMachines
            data = form.cleaned_data
            owner = data['owner']
            vm_ids = data['virtual_machines']

            # update the owner and save the vm.  This isn't the most efficient
            # way of updating the VMs but we would otherwise need to group them
            # by cluster
            orphaned = defaultdict(lambda:0)
            for id in vm_ids:
                vm = VirtualMachine.objects.get(id=id)
                vm.owner = owner
                vm.save()
                orphaned[vm.cluster_id] -= 1

            # remove updated vms from the list
            vms_with_cluster = [i for i in vms_with_cluster
                if unicode(i[0]) not in vm_ids]

    else:
        # strip cluster from vms
        form = ImportForm([(i[0], i[1]) for i in vms_with_cluster])

    clusterdict = {}
    for i in clusters:
        clusterdict[i.id] = i.hostname
    vms = [ (i[0], clusterdict[i[2]], i[1]) for i in vms_with_cluster ]

    return render_to_response("ganeti/importing/orphans.html", {
        'vms': vms,
        'form':form,
        },
        context_instance=RequestContext(request),
    )
Beispiel #13
0
def create_instance_from_template(request, cluster_slug, template):
    """
    View to create a new instance from a given template.
      Post method is handled by virtual_machine create view.
    """
    user = request.user
    if cluster_slug:
        cluster = get_object_or_404(Cluster, slug=cluster_slug)
        if not (
            user.is_superuser or
            user.has_perm('admin', cluster) or
            user.has_perm('create_vm', cluster)
            ):
            raise Http403(NO_PRIVS)

    vm_template = get_object_or_404(VirtualMachineTemplate,
                                    template_name=template,
                                    cluster__slug=cluster_slug)

    # Work with vm_template vars here
    initial = dict(
        cluster=vm_template.cluster_id,
    )
    initial.update(vars(vm_template))

    # nics and disks need to be replaced by expected
    #  form fields of disk_size_#, nic_mode_#, and nic_link_#
    ignore_fields = ('disks', 'nics', '_state',
        'description')
    for field in ignore_fields:
        del initial[field]

    # Initialize mutliple disks
    initial['disk_count'] = len(vm_template.disks)
    if initial['disk_count'] == 0:
        initial['disk_count'] = 1
    for i,disk in enumerate(vm_template.disks):
        initial['disk_size_%s'%i] = disk['size']

    # initialize multiple nics
    initial['nic_count'] = len(vm_template.nics)
    if initial['nic_count'] == 0:
        initial['nic_count'] = 1
    for i,nic in enumerate(vm_template.nics):
        initial['nic_mode_%s'%i] = nic['mode']
        initial['nic_link_%s'%i] = nic['link']

    form = NewVirtualMachineForm(user, initial=initial)
    cluster_defaults = {} #cluster_default_info(cluster)

    return render_to_response('ganeti/virtual_machine/create.html', {
        'form':form,
        'cluster_defaults':json.dumps(cluster_defaults),
        },
        context_instance = RequestContext(request)
    )
Beispiel #14
0
    def get_queryset(self):
        self.node, self.cluster = get_node_and_cluster_or_404(
            self.kwargs["cluster_slug"], self.kwargs["host"])

        user = self.request.user
        if not (user.is_superuser
                or user.has_any_perms(self.cluster, ["admin", "migrate"])):
            raise Http403(NO_PRIVS)

        return self.node.primary_vms.all()
def importing(request):
    """
    View that loads main importing view
    """
    user = request.user
    if not user.is_superuser or user.get_objects_any_perms(Cluster, ['admin']):
        raise Http403(NO_PRIVS)

    return render_to_response('ganeti/importing/nodes/main.html',
                              context_instance=RequestContext(request))
def modify(request, cluster_slug, instance):
    vm, cluster = get_vm_and_cluster_or_404(cluster_slug, instance)

    user = request.user
    if not (user.is_superuser or user.has_any_perms(vm, ['admin','modify'])
        or user.has_perm('admin', cluster)):
        raise Http403(
            'You do not have permissions to edit this virtual machine')

    hv = get_hypervisor(vm)
    if hv == 'kvm':
        hv_form = KvmModifyVirtualMachineForm
        template = 'ganeti/virtual_machine/edit_kvm.html'
    elif hv == 'xen-pvm':
        hv_form = PvmModifyVirtualMachineForm
        template = 'ganeti/virtual_machine/edit_pvm.html'
    elif hv == 'xen-hvm':
        hv_form = HvmModifyVirtualMachineForm
        template = 'ganeti/virtual_machine/edit_hvm.html'
    else:
        hv_form = None
        template = 'ganeti/virtual_machine/edit_base.html'
        # XXX no matter what, we're gonna call hv_form() and die. Let's do it
        # louder than usual. >:3
        msg = "Hey, guys, implementation error in views/vm.py:modify"
        raise RuntimeError(msg)

    if request.method == 'POST':

        form = hv_form(vm, request.POST)

        form.owner = vm.owner
        form.vm = vm
        form.cluster = cluster
        if form.is_valid():
            data = form.cleaned_data
            request.session['edit_form'] = data
            request.session['edit_vm'] = vm.id
            return HttpResponseRedirect(
            reverse('instance-modify-confirm', args=[cluster.slug, vm.hostname]))

    elif request.method == 'GET':
        if 'edit_form' in request.session and vm.id == request.session['edit_vm']:
            form = hv_form(vm, request.session['edit_form'])
        else:
            form = hv_form(vm)

    return render_to_response(template, {
        'cluster': cluster,
        'instance': vm,
        'form': form,
        },
        context_instance=RequestContext(request),
    )
Beispiel #17
0
def clear_ganeti_error(request, pk):
    """
    Clear a single error message
    """
    user = request.user
    error = get_object_or_404(GanetiError, pk=pk)
    obj = error.obj

    # if not a superuser, check permissions on the object itself
    if not user.is_superuser:
        if isinstance(obj, (Cluster,)) and not user.has_perm('admin', obj):
            raise Http403(NO_PRIVS)
        elif isinstance(obj, (VirtualMachine,)):
            # object is a virtual machine, check perms on VM and on Cluster
            if not (obj.owner_id == user.get_profile().pk or \
                user.has_perm('admin', obj.cluster)):
                    raise Http403(NO_PRIVS)

    # clear the error
    GanetiError.objects.filter(pk=error.pk).update(cleared=True)

    return HttpResponse('1', mimetype='application/json')
def missing_ganeti(request):
    """
    View for displaying VirtualMachines missing from the ganeti cluster
    """
    user = request.user
    if user.is_superuser:
        clusters = Cluster.objects.all()
    else:
        clusters = user.get_objects_any_perms(Cluster, ['admin'])
        if not clusters:
            raise Http403(NO_PRIVS)

    nodes = []
    for cluster in clusters:
        for node in cluster.nodes_missing_in_ganeti:
            nodes.append((node, node))

    if request.method == 'POST':
        # process updates if this was a form submission
        form = NodeForm(nodes, request.POST)
        if form.is_valid():
            # update all selected Nodes
            data = form.cleaned_data
            node_ids = data['nodes']
            Node.objects.filter(hostname__in=node_ids).delete()

    else:
        form = NodeForm(nodes)

    nodes = {}
    for cluster in clusters:
        for node in cluster.nodes_missing_in_ganeti:
            nodes[node] = (cluster.hostname, node)

    node_hostnames = nodes.keys()
    node_hostnames.sort()

    node_tuple_list = []
    for i in node_hostnames:
        node_tuple_list.append((i, nodes[i][0], nodes[i][1]))

    nodes = node_tuple_list

    return render_to_response(
        "ganeti/importing/nodes/missing.html",
        {
            'nodes': nodes,
            'form': form,
        },
        context_instance=RequestContext(request),
    )
Beispiel #19
0
def edit(request, cluster_slug=None):
    """
    Edit a cluster
    """
    if cluster_slug:
        cluster = get_object_or_404(Cluster, slug=cluster_slug)
    else:
        cluster = None

    user = request.user
    if not (user.is_superuser or
            (cluster and user.has_perm('admin', cluster))):
        raise Http403(NO_PRIVS)

    if request.method == 'POST':
        form = EditClusterForm(request.POST, instance=cluster)
        if form.is_valid():
            cluster = form.save()
            # TODO Create post signal to import
            #   virtual machines on edit of cluster
            if cluster.info is None:
                try:
                    cluster.sync_nodes()
                    cluster.sync_virtual_machines()
                except GanetiApiError:
                    # ganeti errors here are silently discarded.  It's
                    # valid to enter bad info.  A user might be adding
                    # info for an offline cluster.
                    pass

            log_action('EDIT' if cluster_slug else 'CREATE', user, cluster)

            return HttpResponseRedirect(
                reverse('cluster-detail', args=[cluster.slug]))

    elif request.method == 'DELETE':
        cluster.delete()
        return HttpResponse('1', mimetype='application/json')

    else:
        form = EditClusterForm(instance=cluster)

    return render_to_response(
        "ganeti/cluster/edit.html",
        {
            'form': form,
            'cluster': cluster,
        },
        context_instance=RequestContext(request),
    )
def permissions(request, cluster_slug, instance, user_id=None, group_id=None):
    """
    Update a users permissions.
    """
    vm = get_object_or_404(VirtualMachine, hostname=instance,
                           cluster__slug=cluster_slug)

    user = request.user
    if not (user.is_superuser or user.has_perm('admin', vm) or
        user.has_perm('admin', vm.cluster)):
        raise Http403(NO_PRIVS)

    url = reverse('vm-permissions', args=[cluster_slug, vm.hostname])
    return view_permissions(request, vm, url, user_id, group_id)
    def get_object(self):
        user = self.request.user
        vm, cluster = get_vm_and_cluster_or_404(self.kwargs["cluster_slug"],
                                                self.kwargs["instance"])
        if not (
            user.is_superuser or
            user.has_any_perms(vm, ["remove", "admin"]) or
            user.has_perm("admin", cluster)
            ):
            raise Http403(NO_PRIVS)

        self.vm = vm
        self.cluster = cluster
        return vm
def cluster_defaults(request):
    """
    Ajax view for retrieving the default cluster options to be set
    on the NewVirtualMachineForm.
    """
    cluster_id = request.GET.get('cluster_id', None)
    hypervisor = request.GET.get('hypervisor', None)
    cluster = get_object_or_404(Cluster, id__exact=cluster_id)

    user = request.user
    if not (user.is_superuser or user.has_any_perms(cluster, ['admin', 'create_vm'])):
        raise Http403(_('You do not have permission to view the default cluster options'))

    content = json.dumps(cluster_default_info(cluster, hypervisor))
    return HttpResponse(content, mimetype='application/json')
Beispiel #23
0
def users(request, cluster_slug):
    """
    Display all of the Users of a Cluster
    """
    cluster = get_object_or_404(Cluster, slug=cluster_slug)

    user = request.user
    if not (user.is_superuser or user.has_perm('admin', cluster)):
        raise Http403(NO_PRIVS)

    url = reverse('cluster-permissions', args=[cluster.slug])
    return view_users(request,
                      cluster,
                      url,
                      template='ganeti/cluster/users.html')
Beispiel #24
0
def object_log(request, cluster_slug, host, rest=False):
    """
    Display object log for this node
    """
    node, cluster = get_node_and_cluster_or_404(cluster_slug, host)

    user = request.user
    if not (user.is_superuser
            or user.has_any_perms(cluster, ['admin', 'migrate'])):
        if not rest:
            raise Http403(NO_PRIVS)
        else:
            return {'error': NO_PRIVS}

    return list_for_object(request, node, rest)
def object_log(request, cluster_slug, instance, rest=False):
    """
    Display all of the Users of a VirtualMachine
    """
    cluster = get_object_or_404(Cluster, slug=cluster_slug)
    vm = get_object_or_404(VirtualMachine, hostname=instance)

    user = request.user
    if not (user.is_superuser or user.has_perm('admin', vm) or
        user.has_perm('admin', cluster)):
        raise Http403(NO_PRIVS)

    if rest:
        return list_for_object(request, vm, True)
    else:
        return list_for_object(request, vm)
def users(request, cluster_slug, instance, rest=False):
    """
    Display all of the Users of a VirtualMachine
    """
    vm, cluster = get_vm_and_cluster_or_404(cluster_slug, instance)

    user = request.user
    if not (user.is_superuser or user.has_perm('admin', vm) or
        user.has_perm('admin', cluster)):
        if rest:
            return {'msg': NO_PRIVS, 'code': 403}
        else:
            raise Http403(NO_PRIVS)

    url = reverse('vm-permissions', args=[cluster.slug, vm.hostname])
    return view_users(request, vm, url, rest=rest)
Beispiel #27
0
def create_template_from_instance(request, cluster_slug, instance):
    """
    View to create a new template from a given instance.
      Post method is handled by template create view.
    """
    user = request.user
    if cluster_slug:
        cluster = get_object_or_404(Cluster, slug=cluster_slug)
        if not (
            user.is_superuser or
            user.has_perm('admin', cluster) or
            user.has_perm('create_vm', cluster)
            ):
            raise Http403(NO_PRIVS)

    vm = get_object_or_404(VirtualMachine, hostname=instance,
        cluster__slug=cluster_slug)

    # Work with vm vars here
    info = vm.info
    links = info['nic.links']
    modes = info['nic.modes']
    sizes = info['disk.sizes']

    initial = dict(
        template_name=instance,
        cluster=cluster.id,
        start=info['admin_state'],
        disk_template=info['disk_template'],
        disk_type=info['hvparams']['disk_type'],
        nic_type=info['hvparams']['nic_type'],
        os=vm.operating_system,
        vcpus=vm.virtual_cpus,
        memory=vm.ram,
        disks=[{'size':size} for size in sizes],
        nics=[{'mode':mode, 'link':link} for mode, link in zip(modes, links)],
        nic_count=len(links),
    )
    form = VirtualMachineTemplateForm(user=user, initial=initial)

    return render_to_response('ganeti/vm_template/create.html', {
        'form':form,
        'from_vm':vm,
        'action':reverse('template-create'),
        },
        context_instance = RequestContext(request)
    )
def reinstall(request, cluster_slug, instance):
    """
    Reinstall a VM.
    """

    user = request.user
    instance, cluster = get_vm_and_cluster_or_404(cluster_slug, instance)

    # Check permissions.
    # XXX Reinstalling is somewhat similar to deleting in that you destroy data,
    # so use that for now.
    if not (
        user.is_superuser or
        user.has_any_perms(instance, ["remove", "admin"]) or
        user.has_perm("admin", cluster)
        ):
        raise Http403(NO_PRIVS)

    if request.method == 'GET':
        return render_to_response(
            "ganeti/virtual_machine/reinstall.html",
            {'vm': instance, 'oschoices': cluster_os_list(cluster),
             'current_os': instance.operating_system,
             'cluster':cluster},
            context_instance=RequestContext(request),
        )

    elif request.method == 'POST':
        # Reinstall instance
        if "os" in request.POST:
            os = request.POST["os"]
        else:
            os = instance.operating_system

        # XXX no_startup=True prevents quota circumventions. possible future solution would be a checkbox
        # asking whether they want to start up, and check quota here if they do (would also involve
        # checking whether this VM is already running and subtracting that)

        job_id = instance.rapi.ReinstallInstance(instance.hostname, os=os, no_startup=True)
        job = Job.objects.create(job_id=job_id, obj=instance, cluster=cluster)
        VirtualMachine.objects.filter(id=instance.id).update(last_job=job, ignore_cache=True)

        # log information
        log_action('VM_REINSTALL', user, instance, job)

        return HttpResponseRedirect(
            reverse('instance-detail', args=[cluster.slug, instance.hostname]))
def cluster_options(request):
    """
    Ajax view for retrieving node and operating system choices for a given
    cluster.
    """
    cluster_id = request.GET.get('cluster_id', None)
    cluster = get_object_or_404(Cluster, id__exact=cluster_id)

    user = request.user
    if not (user.is_superuser or user.has_any_perms(cluster, ['admin', 'create_vm'])):
        raise Http403(
            _('You do not have permissions to view this cluster'))

    oslist = cluster_os_list(cluster)
    nodes = [str(h) for h in cluster.nodes.values_list('hostname', flat=True)]
    content = json.dumps({'nodes':nodes, 'os':oslist})
    return HttpResponse(content, mimetype='application/json')
def recover_failed_deploy(request, cluster_slug, instance):
    """
    Loads a vm that failed to deploy back into the edit form
    """
    vm, cluster = get_vm_and_cluster_or_404(cluster_slug, instance)

    user = request.user
    if not (user.is_superuser or user.has_any_perms(vm, ['admin','modify']) \
        or user.has_perm('admin', cluster)):
        raise Http403(_('You do not have permissions to edit \
            this virtual machine'))

    # if there is no template, we can't recover.  redirect back to the detail
    # page.  its likely that this vm was already fixed
    if not vm.template_id:
        return HttpResponseRedirect(reverse('instance-detail', \
                                            args=[cluster_slug, instance]))

    # create initial data - load this from the template.  Not all properties
    # can be copied directly, some need to be copied explicitly due to naming
    # conflicts.
    initial = {'hostname':instance}
    for k,v in vm.template.__dict__.items():
        if v is not None and v != '':
            initial[k] = v
    initial['cluster'] = vm.template.cluster_id
    initial['pnode'] = vm.template.pnode
    initial['owner'] = vm.owner_id

    if "disks" in initial:
        for i, disk in enumerate(initial['disks']):
            initial['disk_size_%s' % i] = "%dMB" % disk["size"]

    if "nics" in initial:
        for i, nic in enumerate(initial['nics']):
            initial['nic_link_%s' % i] = nic['link']
            initial['nic_mode_%s' % i] = nic['mode']

    form = NewVirtualMachineForm(request.user, initial=initial)
    cluster_defaults = cluster_default_info(cluster)

    return render_to_response('ganeti/virtual_machine/create.html',
        {'form': form, 'cluster_defaults':json.dumps(cluster_defaults)},
        context_instance=RequestContext(request),
    )