Files
fuel-devops/devops/models.py
Nikita Prikazchikov 4d8c5828d8 Merge branch '2.0'
Conflicts:
	devops/driver/dummy/dummy_driver.py
	devops/models.py
	devops/tests/test_models.py
2013-09-09 18:52:30 +04:00

367 lines
11 KiB
Python

import json
from django.utils.importlib import import_module
from ipaddr import IPNetwork
from django.db import models
from devops.settings import DRIVER
from devops.helpers.helpers import SSHClient, _wait, _tcp_ping
def choices(*args, **kwargs):
defaults = {'max_length': 255, 'null': False}
defaults.update(kwargs)
defaults.update(choices=double_tuple(*args))
return models.CharField(**defaults)
def double_tuple(*args):
dict = []
for arg in args:
dict.append((arg, arg))
return tuple(dict)
class Environment(models.Model):
name = models.CharField(max_length=255, unique=True, null=False)
@property
def volumes(self):
return Volume.objects.filter(environment=self)
@property
def networks(self):
return Network.objects.filter(environment=self)
@property
def nodes(self):
return Node.objects.filter(environment=self)
def node_by_name(self, name):
return self.nodes.get(name=name, environment=self)
def nodes_by_role(self, role):
return self.nodes.filter(role=role, environment=self)
def network_by_name(self, name):
return self.networks.get(name=name, environment=self)
def has_snapshot(self, name):
return all(map(lambda x: x.has_snapshot(name), self.nodes))
def define(self):
for network in self.networks:
network.define()
for volume in self.volumes:
volume.define()
for node in self.nodes:
node.define()
def start(self, nodes=None):
for network in self.networks:
network.start()
for node in nodes or self.nodes:
node.start()
def destroy(self, verbose=False):
for node in self.nodes:
node.destroy(verbose=verbose)
def erase(self):
for node in self.nodes:
node.erase()
for network in self.networks:
network.erase()
for volume in self.volumes:
volume.erase()
self.delete()
def suspend(self, verbose=False):
for node in self.nodes:
node.suspend(verbose)
def resume(self, verbose=False):
for node in self.nodes:
node.resume(verbose)
def snapshot(self, name=None, description=None, force=False):
for node in self.nodes:
node.snapshot(name=name, description=description, force=force)
def revert(self, name=None, destroy=True):
if destroy:
for node in self.nodes:
node.destroy(verbose=False)
for node in self.nodes:
node.revert(name, destroy=False)
class ExternalModel(models.Model):
_driver = None
@classmethod
def get_driver(cls):
"""
:rtype : DevopsDriver
"""
driver = import_module(DRIVER)
cls._driver = cls._driver or driver.DevopsDriver()
return cls._driver
@property
def driver(self):
"""
:rtype : DevopsDriver
"""
return self.get_driver()
name = models.CharField(max_length=255, unique=False, null=False)
uuid = models.CharField(max_length=255)
environment = models.ForeignKey(Environment, null=True)
class Meta:
abstract = True
unique_together = ('name', 'environment')
@classmethod
def get_allocated_networks(cls):
return cls.get_driver().get_allocated_networks()
class Network(ExternalModel):
_iterhosts = None
has_dhcp_server = models.BooleanField()
has_pxe_server = models.BooleanField()
has_reserved_ips = models.BooleanField(default=True)
tftp_root_dir = models.CharField(max_length=255)
forward = choices(
'nat', 'route', 'bridge', 'private', 'vepa',
'passthrough', 'hostdev', null=True)
ip_network = models.CharField(max_length=255, unique=True)
@property
def interfaces(self):
return Interface.objects.filter(network=self)
@property
def ip_pool_start(self):
return IPNetwork(self.ip_network)[2]
@property
def ip_pool_end(self):
return IPNetwork(self.ip_network)[-2]
def next_ip(self):
while True:
self._iterhosts = self._iterhosts or IPNetwork(
self.ip_network).iterhosts()
ip = self._iterhosts.next()
if ip < self.ip_pool_start or ip > self.ip_pool_end:
continue
if not Address.objects.filter(
interface__network=self,
ip_address=str(ip)).exists():
return ip
def bridge_name(self):
return self.driver.network_bridge_name(self)
def define(self):
self.driver.network_define(self)
self.save()
def start(self):
self.create(verbose=False)
def create(self, verbose=False):
if verbose or not self.driver.network_active(self):
self.driver.network_create(self)
def destroy(self):
self.driver.network_destroy(self)
def erase(self):
self.remove(verbose=False)
def remove(self, verbose=False):
if verbose or self.uuid:
if verbose or self.driver.network_exists(self):
if self.driver.network_active(self):
self.driver.network_destroy(self)
self.driver.network_undefine(self)
self.delete()
class Node(ExternalModel):
hypervisor = choices('kvm')
os_type = choices('hvm')
architecture = choices('x86_64', 'i686')
boot = models.CharField(max_length=255, null=False, default=json.dumps([]))
metadata = models.CharField(max_length=255, null=True)
role = models.CharField(max_length=255, null=True)
vcpu = models.PositiveSmallIntegerField(null=False, default=1)
memory = models.IntegerField(null=False, default=1024)
has_vnc = models.BooleanField(null=False, default=True)
def next_disk_name(self):
disk_names = ('sd' + c for c in list('abcdefghijklmnopqrstuvwxyz'))
while True:
disk_name = disk_names.next()
if not self.disk_devices.filter(target_dev=disk_name).exists():
return disk_name
def get_vnc_port(self):
return self.driver.node_get_vnc_port(node=self)
@property
def disk_devices(self):
return DiskDevice.objects.filter(node=self)
@property
def interfaces(self):
return Interface.objects.filter(node=self).order_by('id')
def interface_by_name(self, name):
self.interfaces.filter(name=name)
def get_ip_address_by_network_name(self, name, interface=None):
interface = interface or Interface.objects.filter(
network__name=name, node=self).order_by('id')[0]
return Address.objects.get(interface=interface).ip_address
def remote(self, network_name, login, password=None, private_keys=None):
"""
:rtype : SSHClient
"""
return SSHClient(
self.get_ip_address_by_network_name(network_name),
username=login,
password=password, private_keys=private_keys)
def send_keys(self, keys):
self.driver.node_send_keys(self, keys)
def await(self, network_name, timeout=120):
_wait(
lambda: _tcp_ping(
self.get_ip_address_by_network_name(network_name), 22),
timeout=timeout)
def define(self):
self.driver.node_define(self)
self.save()
def start(self):
self.create(verbose=False)
def create(self, verbose=False):
if verbose or not self.driver.node_active(self):
self.driver.node_create(self)
def destroy(self, verbose=False):
if verbose or self.driver.node_active(self):
self.driver.node_destroy(self)
def erase(self):
self.remove(verbose=False)
def remove(self, verbose=False):
if verbose or self.uuid:
if verbose or self.driver.node_exists(self):
self.destroy(verbose=False)
self.driver.node_delete_all_snapshots(node=self)
self.driver.node_undefine(self)
self.delete()
def suspend(self, verbose=False):
if verbose or self.driver.node_active(self):
self.driver.node_suspend(self)
def resume(self, verbose=False):
if verbose or self.driver.node_active(self):
self.driver.node_resume(self)
def has_snapshot(self, name):
return self.driver.node_snapshot_exists(node=self, name=name)
def snapshot(self, name=None, force=False, description=None):
if force and self.has_snapshot(name):
self.driver.node_delete_snapshot(node=self, name=name)
self.driver.node_create_snapshot(
node=self, name=name, description=description)
def revert(self, name=None, destroy=True):
if destroy:
self.destroy(verbose=False)
self.driver.node_revert_snapshot(node=self, name=name)
class Volume(ExternalModel):
capacity = models.BigIntegerField(null=False)
backing_store = models.ForeignKey('self', null=True)
format = models.CharField(max_length=255, null=False)
def define(self):
self.driver.volume_define(self)
self.save()
def erase(self):
self.remove(verbose=False)
def remove(self, verbose=False):
if verbose or self.uuid:
if verbose or self.driver.volume_exists(self):
self.driver.volume_delete(self)
self.delete()
def get_capacity(self):
return self.driver.volume_capacity(self)
def get_format(self):
return self.driver.volume_format(self)
def get_path(self):
return self.driver.volume_path(self)
def fill_from_exist(self):
self.capacity = self.get_capacity()
self.format = self.get_format()
def upload(self, path):
self.driver.volume_upload(self, path)
class DiskDevice(models.Model):
device = choices('disk', 'cdrom')
type = choices('file')
bus = choices('virtio')
target_dev = models.CharField(max_length=255, null=False)
node = models.ForeignKey(Node, null=False)
volume = models.ForeignKey(Volume, null=True)
class Interface(models.Model):
mac_address = models.CharField(max_length=255, unique=True, null=False)
network = models.ForeignKey(Network)
node = models.ForeignKey(Node)
type = models.CharField(max_length=255, null=False)
model = choices('virtio')
@property
def target_dev(self):
return self.node.driver.node_get_interface_target_dev(
self.node, self.mac_address)
@property
def addresses(self):
return Address.objects.filter(interface=self)
def add_address(self, address):
Address.objects.create(ip_address=address, interface=self)
class Address(models.Model):
ip_address = models.GenericIPAddressField()
interface = models.ForeignKey(Interface)