Skip to content
This repository has been archived by the owner on Apr 7, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1492 from mfalesni/sprout-quotas
Browse files Browse the repository at this point in the history
Sprout quotas + dashboard
  • Loading branch information
Milan Falešník committed Jan 8, 2015
2 parents 88a1498 + 27116f7 commit ea888e0
Show file tree
Hide file tree
Showing 61 changed files with 2,040 additions and 70 deletions.
8 changes: 6 additions & 2 deletions sprout/appliances/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,18 @@ def request_pool(self, request, groups):

@register_for(Provider)
class ProviderAdmin(Admin):
readonly_fields = ["remaining_provisioning_slots", "provisioning_load", "show_ip_address"]
readonly_fields = [
"remaining_provisioning_slots", "provisioning_load", "show_ip_address", "appliance_load"]
list_display = [
"id", "working", "num_simultaneous_provisioning", "remaining_provisioning_slots",
"provisioning_load", "show_ip_address"]
"provisioning_load", "show_ip_address", "appliance_load"]

def remaining_provisioning_slots(self, instance):
return str(instance.remaining_provisioning_slots)

def appliance_load(self, instance):
return "{0:.2f}%".format(round(instance.appliance_load * 100.0, 2))

def provisioning_load(self, instance):
return "{0:.2f}%".format(round(instance.provisioning_load * 100.0, 2))

Expand Down
4 changes: 4 additions & 0 deletions sprout/appliances/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ def __call__(self, request):
jsonapi = JSONApi()


def jsonapi_doc(*args, **kwargs):
return jsonapi.doc(*args, **kwargs)


def apply_if_not_none(o, meth, *args, **kwargs):
if o is None:
return None
Expand Down
6 changes: 6 additions & 0 deletions sprout/appliances/context_processors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from sprout import settings


def hubber_url(request):
return {'hubber_url': settings.HUBBER_URL}
21 changes: 21 additions & 0 deletions sprout/appliances/migrations/0004_provider_appliance_limit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('appliances', '0003_auto_20141218_1534'),
]

operations = [
migrations.AddField(
model_name='provider',
name='appliance_limit',
field=models.IntegerField(
help_text=b'Hard limit of how many appliances can run on this provider', null=True),
preserve_default=True,
),
]
75 changes: 70 additions & 5 deletions sprout/appliances/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class Provider(models.Model):
working = models.BooleanField(default=False, help_text="Whether provider is available.")
num_simultaneous_provisioning = models.IntegerField(default=5,
help_text="How many simultaneous background provisioning tasks can run on this provider.")
appliance_limit = models.IntegerField(
null=True, help_text="Hard limit of how many appliances can run on this provider")

@property
def api(self):
Expand All @@ -42,13 +44,27 @@ def num_currently_provisioning(self):
Appliance.objects.filter(
ready=False, marked_for_deletion=False, template__provider=self, ip_address=None))

@property
def num_currently_managing(self):
return len(Appliance.objects.filter(template__provider=self))

@property
def currently_managed_appliances(self):
return Appliance.objects.filter(template__provider=self)

@property
def remaining_provisioning_slots(self):
result = self.num_simultaneous_provisioning - self.num_currently_provisioning
if result < 0:
return 0
else:
# Take the appliance limit into account
if self.appliance_limit is None:
return result
else:
free_appl_slots = self.appliance_limit - self.num_currently_managing
if free_appl_slots < 0:
free_appl_slots = 0
return min(free_appl_slots, result)

@property
def free(self):
Expand All @@ -60,6 +76,20 @@ def provisioning_load(self):
return 1.0 # prevent division by zero
return float(self.num_currently_provisioning) / float(self.num_simultaneous_provisioning)

@property
def appliance_load(self):
if self.appliance_limit is None:
return 0.0
return float(self.num_currently_managing) / float(self.appliance_limit)

@property
def load(self):
"""Load for sorting"""
if self.appliance_limit is None:
return self.provisioning_load
else:
return self.appliance_load

@classmethod
def get_available_provider_keys(cls):
return cfme_data.get("management_systems", {}).keys()
Expand Down Expand Up @@ -178,6 +208,9 @@ class Power(object):
"PoweredOff": Power.OFF,
"Stopped": Power.OFF,
"Paused": Power.SUSPENDED,
# EC2 (for VM manager)
"stopped": Power.OFF,
"running": Power.ON,
}
template = models.ForeignKey(Template, help_text="Appliance's source template.")
appliance_pool = models.ForeignKey("AppliancePool", null=True,
Expand Down Expand Up @@ -314,6 +347,38 @@ def owner(self):
else:
return self.appliance_pool.owner

@property
def expires_in(self):
"""Minutes"""
if self.leased_until is None:
return "never"
minutes = (self.leased_until - timezone.now()).total_seconds() / 60.0
if minutes <= 1.0 and minutes > 0.0:
return "Less than one minute!"
elif minutes <= 0.0:
return "Expired!"
else:
return "{} minutes.".format(int(minutes))

@property
def can_launch(self):
return self.power_state in {self.Power.OFF, self.Power.SUSPENDED}

@property
def can_suspend(self):
return self.power_state in {self.Power.ON}

@property
def can_stop(self):
return self.power_state in {self.Power.ON}

@property
def version(self):
if self.template.version is None:
return "---"
else:
return self.template.version


class AppliancePool(models.Model):
total_count = models.IntegerField(help_text="How many appliances should be in this pool.")
Expand All @@ -327,14 +392,14 @@ def create(cls, owner, group, version=None, date=None, num_appliances=1, time_le
from appliances.tasks import request_appliance_pool
# Retrieve latest possible
if not version:
versions = Template.get_versions(template_group=group)
versions = Template.get_versions(template_group=group, ready=True)
if versions:
version = versions[0]
if not date:
if version is not None:
dates = Template.get_dates(template_group=group, version=version)
dates = Template.get_dates(template_group=group, version=version, ready=True)
else:
dates = Template.get_dates(template_group=group)
dates = Template.get_dates(template_group=group, ready=True)
if dates:
date = dates[0]
if isinstance(group, basestring):
Expand Down Expand Up @@ -375,7 +440,7 @@ def possible_provisioning_templates(self):

@property
def appliances(self):
return Appliance.objects.filter(appliance_pool=self).all()
return Appliance.objects.filter(appliance_pool=self).order_by("id").all()

@property
def current_count(self):
Expand Down
7 changes: 7 additions & 0 deletions sprout/appliances/static/css/bootstrap-select.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ea888e0

Please sign in to comment.