Commit 3023abfd authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

Merge branch 'feature-disk-templates' into develop

parents 522fb634 20d0ce4b
......@@ -60,6 +60,9 @@ Cyclades
setting.
* Change --dhcp option of network management commands from a flag to a boolean
value, e.g. --dhcp=True
* Remove 'ARCHIPELAGO_BACKENDS' setting used to distinquish between backends
that hosted only archipelago backends. Instead allocation is based on which
disk-templates are enabled in each backend.
Pithos
------
......
......@@ -62,9 +62,6 @@
## e.g. BACKEND_PER_USER = {'example@synnefo.org': 2}
#BACKEND_PER_USER = {}
#
## List of backend IDs used *only* for archipelago.
#ARCHIPELAGO_BACKENDS = []
#
#
## URL templates for the stat graphs.
## The API implementation replaces '%s' with the encrypted backend id.
......
......@@ -61,9 +61,6 @@ DEFAULT_FIREWALL_PROFILE = 'DISABLED'
# e.g. BACKEND_PER_USER = {'example@synnefo.org': 2}
BACKEND_PER_USER = {}
# List of backend IDs used *only* for archipelago.
ARCHIPELAGO_BACKENDS = []
# URL templates for the stat graphs.
# The API implementation replaces '%s' with the encrypted backend id.
......
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of GRNET S.A.
from django.db import models
from south.modelsinspector import add_introspection_rules
class SeparatedValuesField(models.TextField):
description = ("Stores list of values as a TextField,"
" separated by a delimiter.")
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
self.delimiter = kwargs.pop('delimiter', ',')
super(SeparatedValuesField, self).__init__(*args, **kwargs)
def to_python(self, value):
if not value:
return
if isinstance(value, list):
return value
return value.split(self.delimiter)
def get_db_prep_value(self, value):
if not value:
return
assert(isinstance(value, list) or isinstance(value, tuple))
return self.delimiter.join([unicode(s) for s in value])
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)
add_introspection_rules([
(
[SeparatedValuesField], # Class(es) these apply to
[], # Positional arguments (not used)
{ # Keyword argument
"delimiter": ["delimiter", {"default": ","}],
},
),
], ["^synnefo\.db\.fields\.SeparatedValuesField"])
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Backend.disk_templates'
db.add_column('db_backend', 'disk_templates',
self.gf('synnefo.db.fields.SeparatedValuesField')(null=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Backend.disk_templates'
db.delete_column('db_backend', 'disk_templates')
models = {
'db.backend': {
'Meta': {'ordering': "['clustername']", 'object_name': 'Backend'},
'clustername': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'ctotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'dfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'disk_templates': ('synnefo.db.fields.SeparatedValuesField', [], {'null': 'True'}),
'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'dtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'hash': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'hypervisor': ('django.db.models.fields.CharField', [], {'default': "'kvm'", 'max_length': '32'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'index': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'unique': 'True'}),
'mfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'mtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'offline': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'password_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
'pinst_cnt': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5080'}),
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'})
},
'db.backendnetwork': {
'Meta': {'unique_together': "(('network', 'backend'),)", 'object_name': 'BackendNetwork'},
'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'networks'", 'to': "orm['db.Backend']"}),
'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'backend_networks'", 'to': "orm['db.Network']"}),
'operstate': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '30'}),
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'db.bridgepooltable': {
'Meta': {'object_name': 'BridgePoolTable'},
'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
'size': ('django.db.models.fields.IntegerField', [], {})
},
'db.flavor': {
'Meta': {'unique_together': "(('cpu', 'ram', 'disk', 'disk_template'),)", 'object_name': 'Flavor'},
'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'disk_template': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'db.floatingip': {
'Meta': {'object_name': 'FloatingIP'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15', 'db_index': 'True'}),
'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'floating_ips'", 'null': 'True', 'to': "orm['db.VirtualMachine']"}),
'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'floating_ips'", 'to': "orm['db.Network']"}),
'serial': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'floating_ips'", 'null': 'True', 'to': "orm['db.QuotaHolderSerial']"}),
'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
},
'db.ippooltable': {
'Meta': {'object_name': 'IPPoolTable'},
'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
'size': ('django.db.models.fields.IntegerField', [], {})
},
'db.macprefixpooltable': {
'Meta': {'object_name': 'MacPrefixPoolTable'},
'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
'size': ('django.db.models.fields.IntegerField', [], {})
},
'db.network': {
'Meta': {'object_name': 'Network'},
'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'dhcp': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'flavor': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'floating_ip_pool': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'gateway': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'gateway6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'link': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}),
'mode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'pool': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'network'", 'unique': 'True', 'null': 'True', 'to': "orm['db.IPPoolTable']"}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'serial': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'network'", 'null': 'True', 'to': "orm['db.QuotaHolderSerial']"}),
'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '32'}),
'subnet': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'subnet6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
'tags': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'db_index': 'True'})
},
'db.networkinterface': {
'Meta': {'object_name': 'NetworkInterface'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'dirty': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'index': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'ipv4': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True'}),
'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
'mac': ('django.db.models.fields.CharField', [], {'max_length': '32', 'unique': 'True', 'null': 'True'}),
'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}),
'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}),
'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '32'}),
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'db.quotaholderserial': {
'Meta': {'ordering': "['serial']", 'object_name': 'QuotaHolderSerial'},
'accept': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'pending': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'resolved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'serial': ('django.db.models.fields.BigIntegerField', [], {'primary_key': 'True', 'db_index': 'True'})
},
'db.virtualmachine': {
'Meta': {'object_name': 'VirtualMachine'},
'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '30', 'null': 'True'}),
'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machines'", 'null': 'True', 'to': "orm['db.Backend']"}),
'backend_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'imageid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'operstate': ('django.db.models.fields.CharField', [], {'default': "'BUILD'", 'max_length': '30'}),
'serial': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machine'", 'null': 'True', 'to': "orm['db.QuotaHolderSerial']"}),
'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
'task_job_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'userid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'db_index': 'True'})
},
'db.virtualmachinediagnostic': {
'Meta': {'ordering': "['-created']", 'object_name': 'VirtualMachineDiagnostic'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'details': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'diagnostics'", 'to': "orm['db.VirtualMachine']"}),
'message': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'source': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'source_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'})
},
'db.virtualmachinemetadata': {
'Meta': {'unique_together': "(('meta_key', 'vm'),)", 'object_name': 'VirtualMachineMetadata'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
'vm': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'metadata'", 'to': "orm['db.VirtualMachine']"})
}
}
complete_apps = ['db']
\ No newline at end of file
......@@ -42,7 +42,7 @@ from django.conf import settings as snf_settings
from aes_encrypt import encrypt_db_charfield, decrypt_db_charfield
from synnefo.db.managers import ForUpdateManager, ProtectedDeleteManager
from synnefo.db import pools
from synnefo.db import pools, fields
from synnefo.logic.rapi_pool import (get_rapi_client,
put_rapi_client)
......@@ -90,6 +90,7 @@ class Backend(models.Model):
# Type of hypervisor
hypervisor = models.CharField('Hypervisor', max_length=32, default="kvm",
null=False)
disk_templates = fields.SeparatedValuesField("Disk Templates", null=True)
# Last refresh of backend resources
updated = models.DateTimeField(auto_now_add=True)
# Backend resources
......
......@@ -80,6 +80,8 @@ class BackendFactory(factory.DjangoModelFactory):
pinst_cnt = 2
ctotal = 80
disk_templates = ["file", "plain", "drbd"]
class DrainedBackend(BackendFactory):
drained = True
......
......@@ -894,7 +894,7 @@ def get_physical_resources(backend):
return res
def update_resources(backend, resources=None):
def update_backend_resources(backend, resources=None):
""" Update the state of the backend resources in db.
"""
......@@ -926,6 +926,28 @@ def get_memory_from_instances(backend):
mem += i['oper_ram']
return mem
def get_available_disk_templates(backend):
"""Get the list of available disk templates of a Ganeti backend.
The list contains the disk templates that are enabled in the Ganeti backend
and also included in ipolicy-disk-templates.
"""
with pooled_rapi_client(backend) as c:
info = c.GetInfo()
enabled_disk_templates = info["enabled_disk_templates"]
ipolicy_disk_templates = info["ipolicy"]["disk-templates"]
return [dp for dp in enabled_disk_templates
if dp in ipolicy_disk_templates]
def update_backend_disk_templates(backend):
disk_templates = get_available_disk_templates(backend)
backend.disk_templates = disk_templates
backend.save()
##
## Synchronized operations for reconciliation
##
......
......@@ -32,10 +32,10 @@ import datetime
from django.utils import importlib
from synnefo.settings import (BACKEND_ALLOCATOR_MODULE, BACKEND_REFRESH_MIN,
BACKEND_PER_USER, ARCHIPELAGO_BACKENDS,
BACKEND_PER_USER,
DEFAULT_INSTANCE_NETWORKS)
from synnefo.db.models import Backend
from synnefo.logic.backend import update_resources
from synnefo.logic.backend import update_backend_resources
from synnefo.api.util import backend_public_networks
log = logging.getLogger(__name__)
......@@ -72,11 +72,8 @@ class BackendAllocator():
log.debug("Allocating VM: %r", vm)
# Get available backends
available_backends = get_available_backends()
available_backends = get_available_backends(flavor)
# Temporary fix for distinquishing archipelagos capable backends
available_backends = filter_archipelagos_backends(available_backends,
flavor.disk_template)
# Refresh backends, if needed
refresh_backends_stats(available_backends)
......@@ -96,27 +93,26 @@ class BackendAllocator():
return backend
def get_available_backends():
"""Get available backends from db.
def get_available_backends(flavor):
"""Get the list of available backends that can host a new VM of a flavor.
The list contains the backends that are online and that have enabled
the disk_template of the new VM.
Also, if the new VM will be automatically connected to a public network,
the backends that do not have an available public IPv4 address are
excluded.
"""
backends = list(Backend.objects.select_for_update().filter(drained=False,
offline=False))
backends = Backend.objects.select_for_update()
backends = backends.filter(offline=False, drained=False,
disk_templates__contains=flavor.disk_template)
backends = list(backends)
if "SNF:ANY_PUBLIC" in DEFAULT_INSTANCE_NETWORKS:
backends = filter(lambda x: has_free_ip(x), backends)
return backends
def filter_archipelagos_backends(available_backends, disk_template):
if disk_template == "ext":
available_backends = filter(lambda x: x.id in ARCHIPELAGO_BACKENDS,
available_backends)
else:
available_backends = filter(lambda x: x.id not in ARCHIPELAGO_BACKENDS,
available_backends)
return available_backends
def has_free_ip(backend):
"""Find if Backend has any free public IP."""
for network in backend_public_networks(backend):
......@@ -167,7 +163,7 @@ def refresh_backends_stats(backends):
if now > b.updated + delta:
log.debug("Updating resources of backend %r. Last Updated %r",
b, b.updated)
update_resources(b)
update_backend_resources(b)
def get_backend_for_user(userid):
......
......@@ -33,10 +33,7 @@ from django.core.management.base import BaseCommand, CommandError
from synnefo.db.models import Backend, Network
from django.db.utils import IntegrityError
from synnefo.logic.backend import (get_physical_resources,
update_resources,
create_network_synced,
connect_network_synced)
from synnefo.logic import backend as backend_mod
from synnefo.management.common import check_backend_credentials
from snf_django.management.utils import pprint_table
......@@ -116,13 +113,14 @@ def create_backend(clustername, port, username, password, hypervisor=None,
return
stream.write("Retrieving backend resources:\n")
resources = get_physical_resources(backend)
resources = backend_mod.get_physical_resources(backend)
attr = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal']
table = [[str(resources[x]) for x in attr]]
pprint_table(stream, table, attr)
update_resources(backend, resources)
backend_mod.update_backend_resources(backend, resources)
backend_mod.update_backend_disk_templates(backend)
networks = Network.objects.filter(deleted=False, floating_ip_pool=True)
if not networks:
......@@ -139,14 +137,15 @@ def create_backend(clustername, port, username, password, hypervisor=None,
for net in networks:
net.create_backend_network(backend)
result = create_network_synced(net, backend)
result = backend_mod.create_network_synced(net, backend)
if result[0] != "success":
stream.write('\nError Creating Network %s: %s\n' %
(net.backend_id, result[1]))
else:
stream.write('Successfully created Network: %s\n' %
net.backend_id)
result = connect_network_synced(network=net, backend=backend)
result = backend_mod.connect_network_synced(network=net,
backend=backend)
if result[0] != "success":
stream.write('\nError Connecting Network %s: %s\n' %
(net.backend_id, result[1]))
......
......@@ -70,7 +70,8 @@ class Command(ListCommand):
"mem": (get_mem, "free/total memory (MB)"),
"disk": (get_mem, "free/total disk (GB)"),
"hypervisor": ("hypervisor", "The hypervisor the backend is using"),
"disk_templates": ("disk_templates", "Enabled disk-templates"),
}
fields = ["id", "clustername", "port", "username", "drained", "offline",
"vms", "hypervisor", "ips"]
"vms", "hypervisor", "ips", "disk_templates"]
# Copyright 2011-2012 GRNET S.A. All rights reserved.
# Copyright 2011-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
......@@ -27,56 +27,25 @@
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of GRNET S.A.
#
from optparse import make_option
from django.core.management.base import BaseCommand
from synnefo.management.common import get_backend
from django.conf import settings
import datetime
from synnefo.db.models import Backend
from synnefo.logic.backend import update_resources
class Command(BaseCommand):
can_import_settings = True
from synnefo.logic import backend as backend_mod
help = "Update backend statistics, which are used for instance allocation."
output_transaction = True # The management command runs inside
# an SQL transaction
option_list = BaseCommand.option_list + (
make_option('--backend-id', dest='backend_id',
help="Update statistics of only this backend"),
make_option('--older-than', dest='older_than', metavar="MINUTES",
help="Update only backends that have not been updated for\
MINUTES. Set to 0 to force update."),
make_option('--include-drained', dest='drained',
default=False,
action='store_true',
help="Also update statistics of drained backends")
)
def handle(self, **options):
HELP_MSG = """Query Ganeti backends and update the status of backend in DB.
if options['backend_id']:
backends = [get_backend(options['backend_id'])]
else:
backends = Backend.objects.filter(offline=False)
if not options['drained']:
backends = backends.filter(drained=False)
This command updates:
* the list of the enabled disk-templates
* the available resources (disk, memory, CPUs)
"""
now = datetime.datetime.now()
if options['older_than'] is not None:
minutes = int(options['older_than'])
else:
minutes = settings.BACKEND_REFRESH_MIN
delta = datetime.timedelta(minutes=minutes)
class Command(BaseCommand):
help = HELP_MSG