Files
fuel-devops/devops/models/environment.py
Egor Kotko 2540ede313 Enlarge admin node hdd size
Change hdd size for master node
due to requrements of backup feature

Change-Id: I56e65ba8b9ccda1e32d0498842084a205adb9e9e
Closes-Bug: #1450071
2015-05-04 10:40:58 +02:00

379 lines
13 KiB
Python

# Copyright 2013 - 2015 Mirantis, Inc.
#
# 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 django.conf import settings
from django.db import models
from ipaddr import IPNetwork
from paramiko import Agent
from paramiko import RSAKey
from devops.helpers.helpers import _get_file_size
from devops.helpers.helpers import SSHClient
from devops import logger
from devops.models.base import DriverModel
from devops.models.network import DiskDevice
from devops.models.network import Interface
from devops.models.network import Network
from devops.models.node import Node
from devops.models.volume import Volume
class Environment(DriverModel):
class Meta:
db_table = 'devops_environment'
name = models.CharField(max_length=255, unique=True, null=False)
hostname = 'nailgun'
domain = 'test.domain.local'
nat_interface = '' # INTERFACES.get('admin')
admin_net = 'admin'
admin_net2 = 'admin2'
os_image = None # Dirty hack. Check for os_image attribute for relevancy.
def get_volume(self, *args, **kwargs):
return self.volume_set.get(*args, **kwargs)
def get_volumes(self, *args, **kwargs):
return self.volume_set.filter(*args, **kwargs)
def get_network(self, *args, **kwargs):
return self.network_set.get(*args, **kwargs)
def get_networks(self, *args, **kwargs):
return self.network_set.filter(*args, **kwargs)
def get_node(self, *args, **kwargs):
return self.node_set.get(*args, **kwargs)
def get_nodes(self, *args, **kwargs):
return self.node_set.filter(*args, **kwargs)
def add_node(self, memory, name, vcpu=1, boot=None):
return Node.node_create(
name=name,
memory=memory,
vcpu=vcpu,
environment=self,
boot=boot)
def add_empty_volume(self, node, name,
capacity=settings.NODE_VOLUME_SIZE * 1024 * 1024
* 1024, device='disk', bus='virtio', format='qcow2'):
return DiskDevice.node_attach_volume(
node=node,
volume=Volume.volume_create(
name=name,
capacity=capacity,
environment=self,
format=format),
device=device,
bus=bus)
@classmethod
def create(cls, name):
"""Create Environment instance with given name.
:rtype: devops.models.Environment
"""
return cls.objects.create(name=name)
@classmethod
def get(cls, *args, **kwargs):
return cls.objects.get(*args, **kwargs)
@classmethod
def list(cls):
return cls.objects.all()
def has_snapshot(self, name):
return all(map(lambda x: x.has_snapshot(name), self.get_nodes()))
def define(self):
for network in self.get_networks():
network.define()
for volume in self.get_volumes():
volume.define()
for node in self.get_nodes():
node.define()
def start(self, nodes=None):
for network in self.get_networks():
network.start()
for node in nodes or self.get_nodes():
node.start()
def destroy(self, verbose=False):
for node in self.get_nodes():
node.destroy(verbose=verbose)
def erase(self):
for node in self.get_nodes():
node.erase()
for network in self.get_networks():
network.erase()
for volume in self.get_volumes():
volume.erase()
self.delete()
@classmethod
def erase_empty(cls):
for env in cls.objects.all():
if len(env.get_nodes()) == 0:
env.delete()
def suspend(self, verbose=False):
for node in self.get_nodes():
node.suspend(verbose)
def resume(self, verbose=False):
for node in self.get_nodes():
node.resume(verbose)
def snapshot(self, name=None, description=None, force=False):
for node in self.get_nodes():
node.snapshot(name=name, description=description, force=force)
def revert(self, name=None, destroy=True, flag=True):
if destroy:
for node in self.get_nodes():
node.destroy(verbose=False)
if (flag and not all(
[node.has_snapshot(name) for node in self.get_nodes()])):
raise Exception("some nodes miss snapshot,"
" test should be interrupted")
for node in self.get_nodes():
node.revert(name, destroy=False)
@classmethod
def synchronize_all(cls):
driver = cls.get_driver()
nodes = {driver._get_name(e.name, n.name): n
for e in cls.objects.all()
for n in e.get_nodes()}
domains = set(driver.node_list())
# FIXME (AWoodward) This willy nilly wacks domains when you run this
# on domains that are outside the scope of devops, if anything this
# should cause domains to be imported into db instead of undefined.
# It also leaves network and volumes around too
# Disabled untill a safer implmentation arrives
# Undefine domains without devops nodes
#
# domains_to_undefine = domains - set(nodes.keys())
# for d in domains_to_undefine:
# driver.node_undefine_by_name(d)
# Remove devops nodes without domains
nodes_to_remove = set(nodes.keys()) - domains
for n in nodes_to_remove:
nodes[n].delete()
cls.erase_empty()
logger.info('Undefined domains: %s, removed nodes: %s' %
(0, len(nodes_to_remove)))
@classmethod
def describe_environment(cls, boot_from='cdrom'):
environment = cls.create(settings.ENV_NAME)
networks = []
interfaces = settings.INTERFACE_ORDER
if settings.MULTIPLE_NETWORKS:
logger.info('Multiple cluster networks feature is enabled!')
if settings.BONDING:
interfaces = settings.BONDING_INTERFACES.keys()
for name in interfaces:
networks.append(environment.create_networks(name))
for name in environment.node_roles.admin_names:
environment.describe_admin_node(name, networks, boot_from)
for name in environment.node_roles.other_names:
if settings.MULTIPLE_NETWORKS:
networks1 = [net for net in networks if net.name
in settings.NODEGROUPS[0]['pools']]
networks2 = [net for net in networks if net.name
in settings.NODEGROUPS[1]['pools']]
# If slave index is even number, then attach to
# it virtual networks from the second network group.
if int(name[-2:]) % 2 == 1:
environment.describe_empty_node(name, networks1)
elif int(name[-2:]) % 2 == 0:
environment.describe_empty_node(name, networks2)
else:
environment.describe_empty_node(name, networks)
return environment
def create_networks(self, name):
ip_networks = [
IPNetwork(x) for x in settings.POOLS.get(name)[0].split(',')]
new_prefix = int(settings.POOLS.get(name)[1])
pool = Network.create_network_pool(networks=ip_networks,
prefix=int(new_prefix))
return Network.network_create(
name=name,
environment=self,
pool=pool,
forward=settings.FORWARDING.get(name),
has_dhcp_server=settings.DHCP.get(name))
def create_interfaces(self, networks, node,
model=settings.INTERFACE_MODEL):
if settings.BONDING:
for network in networks:
Interface.interface_create(
network, node=node, model=model,
interface_map=settings.BONDING_INTERFACES)
else:
for network in networks:
Interface.interface_create(network, node=node, model=model)
@property
def node_roles(self):
return NodeRoles(
admin_names=['admin'],
other_names=['slave-%02d' % x for x in range(1, int(
settings.NODES_COUNT))]
)
def describe_empty_node(self, name, networks):
node = self.add_node(
name=name,
memory=settings.HARDWARE.get("slave_node_memory", 1024),
vcpu=settings.HARDWARE.get("slave_node_cpu", 1))
self.create_interfaces(networks, node)
self.add_empty_volume(node, name + '-system')
if settings.USE_ALL_DISKS:
self.add_empty_volume(node, name + '-cinder')
self.add_empty_volume(node, name + '-swift')
return node
# @logwrap
def describe_admin_node(self, name, networks, boot_from='cdrom',
vcpu=None, memory=None,
iso_path=None):
if boot_from == 'cdrom':
boot_device = ['hd', 'cdrom']
device = 'cdrom'
bus = 'ide'
elif boot_from == 'usb':
boot_device = ['hd']
device = 'disk'
bus = 'usb'
node = self.add_node(
memory=memory or settings.HARDWARE.get("admin_node_memory", 1024),
vcpu=vcpu or settings.HARDWARE.get("admin_node_cpu", 1),
name=name,
boot=boot_device)
self.create_interfaces(networks, node)
if self.os_image is None:
iso = iso_path or settings.ISO_PATH
self.add_empty_volume(node, name + '-system',
capacity=settings.ADMIN_NODE_VOLUME_SIZE
* 1024 * 1024 * 1024)
self.add_empty_volume(
node,
name + '-iso',
capacity=_get_file_size(iso),
format='raw',
device=device,
bus=bus)
else:
volume = Volume.volume_get_predefined(self.os_image)
vol_child = Volume.volume_create_child(
name=name + '-system',
backing_store=volume,
environment=self
)
DiskDevice.node_attach_volume(
node=node,
volume=vol_child
)
return node
# Rename it to default_gw and move to models.Network class
def router(self, router_name=None): # Alternative name: get_host_node_ip
router_name = router_name or self.admin_net
if router_name == self.admin_net2:
return str(self.get_network(name=router_name).ip[2])
return str(self.get_network(name=router_name).ip[1])
def nodes(self): # migrated from EnvironmentModel.nodes()
return Nodes(self, self.node_roles)
# @logwrap
def get_admin_remote(self,
login=settings.SSH_CREDENTIALS['login'],
password=settings.SSH_CREDENTIALS['password']):
"""SSH to admin node
:rtype : SSHClient
"""
return self.nodes().admin.remote(
self.admin_net,
login=login,
password=password)
# @logwrap
def get_ssh_to_remote(self, ip):
keys = []
for key_string in ['/root/.ssh/id_rsa',
'/root/.ssh/bootstrap.rsa']:
with self.get_admin_remote().open(key_string) as f:
keys.append(RSAKey.from_private_key(f))
return SSHClient(ip,
username=settings.SSH_CREDENTIALS['login'],
password=settings.SSH_CREDENTIALS['password'],
private_keys=keys)
# @logwrap
def get_ssh_to_remote_by_key(self, ip, keyfile):
try:
with open(keyfile) as f:
keys = [RSAKey.from_private_key(f)]
except IOError:
logger.warning('Loading of SSH key from file failed. Trying to use'
' SSH agent ...')
keys = Agent().get_keys()
return SSHClient(ip, private_keys=keys)
class NodeRoles(object):
def __init__(self,
admin_names=None,
other_names=None):
self.admin_names = admin_names or []
self.other_names = other_names or []
class Nodes(object):
def __init__(self, environment, node_roles):
self.admins = []
self.others = []
for node_name in node_roles.admin_names:
self.admins.append(environment.get_node(name=node_name))
for node_name in node_roles.other_names:
self.others.append(environment.get_node(name=node_name))
self.slaves = self.others
self.all = self.slaves + self.admins
self.admin = self.admins[0]
def __iter__(self):
return self.all.__iter__()