class Instance(models.Model):
  name = models.CharField(max_length=100)
  alias = models.CharField(max_length=100, default="My Instance", null=True, blank=True)
  volume = models.OneToOneField(Volume)
  user = models.ForeignKey(User, related_name="instance_user")
  creator = models.ForeignKey(User)
  # virtual attributes
  vcpu = models.IntegerField(max_length=2, default=1)
  memory = models.ForeignKey(Size, related_name="instance_memory")
  disk = models.ForeignKey(InstallationDisk, null=True, blank=True)
  # time fields
  updated = models.DateTimeField(auto_now=True)
  created = models.DateTimeField(auto_now_add=True)
  initialised = models.BooleanField(default=False)
  # status
  NOSTATE = 0
  RUNNING = 1
  BLOCKED = 2
  PAUSED = 3
  SHUTTINGDOWN = 4
  SHUTDOWN = 5
  CRASHED = 6
  SUSPENDED = 7
  LAST = 8
  NONE = 9
  STATUS_CHOICES = (
    (NOSTATE, 'No State - Hypervisor may be down'),
    (RUNNING, 'Running'),
    (BLOCKED, 'Blocked'),
    (PAUSED, 'Paused'),
    (SHUTTINGDOWN, 'Shutting Down'),
    (SHUTDOWN, 'Shutdown'),
    (CRASHED, 'Crashed'),
    (SUSPENDED, 'Suspended'),
    (LAST, 'Last'),
    (NONE, 'None'),
  )
  status = models.IntegerField(max_length=1, choices=STATUS_CHOICES, default=NONE)
  base = models.BooleanField(default=False, editable=False)

  def __str__(self):
    return unicode(self).encode('utf-8')

  def __unicode__(self):
    return "%s / %d VCPUs / %s RAM / %s HDD / %s" % (self.alias, self.vcpu, self.memory.name, self.volume.capacity.name, self.name)

  def get_xml(self):
    dom = self.get_instance()
    if dom: 
      try:
        return dom.XMLDesc(0)
      except libvirt.libvirtError as e:
        return e
    return  "Error"

  def get_vnc_port(self):
    instance = self.get_instance()
    if not instance: return 0
    tree = ElementTree.fromstring(instance.XMLDesc(0))
    graphics = tree.findall('devices/graphics')
    return int(graphics[0].get('port'))

  def get_status_html(self):
    if self.status == 1:
      return '<span class="label label-success">%s</span>' % (self.get_status_display())
    return '<span class="label label-warning">%s</span>' % (self.get_status_display())

  def get_instance(self):
    hypervisor = self.volume.storagepool.hypervisor.get_connection()
    instance = None
    if hypervisor:
      try:
        instance = hypervisor.lookupByName(self.name)
      except libvirt.libvirtError:
        pass
    if not instance:
      self.status = self.NOSTATE
      self.save()
    return instance

  def update(self, force=False):
    if not force and (timezone.now() - self.updated) < timedelta(seconds=15): return
    instance = self.get_instance()
    if instance:
      (status, na, memory, vcpu, na) = instance.info()
      self.status = status
      self.save()

  def delete(self, request=None):
    instance = self.get_instance()
    if instance:
      try:
        try:
          # shutdown/destroy the vm if it's still up and running. 
          instance.destroy()
        except libvirt.libvirtError as e:
          if str(e) != 'Requested operation is not valid: domain is not running': raise e
        instance.undefine()
        self.volume.delete(request)
        if request:
          persistent_messages.add_message(request, persistent_messages.SUCCESS, 'Deleted Instance %s' % (self))
          if request.user != self.user:
            persistent_messages.add_message(request, persistent_messages.SUCCESS, 'Deleted Instance %s' % (self), user=self.user)
        super(Instance, self).delete()
      except libvirt.libvirtError as e:
        print e
        print type(e)
        if request:
          persistent_messages.add_message(request, persistent_messages.ERROR, 'Unable to delete Instance %s: %s' % (self, e))
          if request.user != self.user:
            persistent_messages.add_message(request, persistent_messages.ERROR, 'Unable to delete Instance %s' % (self), user=self.user)
  
    elif request:
      persistent_messages.add_message(request, persistent_messages.ERROR, 'Unable to get instance object inorder to delete Instance %s' % (self))
      if request.user != self.user:
        persistent_messages.add_message(request, persistent_messages.ERROR, 'Unable to delete Instance %s' % (self), user=self.user)

  def detach_disk(self, request=None):
    dom = self.get_instance()
    if dom:
      try:
        dom.updateDeviceFlags(DETACH_DISK_TEMPLATE, 0)
        self.disk = None
        self.save()
        if request:
          messages.add_message(request, persistent_messages.SUCCESS, 'Unmounted disk on %s' % (self.name))
      except libvirt.libvirtError as e:
        if request and request.user.is_staff:
          messages.add_message(request, persistent_messages.ERROR, 'Unable to unmount disk on %s, unable to get dom: %s' % (self.name, e))
        elif request:
          messages.add_message(request, persistent_messages.ERROR, 'Unable to unmount diskon %s' % (self.name))

    elif request and request.user.is_staff:
      messages.add_message(request, persistent_messages.ERROR, 'Unable to unmount disk on %s, unable to get dom' % (self.name))
    elif request:
      messages.add_message(request, persistent_messages.ERROR, 'Unable to unmount disk on %s' % (self.name))

  def attach_network(self, address, request=None, first=False):
    dom = self.get_instance()
    if dom:
      template = ATTACH_NETWORK_TEMPLATE.format(
        ip=address.ip,
        device=address.network.device,
        alias=address.ip
      )
      try:
        dom.attachDeviceFlags(template, 0)
        tree = ElementTree.fromstring(dom.XMLDesc(0))
        for i in tree.findall('devices/interface'):
          for p in i.findall('filterref/parameter'):
            if p.get('value') == address.ip:
              mac = i.find('mac').get('address').upper()
              address.mac = mac
              address.save()
        resp = node.send_command(address.network.hypervisor, "network_track_ip", {'ip': address.ip})
        if resp:
          print resp
      except libvirt.libvirtError as e:
        print e

  def detach_network(self, address, request=None):
    dom = self.get_instance()
    if dom:
      template = DETACH_NETWORK_TEMPLATE.format(alias=address.ip, device=address.network.device, mac=address.mac)
      try:
        dom.detachDeviceFlags(template, 0)
        address.delete()
        resp = node.send_command(address.network.hypervisor, "network_remove_ip", {'ip': address.ip})
        if resp:
          print resp
      except libvirt.libvirtError as e:
        print e 

  def attach_disk(self, disk, request=None):
    if disk == None:
      self.detach_disk(request)
      return
    dom = self.get_instance()
    if dom:
      template = ATTACH_DISK_TEMPLATE.format(path=disk.path())
      print template
      try:
        dom.updateDeviceFlags(template, 0)
        self.disk = disk
        self.save()
        messages.add_message(request, persistent_messages.SUCCESS, 'Mounted Disk %s on %s' % (disk, self.name))
      except libvirt.libvirtError as e:
        if request and request.user.is_staff:
          messages.add_message(request, persistent_messages.ERROR, 'Unable to mount %s on %s, unable to get dom: %s' % (disk, self.name, e))
        elif request:
          messages.add_message(request, persistent_messages.ERROR, 'Unable to mount %s on %s' % (disk, self.name))

    elif request and request.user.is_staff:
      messages.add_message(request, persistent_messages.ERROR, 'Unable to mount %s on %s, unable to get dom' % (disk, self.name))
    elif request:
      messages.add_message(request, persistent_messages.ERROR, 'Unable to mount %s on %s' % (disk, self.name))

  def create_random_vnc_password(self, request=None):
    instance = self.get_instance()
    if instance == None:
      if request:
        messages.add_message(request, persistent_messages.ERRPR, 'Unable to get instance in order to create a random VNC password')
      return

    xml = ElementTree.fromstring(instance.XMLDesc(0))
    graphic_element = xml.find('.//graphics')
    passwd = ''.join(choice(string.letters + string.digits) for _ in xrange(8)) 
    graphic_element.attrib['passwd'] = passwd
    try:
      instance.updateDeviceFlags(ElementTree.tostring(graphic_element), 0)
    except libvirt.libvirtError as e:
      if request and request.user.is_staff:
        messages.add_message(request, persistent_messages.ERROR, 'Unable to create random VNC password on %s, error: %s' % (self.name, e))
      elif request:
        messages.add_message(request, persistent_messages.ERROR, 'Unable to create random VNC password on %s' % (self.name))
      return ""
    return passwd
    
  def get_vnc_password(self, request=None):
    return self.create_random_vnc_password(request) class InstanceTask(models.Model):
  name = models.CharField(max_length=100)
  user = models.ForeignKey(User, related_name="instancetask_user")
  storagepool = models.ForeignKey(StoragePool)
  creator = models.ForeignKey(User)
  vcpu = models.IntegerField(max_length=2, default=1)
  memory = models.ForeignKey(Size, related_name="instancetask_memory")
  capacity = models.ForeignKey(Size, related_name="instancetask_capacity")
  disk = models.ForeignKey(InstallationDisk, null=True, blank=True)
  volume = models.OneToOneField(Volume, null=True, blank=True)
  network = models.ForeignKey('network.Network')
  # time fields
  updated = models.DateTimeField(auto_now=True)
  created = models.DateTimeField(auto_now_add=True)
  # task attributes
  task_id = models.CharField(max_length=100, default='dummy')
  percent = models.DecimalField(max_digits=5, decimal_places=2, default=0.0)
  message = models.CharField(max_length=255, default='N/A')
  state = models.CharField(max_length=50, default='STARTING')

  def __str__(self):
    return unicode(self).encode('utf-8')

  def __unicode__(self):
    return "Creating: %s [%d CPU/%s RAM/%s HDD][%s]" % \
      (self.user, self.vcpu, (self.memory.name), (self.capacity.name), self.state)

  def abort(self, request=None):
    pass

  def delete(self, purge=True):
    if self.volume and purge:
      if self.volume.delete():
        super(InstanceTask, self).delete()
    else:
      super(InstanceTask, self).delete()

  def update(self):
    task = AsyncResult(self.task_id)
    status = task.result
    if not status: return
    self.state = task.state
    if 'custom_state' in status:
      self.state = status['custom_state']
    if 'msg' in status:
      self.message = status['msg']
    if 'percent' in status:
      self.percent = int(status['percent'])
    self.save()

  """
    Used to create a random name.
  """
  @staticmethod
  def get_random_name():
    while True:
      name = hexlify(os.urandom(16))
      try:
        volume = InstanceTask.objects.get(name=name)
      except InstanceTask.DoesNotExist:
        try:
          volume = Instance.objects.get(name=name)
        except Instance.DoesNotExist:
          return name class InstanceTaskForm(forms.ModelForm):
  storagepool = forms.ModelChoiceField(queryset=StoragePool.objects.filter(status=2))
  class Meta:
    model = InstanceTask
    exclude = ('name', 'creator', 'updated', 'created', 'volume',
      'status', 'state', 'message', 'task_id', 'percent', 'disk',) class InstallationDisksForm(forms.Form):
  installation_disk = forms.ModelChoiceField(
    queryset=[],
    label='',
    required=False)
  
  def __init__(self, instance, *args, **kwargs):
    super(InstallationDisksForm, self).__init__(*args, **kwargs)
    self.fields['installation_disk'].initial = instance.disk
    self.fields['installation_disk'].queryset = instance.volume.storagepool.hypervisor.installationdisk_set.all() @task()
def create_instance(instancetask_name):
  try:
    instancetask = InstanceTask.objects.get(name=instancetask_name)
  except InstanceTask.DoesNotExist:
    return {'custum_state': 'FAILURE', 'msg': 'Unable to find InstanceTask with name: %s' % (instancetask_name)}

  try:
    network_address = instancetask.network.create_unique_address()
  except NoUniqueAddress:
    return {'custum_state': 'FAILURE', 'msg': 'Error creating instance: No unique address available on specified network (%s)' % (instancetask.network)}

  request = HttpRequest()
  storagepool = instancetask.storagepool.get_storagepool()
  if not storagepool:
    return {'custom_state': 'FAILURE', 'msg': 'Unable to get Storage Pool %s' % (instancetask.storagepool)}

  volume_name = Volume.create_random_name()
  (volume, created) = Volume.objects.get_or_create(
    name=volume_name,
    storagepool=instancetask.storagepool,
    capacity=instancetask.capacity
  )
  if created: volume.save()
  if not volume.create(request):
    volume.delete()
    return {'custom_state': 'FAILURE', 'msg': 'Unable to create Volume on %s' % (instancetask.storagepool)}
  instancetask.volume = volume
  instancetask.save()

  xml = INSTANCE_TEMPLATE % (instancetask.name, instancetask.memory.size, instancetask.memory.size, 
      instancetask.vcpu, volume.path(), volume.storagepool.hypervisor.address)
  print xml
  con = instancetask.storagepool.hypervisor.get_connection()
  if not con:
    return {'custom_state': 'FAILURE', 'msg': 'Unable to get Hypervisor %s' % (instancetask.storagepool.hypervisor)}
    
  try:
    con.defineXML(xml)
    current_task.update_state(state='PROGRESS', meta={
      'percent': 15,
      'msg': 'Defined XML'
    })
    instance = con.lookupByName(instancetask.name)
    instance.setAutostart(1)
    current_task.update_state(state='PROGRESS', meta={
      'percent': 50,
      'msg': 'Set Autostart',
    })
    instance.create()
    current_task.update_state(state='PROGRESS', meta={
      'percent': 75,
      'msg': 'Creating the Instance on the Hypervisor..',
    })

    # shutdown initially so that user can pick their own install medium
    instance.destroy()

    # switch instance task for an instance
    new_instance = Instance.objects.create(
      name=instancetask.name,
      volume=instancetask.volume,
      user=instancetask.user,
      creator=instancetask.creator,
      vcpu=instancetask.vcpu,
      memory=instancetask.memory,
      disk=instancetask.disk,
      created=instancetask.created,
      initialised=False,
    )
    new_instance.save()

    network_address.instance = new_instance
    network_address.save() 

    new_instance.attach_network(network_address, None, True)

    instancetask.delete(False)

  except libvirt.libvirtError as e:
    return {'custom_state': 'FAILURE', 'msg': 'Error while creating instance: %s' % (e)} class SnapshotForm(forms.Form):
  snapshot = forms.ModelChoiceField(
    queryset=[],
    label='',
    required=False)
  
  def __init__(self, instance, *args, **kwargs):
    super(SnapshotForm, self).__init__(*args, **kwargs)
    self.fields['snapshot'].queryset = Snapshot.objects.filter(instance=instance) def restore_snapshot(snapshot, request):
  instance = snapshot.instance.get_instance()
  snap = snapshot.get_snapshot()
  if snap and instance:
    try:
      instance.revertToSnapshot(snap, 0)
      messages.add_message(request, persistent_messages.SUCCESS, 'restored Snapshot %s' % (snapshot))
    except libvirt.libvirtError as e:
      messages.add_message(request, persistent_messages.ERROR, 'Unable to restore Snapshot %s: %s' % (snapshot, e))
  else:
    messages.add_message(request, persistent_messages.ERROR, 'Unable to restore Snapshot %s: unable to get snapshot or instance' % (snapshot, e)) class Snapshot(models.Model):
  name = models.CharField(max_length=100, default="Snapshot")
  creating = models.BooleanField(default=True)
  status = models.CharField(max_length=200, blank=True, null=True)
  instance = models.ForeignKey(Instance)
  created = models.DateTimeField(auto_now_add=True)

  def __str__(self):
    return unicode(self).encode('utf-8')

  def __unicode__(self):
    return "%s // %s" % (self.name, self.instance)

  def get_unixtime(self):
    return int(time.mktime(self.created.timetuple())*1000)

  def get_snapshot(self):
    instance = self.instance.get_instance()
    snapshot = None
    if instance:
      try:
        snapshot = instance.snapshotLookupByName("%s_%d" % (self.instance.name, self.get_unixtime()), 0)
      except libvirt.libvirtError:
        pass
    return snapshot class StoragePool(models.Model):
  name = models.CharField(max_length=100)
  hypervisor = models.ForeignKey(Hypervisor)
  path = models.CharField(max_length=255)
  # status choices
  INACTIVE = 0
  BUILDING = 1
  RUNNING = 2
  DEGRADED = 3
  INACCESSIBLE = 4
  LAST = 5
  NONE = 6
  STATUS_CHOICES = (
    (INACTIVE, 'Inactive'),
    (BUILDING, 'Buidling'),
    (RUNNING, 'Running'),
    (DEGRADED, 'Degraded'),
    (INACCESSIBLE, 'Inaccessible'),
    (LAST, 'Last'),
    (NONE, 'None'),
  )
  status = models.IntegerField(max_length=1, choices=STATUS_CHOICES, default=NONE)
  # more details
  capacity = models.BigIntegerField(default=0)
  allocated = models.BigIntegerField(default=0)
  percent = models.IntegerField(default=0)
  available = models.BigIntegerField(default=0)
  percent = models.DecimalField(max_digits=5, decimal_places=2, default=0.0)
  # updated
  updated = models.DateTimeField(auto_now=True)
  
  class Meta:
    unique_together = ('name', 'hypervisor')

  def __str__(self):
    return unicode(self).encode('utf-8')
  
  def __unicode__(self):
    return "%s [%s][%s]" % (self.name, self.hypervisor, self.get_status_display())

  def get_status_html(self):
    if self.status == 2:
      return '<span class="label label-success">%s</span>' % (self.get_status_display())
    return '<span class="label label-warning">%s</span>' % (self.get_status_display())

  def get_storagepool(self):
    storagepool = None
    try:
      if self.hypervisor.status == 'UP':
        conn = self.hypervisor.get_connection()
        if conn: storagepool = conn.storagePoolLookupByName(self.name)
          
    except libvirt.libvirtError:
      pass
    return storagepool

  def delete(self, request=None):
    storagepool = self.get_storagepool()
    if storagepool:
      if storagepool.info()[0] == 2:
        # delete any associated volumes
        for vol in storagepool.listVolumes():
          try:
            vol = storagepool.storageVolLookupByName(vol)
            vol.delete(0)
          except libvirt.libvirtError as e:
            if request:
              messages.add_message(request, persistent_messages.ERROR, 'Unable to delete Storage Pool Volume: %s' % (e))
            # try and continue
        # set pool to inactive
        try:
          storagepool.destroy()
        except libvirt.libvirtError as e:
          if request:
            messages.add_message(request, persistent_messages.ERROR, 
              'Unable to destroy Storage Pool: %s' % (e))
            return
      # delete and undefine the pool
      try:
        storagepool.delete(0)
        storagepool.undefine()
      except libvirt.libvirtError as e:
        if request:
          messages.add_message(request, persistent_messages.ERROR, 'Unable to delete Storage Pool: %s' % (e))
        return
      if request:
        messages.add_message(request, persistent_messages.SUCCESS,
          'Deleted Storage Pool: %s [%s][%s]' % (self.name, self.path, self.hypervisor))
      # delete the model
      super(StoragePool, self).delete()
    elif request:
      messages.add_message(request, persistent_messages.ERROR, 'Unable to delete Storage Pool: Unable to get Storage Pool %s' % (self))
    

  def update(self, force=False):
    # check if we have checked in the last minute
    if not force and (timezone.now() - self.updated) < timedelta(minutes=1): return
    storagepool = self.get_storagepool()
    if storagepool:
      
      (status, capacity, allocated, available) = storagepool.info()
      self.status = status
      self.capacity = capacity
      self.allocated = allocated
      self.available = available
      self.percent = ((float(allocated)/float(capacity))*100)
      self.save() class InstanceNetwork(object): pass class NetworkListForm(forms.Form):
  network = forms.ModelChoiceField(queryset=Network.objects.all()) class IPForm(forms.Form):
  ip = forms.ChoiceField(label='IP Address')
  network = forms.IntegerField(widget=forms.HiddenInput())

  def clean_model_instance(self):
    pk =  self.cleaned_data['model_instance']
    if not pk:
      raise forms.ValidationError()
    try:
      instance = Network.objects.get(pk=pk)
    except Network.DoesNotExist:
      raise forms.ValidationError()
    return instance def send_command(hypervisor, command, args):
def check_command(hypervisor, task_id):
def abort_command(hypervisor, task_id):
from django.template import RequestContext, loader
from django.shortcuts import get_object_or_404, render_to_response, redirect
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, Http404
from apps.instance.models import Instance, InstanceTask
from apps.instance.forms import InstanceTaskForm
from apps.installationdisk.forms import InstallationDisksForm
from apps.instance.tasks import create_instance
from apps.snapshot.forms import SnapshotForm
from apps.snapshot.tasks import restore_snapshot
from apps.snapshot.models import Snapshot
from apps.storagepool.models import StoragePool
from apps.network.models import InstanceNetwork
from apps.network.forms import NetworkListForm, IPForm
from django.contrib import messages
from xml.etree import ElementTree
from utils import node
import persistent_messages
import simplejson
import libvirt
import time

@staff_member_required
def search(request):
  search = 'Invalid search'
  rows = None
  if request.method == 'POST':
    search_type = int(request.POST.get('search-option'))
    search_text = request.POST.get('search-text')
    search = 'Searching for %s in' % (search_text)

    if search_type == 0:
      rows = Instance.objects.filter(alias__icontains=search_text)
      search += ' Alias'
    elif search_type == 1:
      rows = Instance.objects.filter(name__icontains=search_text)
      search += ' Name'
    elif search_type == 2:
      rows = Instance.objects.filter(user__email__icontains=search_text)
      search += ' User'
    elif search_type == 3:
      rows = Instance.objects.filter(creator__email__icontains=search_text)
      search += ' Creator'
    elif search_type == 4:
      rows = Instance.objects.filter(network__ip__icontains=search_text)
      search += ' IP'

  if search == 'Invalid search':
    rows = Instance.objects.filter()

  return render_to_response('instance/index.html',
    {
    'instances': rows,
    'search': search,
    },
    context_instance=RequestContext(request))

@staff_member_required
def add(request):
  form = InstanceTaskForm()

  if request.method == 'POST':
    form = InstanceTaskForm(request.POST)
    if form.is_valid():
      # first check if we are not overallocating
      hypervisor = form.cleaned_data['storagepool'].hypervisor
      instances = Instance.objects.filter(volume__storagepool__hypervisor=hypervisor)
      allocated_memory = 0
      allocated_vcpus = 0
      allocated_capacity = 0
      for i in StoragePool.objects.filter(hypervisor=hypervisor): 
        allocated_capacity += i.allocated
      for i in instances:
        allocated_memory += i.memory.size
        allocated_vcpus += i.vcpu
      if allocated_memory + form.cleaned_data['memory'].size > hypervisor.maximum_memory.size:
        messages.add_message(request, persistent_messages.ERROR,
          'Unable to create instance, <a href="/hypervisor/%d/">Hypervisor</a> has insufficient Memory available to allocate' % (hypervisor.id))
      elif allocated_vcpus + form.cleaned_data['vcpu'] > hypervisor.maximum_vcpus:
        messages.add_message(request, persistent_messages.ERROR,
          'Unable to create instance, <a href="/hypervisor/%d/">Hypervisor</a> has insufficient VCPUs available to allocate' % (hypervisor.id))
      elif allocated_capacity + form.cleaned_data['capacity'].size > hypervisor.maximum_hdd.size:
        messages.add_message(request, persistent_messages.ERROR,
          'Unable to create instance, <a href="/hypervisor/%d/">Hypervisor</a> has insufficient Disk space available to allocate' % (hypervisor.id))
      else:
        # end
        name = InstanceTask.get_random_name()
        (instancetask, created) = InstanceTask.objects.get_or_create(
          name=name,
          user=form.cleaned_data['user'],
          creator=request.user,
          vcpu=form.cleaned_data['vcpu'],
          memory=form.cleaned_data['memory'],
          capacity=form.cleaned_data['capacity'],
          storagepool=form.cleaned_data['storagepool'],
          network=form.cleaned_data['network'],
        )
        if created: instancetask.save()
        task = create_instance.delay(instancetask.name)
        instancetask.task_id = task.id
        instancetask.save()
        messages.add_message(request, persistent_messages.INFO,
          'Attempting to create Instance: %s' % (instancetask))
        return redirect('/instance/')

  return render_to_response('instance/add.html', {
      'form': form,
    },
    context_instance=RequestContext(request))

@staff_member_required
def delete(request, name):
  instance = get_object_or_404(Instance, name=name)
  instance.delete(request)
  return redirect('/instance/')

@staff_member_required
def delete_task(request, pk):
  task = get_object_or_404(InstanceTask, pk=pk)
  task.abort(request)
  persistent_messages.add_message(request, persistent_messages.INFO,
    'Delete Instance creating task of %s on %s' % (task.name, task.storagepool.hypervisor), user=task.creator)
  task.delete(request)
  return redirect('/instance/')

def network_delete(request, pk):
  address = get_object_or_404(InstanceNetwork, pk=pk)
  instance = address.instance
  if not request.user.is_staff and request.user != instance.user:
    raise Http404
  instance.detach_network(address)
  return redirect('/instance/%s/' % (instance.name))

def network_add(request, name):
  instance = get_object_or_404(Instance, name=name)
  if not request.user.is_staff and request.user != instance.user:
    raise Http404
  form = NetworkListForm()
  ip_form = None
  form['network'].queryset = instance.volume.storagepool.hypervisor.network_set.all()
  if request.method == 'POST':

    form = NetworkListForm(request.POST) # needed in both cases