Commit 327e7892 by Szabolcs Gelencser

Implement start vm

parent 55430047
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>
\ No newline at end of file
No preview for this file type
...@@ -20,6 +20,7 @@ from __future__ import absolute_import ...@@ -20,6 +20,7 @@ from __future__ import absolute_import
from datetime import timedelta from datetime import timedelta
from urlparse import urlparse from urlparse import urlparse
import openstack_api
import pyotp import pyotp
from django.forms import ModelForm from django.forms import ModelForm
...@@ -107,6 +108,47 @@ class VmSaveForm(OperationForm): ...@@ -107,6 +108,47 @@ class VmSaveForm(OperationForm):
help_text=_("Clone the access list of parent template. Useful " help_text=_("Clone the access list of parent template. Useful "
"for updating a template.")) "for updating a template."))
class VmFromPlainImageForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={
'class': "form-control",
'required': "",
}))
image = forms.ChoiceField([], widget=forms.Select(attrs={
'class': "form-control input-tags",
}))
flavor = forms.ChoiceField([], widget=forms.Select(attrs={
'class': "form-control input-tags",
}))
network = forms.ChoiceField([], widget=forms.Select(attrs={
'class': "form-control input-tags",
}))
def __init__(self, request, *args, **kwargs):
super(VmFromPlainImageForm, self).__init__(*args, **kwargs)
images = openstack_api.glance.image_list_detailed(request)[0] #TODO: flatten?
def sizeof_fmt(num, suffix='B'):
for unit in ['', 'K', 'M', 'G', 'T']:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
self.fields['image'].choices = (
(image.id, '%s - %s' % (image.name, sizeof_fmt(image.size))) for image in images
)
flavors = openstack_api.nova.flavor_list(request) #TODO: flattent
self.fields['flavor'].choices = (
(flavor.id, '%s - %s CPUs, %s MB RAM' % (flavor.name, flavor.vcpus, flavor.ram)) for flavor in flavors
)
networks = openstack_api.neutron.network_list_for_tenant(request, request.user.tenant_id)
self.fields['network'].choices = (
(network.id, '%s' % (network.name)) for network in networks
)
class VmCustomizeForm(forms.Form): class VmCustomizeForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={ name = forms.CharField(widget=forms.TextInput(attrs={
......
{% load sizefieldtags %} {% load sizefieldtags %}
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %}
<div class="vm-create-template-list"> <div class="vm-create-template-list">
{% for t in templates %} {% for t in templates %}
...@@ -87,9 +88,20 @@ ...@@ -87,9 +88,20 @@
{% include "request/_request-template-form.html" %} {% include "request/_request-template-form.html" %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<div class="vm-create-template">
<a href="{% url "dashboard.views.vm-plain-image-create" %}" style="text-decoration: none; color: #333333">
<div class="vm-create-template-summary">
<span class="vm-create-list-name">
New VM from plain OS image
</span>
<div class="clearfix"></div>
</div>
</a>
</div>
</div> </div>
{% if templates and template_access_types %} {% if templates and template_access_types %}
{% url "request.views.request-template" as request_url %} {% url "request.views.request-template" as request_url %}
<hr /> <hr />
<p class="text-right"> <p class="text-right">
...@@ -97,7 +109,7 @@ ...@@ -97,7 +109,7 @@
Need other templates? Submit a new <a href="{{ url }}">request</a>. Need other templates? Submit a new <a href="{{ url }}">request</a>.
{% endblocktrans %} {% endblocktrans %}
</p> </p>
{% endif %} {% endif %}
<style> <style>
.progress { .progress {
......
{% extends "dashboard/base.html" %}
{% load sizefieldtags %}
{% load i18n %}
{% load crispy_forms_tags %}
{% load staticfiles %}
{% block content %}
<form method="POST" action="{% url "dashboard.views.vm-plain-image-create" %} ">
{% csrf_token %}
{{ form.name|as_crispy_field }}
{{ form.image|as_crispy_field }}
{{ form.flavor|as_crispy_field }}
{# {{ form.network|as_crispy_field }}#}
<button class="btn btn-success btn-xs vm-create-start pull-right text-right" type="submit">
<i class="fa fa-play"></i> {% trans "Start" %}
</button>
</form>
{% endblock %}
\ No newline at end of file
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from dashboard.views.autocomplete import AclUserGroupAutocomplete, AclUserAutocomplete from dashboard.views.autocomplete import AclUserGroupAutocomplete, AclUserAutocomplete
from dashboard.views.vm import VmDetailView, VmList, VmCreate, vm_activity, vm_ops, FavouriteView from dashboard.views.vm import VmDetailView, VmList, VmCreate, vm_activity, vm_ops, FavouriteView, VmPlainImageCreate
from django.conf.urls import url from django.conf.urls import url
from .views import ( from .views import (
...@@ -55,7 +55,7 @@ urlpatterns = [ ...@@ -55,7 +55,7 @@ urlpatterns = [
# name="dashboard.views.template-delete"), # name="dashboard.views.template-delete"),
# url(r'^template/(?P<pk>\d+)/tx/$', TransferTemplateOwnershipView.as_view(), # url(r'^template/(?P<pk>\d+)/tx/$', TransferTemplateOwnershipView.as_view(),
# name='dashboard.views.template-transfer-ownership'), # name='dashboard.views.template-transfer-ownership'),
url(r'^vm/(?P<pk>\d+)/$', VmDetailView.as_view(), url(r'^vm/(?P<pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/$', VmDetailView.as_view(),
name='dashboard.views.detail'), name='dashboard.views.detail'),
# url(r'^vm/(?P<pk>\d+)/vnctoken/$', VmDetailVncTokenView.as_view(), # url(r'^vm/(?P<pk>\d+)/vnctoken/$', VmDetailVncTokenView.as_view(),
# name='dashboard.views.detail-vnc'), # name='dashboard.views.detail-vnc'),
...@@ -64,9 +64,9 @@ urlpatterns = [ ...@@ -64,9 +64,9 @@ urlpatterns = [
# url(r'^vm/(?P<pk>\d+)/tx/$', TransferInstanceOwnershipView.as_view(), # url(r'^vm/(?P<pk>\d+)/tx/$', TransferInstanceOwnershipView.as_view(),
# name='dashboard.views.vm-transfer-ownership'), # name='dashboard.views.vm-transfer-ownership'),
url(r'^vm/list/$', VmList.as_view(), name='dashboard.views.vm-list'), url(r'^vm/list/$', VmList.as_view(), name='dashboard.views.vm-list'),
url(r'^vm/create/$', VmCreate.as_view(), url(r'^vm/create/$', VmCreate.as_view(), name='dashboard.views.vm-create'),
name='dashboard.views.vm-create'), url(r'^vm-plain-image-create$', VmPlainImageCreate.as_view(), name='dashboard.views.vm-plain-image-create'),
url(r'^vm/(?P<pk>\d+)/activity/$', vm_activity, url(r'^vm/(?P<pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/activity/$', vm_activity,
name='dashboard.views.vm-activity-list'), name='dashboard.views.vm-activity-list'),
# url(r'^vm/activity/(?P<pk>\d+)/$', InstanceActivityDetail.as_view(), # url(r'^vm/activity/(?P<pk>\d+)/$', InstanceActivityDetail.as_view(),
# name='dashboard.views.vm-activity'), # name='dashboard.views.vm-activity'),
...@@ -223,7 +223,7 @@ urlpatterns = [ ...@@ -223,7 +223,7 @@ urlpatterns = [
] ]
urlpatterns += [ urlpatterns += [
url(r'^vm/(?P<pk>\d+)/op/%s/$' % op, v.as_view(), name=v.get_urlname()) url(r'^vm/(?P<pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/op/%s/$' % op, v.as_view(), name=v.get_urlname())
for op, v in vm_ops.iteritems() for op, v in vm_ops.iteritems()
] ]
# #
......
...@@ -105,6 +105,9 @@ class IndexView(LoginRequiredMixin, TemplateView): ...@@ -105,6 +105,9 @@ class IndexView(LoginRequiredMixin, TemplateView):
context['templates'] = InstanceTemplate.get_objects_with_level( context['templates'] = InstanceTemplate.get_objects_with_level(
'operator', user, disregard_superuser=True).all()[:5] 'operator', user, disregard_superuser=True).all()[:5]
# vxlan
#context['vxlans'] = Instance.list_from_os(self.request)[:5]
# toplist # toplist
if settings.STORE_URL: if settings.STORE_URL:
cache_key = "files-%d" % self.request.user.pk cache_key = "files-%d" % self.request.user.pk
......
...@@ -42,6 +42,7 @@ from django.views.decorators.csrf import csrf_protect ...@@ -42,6 +42,7 @@ from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import DetailView, View, DeleteView, FormView from django.views.generic import DetailView, View, DeleteView, FormView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from vm.models import Instance
from ..models import GroupProfile from ..models import GroupProfile
...@@ -238,6 +239,9 @@ class OperationView(RedirectToLoginMixin, DetailView): ...@@ -238,6 +239,9 @@ class OperationView(RedirectToLoginMixin, DetailView):
else: else:
return ['dashboard/_base.html'] return ['dashboard/_base.html']
def get_object(self):
return Instance(os_server_id=self.kwargs['pk']).get_from_os(self.request)
@classmethod @classmethod
def get_op_by_object(cls, obj): def get_op_by_object(cls, obj):
return getattr(obj, cls.op) return getattr(obj, cls.op)
......
...@@ -48,17 +48,19 @@ from common.models import ( ...@@ -48,17 +48,19 @@ from common.models import (
) )
from firewall.models import Vlan, Host, Rule from firewall.models import Vlan, Host, Rule
from manager.scheduler import SchedulerError from manager.scheduler import SchedulerError
from request.forms import TemplateRequestForm
from request.models import TemplateAccessType
from storage.models import Disk from storage.models import Disk
from vm.models import ( from vm.models import (
Instance, InstanceActivity, Interface, Instance, InstanceActivity, Interface,
) InstanceTemplate)
from .util import ( from .util import (
CheckedDetailView, AjaxOperationMixin, OperationView, AclUpdateView, CheckedDetailView, AjaxOperationMixin, OperationView, AclUpdateView,
FormOperationMixin, FilterMixin, GraphMixin FormOperationMixin, FilterMixin, GraphMixin
) )
from ..forms import ( from ..forms import (
AclUserOrGroupAddForm, VmResourcesForm, VmCustomizeForm, VmDeployForm) AclUserOrGroupAddForm, VmResourcesForm, VmCustomizeForm, VmDeployForm, VmFromPlainImageForm)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -105,7 +107,7 @@ class VmDetailView(LoginRequiredMixin, GraphMixin, DetailView): ...@@ -105,7 +107,7 @@ class VmDetailView(LoginRequiredMixin, GraphMixin, DetailView):
"password": instance.pw} "password": instance.pw}
def get_object(self, queryset=None): def get_object(self, queryset=None):
return super(VmDetailView, self).get_object(queryset).get_from_os(self.request) return Instance(os_server_id=self.kwargs['pk']).get_from_os(self.request)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(VmDetailView, self).get_context_data(**kwargs) context = super(VmDetailView, self).get_context_data(**kwargs)
...@@ -1007,6 +1009,28 @@ class VmList(LoginRequiredMixin, FilterMixin, ListView): ...@@ -1007,6 +1009,28 @@ class VmList(LoginRequiredMixin, FilterMixin, ListView):
"interface_set__host").distinct() "interface_set__host").distinct()
class VmPlainImageCreate(LoginRequiredMixin, TemplateView):
form_class = VmFromPlainImageForm
template_name = "dashboard/vm-plain-image-create.html"
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
context.update({
'form': VmFromPlainImageForm(request),
})
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
server_created = openstack_api.nova.server_create(
request,
request.POST.get("name"),
request.POST.get("image"),
request.POST.get("flavor"),
)
return HttpResponseRedirect("vm/%s#activity" % server_created.id)
class VmCreate(LoginRequiredMixin, TemplateView): class VmCreate(LoginRequiredMixin, TemplateView):
form_class = VmCustomizeForm form_class = VmCustomizeForm
...@@ -1030,9 +1054,6 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -1030,9 +1054,6 @@ class VmCreate(LoginRequiredMixin, TemplateView):
return template return template
def get(self, request, form=None, *args, **kwargs): def get(self, request, form=None, *args, **kwargs):
if not request.user.has_perm('vm.create_vm'):
raise PermissionDenied()
if form is None: if form is None:
template_pk = request.GET.get("template") template_pk = request.GET.get("template")
else: else:
...@@ -1043,8 +1064,9 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -1043,8 +1064,9 @@ class VmCreate(LoginRequiredMixin, TemplateView):
if form is None: if form is None:
form = self.form_class(user=request.user, template=template) form = self.form_class(user=request.user, template=template)
else: else:
templates = InstanceTemplate.get_objects_with_level( templates = InstanceTemplate.objects
'user', request.user, disregard_superuser=True)
images = openstack_api.glance.image_list_detailed(request)
context = self.get_context_data(**kwargs) context = self.get_context_data(**kwargs)
if template_pk: if template_pk:
...@@ -1062,17 +1084,20 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -1062,17 +1084,20 @@ class VmCreate(LoginRequiredMixin, TemplateView):
'ajax_title': True, 'ajax_title': True,
'templates': templates.all(), 'templates': templates.all(),
'template_access_types': TemplateAccessType.objects.exists(), 'template_access_types': TemplateAccessType.objects.exists(),
'form': TemplateRequestForm(request=request), 'form': TemplateRequestForm(request=request)
}) })
return self.render_to_response(context) return self.render_to_response(context)
def __create_normal(self, request, template, *args, **kwargs): def _create_from_plain_image(self, request, *args, **kwargs):
pass
def __create_normal(self, request, *args, **kwargs):
instances = [Instance.create_from_template( instances = [Instance.create_from_template(
template=template, template=template,
owner=request.user)] owner=request.user)]
return self.__deploy(request, instances) return self.__deploy(request, instances)
def __create_customized(self, request, template, *args, **kwargs): def __create_customized(self, request, *args, **kwargs):
user = request.user user = request.user
# no form yet, using POST directly: # no form yet, using POST directly:
form = self.form_class( form = self.form_class(
...@@ -1130,39 +1155,16 @@ class VmCreate(LoginRequiredMixin, TemplateView): ...@@ -1130,39 +1155,16 @@ class VmCreate(LoginRequiredMixin, TemplateView):
return HttpResponseRedirect("%s#activity" % path) return HttpResponseRedirect("%s#activity" % path)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
user = request.user #TODO: check if user can create VM
#TODO: limit chekcs
if not request.user.has_perm('vm.create_vm'):
raise PermissionDenied()
template = self.get_template(request, request.POST.get("template"))
# limit chekcs
try:
limit = user.profile.instance_limit
except Exception as e:
logger.debug('No profile or instance limit: %s', e)
else:
try:
amount = int(request.POST.get("amount", 1))
except:
amount = limit # TODO this should definitely use a Form
current = Instance.active.filter(owner=user).count()
logger.debug('current use: %d, limit: %d', current, limit)
if current + amount > limit:
messages.error(request,
_('Instance limit (%d) exceeded.') % limit)
if request.is_ajax():
return HttpResponse(json.dumps({'redirect': '/'}),
content_type="application/json")
else:
return redirect('/')
create_func = (self.__create_normal if create_func = (self._create_from_plain_image if
request.POST.get("from_plain_image") is not None else
self.__create_normal if
request.POST.get("customized") is None else request.POST.get("customized") is None else
self.__create_customized) self.__create_customized)
return create_func(request, template, *args, **kwargs) return create_func(request, *args, **kwargs)
# @require_GET # @require_GET
......
...@@ -362,6 +362,7 @@ class VxlanSuperUserForm(ModelForm): ...@@ -362,6 +362,7 @@ class VxlanSuperUserForm(ModelForm):
'vlan', 'vlan',
'description', 'description',
'comment', 'comment',
'owner',
) )
), ),
FormActions( FormActions(
...@@ -371,11 +372,6 @@ class VxlanSuperUserForm(ModelForm): ...@@ -371,11 +372,6 @@ class VxlanSuperUserForm(ModelForm):
) )
) )
class Meta:
model = Vxlan
fields = ('name', 'vni', 'vlan', 'description', 'comment', )
class VxlanForm(ModelForm): class VxlanForm(ModelForm):
helper = FormHelper() helper = FormHelper()
helper.layout = Layout( helper.layout = Layout(
...@@ -394,7 +390,3 @@ class VxlanForm(ModelForm): ...@@ -394,7 +390,3 @@ class VxlanForm(ModelForm):
'network.vxlan-list')) 'network.vxlan-list'))
) )
) )
class Meta:
model = Vxlan
fields = ('name', 'description', 'comment', 'vni', )
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
from django.db import models from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import CharField
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib.contenttypes.fields import ( from django.contrib.contenttypes.fields import (
GenericRelation, GenericForeignKey GenericRelation, GenericForeignKey
...@@ -60,53 +61,12 @@ class Vxlan(models.Model): ...@@ -60,53 +61,12 @@ class Vxlan(models.Model):
""" """
A virtual L2 network, A virtual L2 network,
These networks are isolated by the vxlan (virtual extensible lan)
technology, which is commonly used by managed network switches
to partition the network, with a more scalable way opposite vlan
technology. Usually, it used over vlan networks.
Each vxlan network has a unique identifier (VNI), a name, and
a server vlan network.
""" """
os_network_id = CharField(blank=False, max_length=100, unique=True)
# NOTE: VXLAN VNI's maximal value is 2^24-1, but MAC address generator
# only supports 2^12-1 maximal value.
vni = models.IntegerField(unique=True,
verbose_name=_('VNI'),
help_text=_('VXLAN Network Identifier.'),
validators=[MinValueValidator(0),
MaxValueValidator(2 ** 12 - 1)])
vlan = models.ForeignKey(Vlan,
verbose_name=_('vlan'),
help_text=_('The server vlan.'))
name = models.CharField(max_length=20,
verbose_name=_('Name'),
help_text=_('The short name of the '
'virtual network.'),
validators=[val_alfanum])
description = models.TextField(blank=True, verbose_name=_('description'),
help_text=_(
'Description of the goals and elements '
'of the virtual network.'))
comment = models.TextField(blank=True,
verbose_name=_('comment'),
help_text=_(
'Notes, comments about the network'))
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created at'))
modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified at'))
editor_elements = GenericRelation(EditorElement) editor_elements = GenericRelation(EditorElement)
class Meta: class Meta:
app_label = 'network' app_label = 'network'
verbose_name = _("vxlan")
verbose_name_plural = _("vxlans")
ordering = ('vni', )
permissions = (
('create_vxlan', _('Can create a Vxlan network.')),
)
def __unicode__(self): def __unicode__(self):
return self.name return self.name
......
...@@ -33,10 +33,10 @@ Keystone/Nova/Glance/Swift et. al. ...@@ -33,10 +33,10 @@ Keystone/Nova/Glance/Swift et. al.
""" """
from openstack_api import base from openstack_api import base
# from openstack_api import cinder # from openstack_api import cinder
# from openstack_api import glance from openstack_api import glance
# from openstack_api import keystone # from openstack_api import keystone
# from openstack_api import network # from openstack_api import network
# from openstack_api import neutron from openstack_api import neutron
from openstack_api import nova from openstack_api import nova
# from openstack_api import swift # from openstack_api import swift
......
...@@ -506,16 +506,16 @@ def keypair_get(request, name): ...@@ -506,16 +506,16 @@ def keypair_get(request, name):
def server_create(request, name, image, flavor, key_name, user_data, def server_create(request, name, image, flavor, key_name=None, user_data=None,
security_groups, block_device_mapping=None, security_groups=None, block_device_mapping=None,
block_device_mapping_v2=None, nics=None, block_device_mapping_v2=None, nics="auto",
availability_zone=None, instance_count=1, admin_pass=None, availability_zone=None, instance_count=1, admin_pass=None,
disk_config=None, config_drive=None, meta=None, disk_config=None, config_drive=None, meta=None,
scheduler_hints=None, description=None): scheduler_hints=None, description=None):
kwargs = {} kwargs = {}
if description is not None: if description is not None:
kwargs['description'] = description kwargs['description'] = description
return InstanceServer(get_novaclient_with_instance_desc(request).servers.create( return Server(get_novaclient_with_instance_desc(request).servers.create(
name.strip(), image, flavor, userdata=user_data, name.strip(), image, flavor, userdata=user_data,
security_groups=security_groups, security_groups=security_groups,
key_name=key_name, block_device_mapping=block_device_mapping, key_name=key_name, block_device_mapping=block_device_mapping,
......
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from openstack_api.utils import settings as utils_settings
def check(actions, request, target=None):
"""Wrapper of the configurable policy method."""
policy_check = utils_settings.import_setting("POLICY_CHECK_FUNCTION")
if policy_check:
return policy_check(actions, request, target)
return True
class PolicyTargetMixin(object):
"""Mixin that adds the get_policy_target function
policy_target_attrs - a tuple of tuples which defines
the relationship between attributes in the policy
target dict and attributes in the passed datum object.
policy_target_attrs can be overwritten by sub-classes
which do not use the default, so they can neatly define
their policy target information, without overriding the
entire get_policy_target function.
"""
policy_target_attrs = (("project_id", "tenant_id"),
("tenant_id", "tenant_id"),
("user_id", "user_id"),
("domain_id", "domain_id"),
("target.project.domain_id", "domain_id"),
("target.user.domain_id", "domain_id"),
("target.group.domain_id", "domain_id"))
def get_policy_target(self, request, datum=None):
policy_target = {}
for policy_attr, datum_attr in self.policy_target_attrs:
if datum:
policy_target[policy_attr] = getattr(datum, datum_attr, None)
else:
policy_target[policy_attr] = None
return policy_target
...@@ -29,7 +29,7 @@ from .views import ( ...@@ -29,7 +29,7 @@ from .views import (
urlpatterns = [ urlpatterns = [
url(r'^list/$', RequestList.as_view(), url(r'^list/$', RequestList.as_view(),
name="request.views.request-list"), name="request.views.request-list"),
url(r'^(?P<pk>\d+)/$', RequestDetail.as_view(), url(r'^(?P<pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/$', RequestDetail.as_view(),
name="request.views.request-detail"), name="request.views.request-detail"),
url(r'^type/list/$', RequestTypeList.as_view(), url(r'^type/list/$', RequestTypeList.as_view(),
...@@ -55,10 +55,10 @@ urlpatterns = [ ...@@ -55,10 +55,10 @@ urlpatterns = [
# request views (visible for users) # request views (visible for users)
url(r'template/$', TemplateRequestView.as_view(), url(r'template/$', TemplateRequestView.as_view(),
name="request.views.request-template"), name="request.views.request-template"),
url(r'lease/(?P<vm_pk>\d+)/$', LeaseRequestView.as_view(), url(r'lease/(?P<vm_pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/$', LeaseRequestView.as_view(),
name="request.views.request-lease"), name="request.views.request-lease"),
url(r'resource/(?P<vm_pk>\d+)/$', ResourceRequestView.as_view(), url(r'resource/(?P<vm_pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/$', ResourceRequestView.as_view(),
name="request.views.request-resource"), name="request.views.request-resource"),
url(r'resize/(?P<vm_pk>\d+)/(?P<disk_pk>\d+)/$', url(r'resize/(?P<vm_pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/(?P<disk_pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/$',
ResizeRequestView.as_view(), name="request.views.request-resize"), ResizeRequestView.as_view(), name="request.views.request-resize"),
] ]
...@@ -305,6 +305,10 @@ class Instance(OperatedMixin, TimeStampedModel): ...@@ -305,6 +305,10 @@ class Instance(OperatedMixin, TimeStampedModel):
return " ".join(s for s in parts if s != "") return " ".join(s for s in parts if s != "")
@property @property
def pk(self):
return self.os_server_id
@property
def name(self): def name(self):
return self._os_server.name return self._os_server.name
...@@ -470,7 +474,7 @@ class Instance(OperatedMixin, TimeStampedModel): ...@@ -470,7 +474,7 @@ class Instance(OperatedMixin, TimeStampedModel):
@permalink @permalink
def get_absolute_url(self): def get_absolute_url(self):
return ('dashboard.views.detail', None, {'pk': self.id}) return ('dashboard.views.detail', None, {'pk': self.os_server_id})
@property @property
def vm_name(self): def vm_name(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment