247 lines
7.3 KiB
Python
247 lines
7.3 KiB
Python
# Copyright 2013 - 2016 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.db import models
|
|
from django.utils.functional import cached_property
|
|
|
|
from devops.helpers.helpers import tcp_ping_
|
|
from devops.helpers.helpers import wait_pass
|
|
from devops.helpers import loader
|
|
from devops.helpers.ssh_client import SSHClient
|
|
from devops.models.base import BaseModel
|
|
from devops.models.base import ParamedModel
|
|
from devops.models.network import Interface
|
|
from devops.models.network import NetworkConfig
|
|
from devops.models.volume import DiskDevice
|
|
|
|
|
|
class Node(ParamedModel, BaseModel):
|
|
class Meta(object):
|
|
unique_together = ('name', 'group')
|
|
db_table = 'devops_node'
|
|
app_label = 'devops'
|
|
|
|
group = models.ForeignKey('Group', null=True)
|
|
name = models.CharField(max_length=255, unique=False, null=False)
|
|
role = models.CharField(max_length=255, null=True)
|
|
|
|
@property
|
|
def driver(self):
|
|
drv = self.group.driver
|
|
|
|
# LEGACY (fuel-qa compatibility requires), TO REMOVE
|
|
def node_active(node):
|
|
return node.is_active()
|
|
drv.node_active = node_active
|
|
|
|
return drv
|
|
|
|
@cached_property
|
|
def ext(self):
|
|
ExtCls = loader.load_class(
|
|
'devops.models.node_ext.{ext_name}:NodeExtension'
|
|
''.format(ext_name=self.role or 'default'))
|
|
return ExtCls(node=self)
|
|
|
|
def define(self, *args, **kwargs):
|
|
self.save()
|
|
|
|
def start(self, *args, **kwargs):
|
|
pass
|
|
|
|
def destroy(self, *args, **kwargs):
|
|
pass
|
|
|
|
def erase(self, *args, **kwargs):
|
|
self.remove()
|
|
|
|
def remove(self, *args, **kwargs):
|
|
self.erase_volumes()
|
|
self.delete()
|
|
|
|
def suspend(self, *args, **kwargs):
|
|
pass
|
|
|
|
def resume(self, *args, **kwargs):
|
|
pass
|
|
|
|
def snapshot(self, *args, **kwargs):
|
|
pass
|
|
|
|
def revert(self, *args, **kwargs):
|
|
pass
|
|
|
|
# for fuel-qa compatibility
|
|
def has_snapshot(self, *args, **kwargs):
|
|
return True
|
|
|
|
# for fuel-qa compatibility
|
|
def get_snapshots(self):
|
|
"""Return full snapshots objects"""
|
|
return []
|
|
|
|
@property
|
|
def disk_devices(self):
|
|
return self.diskdevice_set.all()
|
|
|
|
@property
|
|
def interfaces(self):
|
|
return self.interface_set.order_by('id')
|
|
|
|
@property
|
|
def network_configs(self):
|
|
return self.networkconfig_set.all()
|
|
|
|
# LEGACY, for fuel-qa compatibility
|
|
@property
|
|
def is_admin(self):
|
|
return self.role == 'fuel_master'
|
|
|
|
# LEGACY, for fuel-qa compatibility
|
|
@property
|
|
def is_slave(self):
|
|
return self.role == 'fuel_slave'
|
|
|
|
def next_disk_name(self):
|
|
disk_names = ('sd' + c for c in list('abcdefghijklmnopqrstuvwxyz'))
|
|
for disk_name in disk_names:
|
|
if not self.disk_devices.filter(target_dev=disk_name).exists():
|
|
return disk_name
|
|
|
|
def interface_by_network_name(self, network_name):
|
|
return self.interface_set.filter(
|
|
l2_network_device__name=network_name)
|
|
|
|
def get_interface_by_nailgun_network_name(self, name):
|
|
for net_conf in self.networkconfig_set.all():
|
|
if name in net_conf.networks:
|
|
label = net_conf.label
|
|
break
|
|
else:
|
|
return None
|
|
return self.interface_set.get(label=label)
|
|
|
|
def get_ip_address_by_network_name(self, name, interface=None):
|
|
interface = interface or self.interface_set.filter(
|
|
l2_network_device__name=name).order_by('id')[0]
|
|
return interface.address_set.get(interface=interface).ip_address
|
|
|
|
def get_ip_address_by_nailgun_network_name(self, name):
|
|
interface = self.get_interface_by_nailgun_network_name(name)
|
|
return interface.address_set.first().ip_address
|
|
|
|
def remote(self, network_name, login, password=None, private_keys=None):
|
|
"""Create SSH-connection to the network
|
|
|
|
:rtype : SSHClient
|
|
"""
|
|
return SSHClient(
|
|
self.get_ip_address_by_network_name(network_name),
|
|
username=login,
|
|
password=password, private_keys=private_keys)
|
|
|
|
def await(self, network_name, timeout=120, by_port=22):
|
|
wait_pass(
|
|
lambda: tcp_ping_(
|
|
self.get_ip_address_by_network_name(network_name), by_port),
|
|
timeout=timeout)
|
|
|
|
# NEW
|
|
def add_interfaces(self, interfaces):
|
|
for interface in interfaces:
|
|
label = interface['label']
|
|
l2_network_device_name = interface.get('l2_network_device')
|
|
interface_model = interface.get('interface_model')
|
|
self.add_interface(
|
|
label=label,
|
|
l2_network_device_name=l2_network_device_name,
|
|
interface_model=interface_model)
|
|
|
|
# NEW
|
|
def add_interface(self, label, l2_network_device_name, interface_model):
|
|
if l2_network_device_name:
|
|
env = self.group.environment
|
|
l2_network_device = env.get_env_l2_network_device(
|
|
name=l2_network_device_name)
|
|
else:
|
|
l2_network_device = None
|
|
|
|
Interface.interface_create(
|
|
node=self,
|
|
label=label,
|
|
l2_network_device=l2_network_device,
|
|
model=interface_model,
|
|
)
|
|
|
|
# NEW
|
|
def add_network_configs(self, network_configs):
|
|
for label, data in network_configs.items():
|
|
self.add_network_config(
|
|
label=label,
|
|
networks=data.get('networks', []),
|
|
aggregation=data.get('aggregation'),
|
|
parents=data.get('parents', []),
|
|
)
|
|
|
|
# NEW
|
|
def add_network_config(self, label, networks=None, aggregation=None,
|
|
parents=None):
|
|
if networks is None:
|
|
networks = []
|
|
if parents is None:
|
|
parents = []
|
|
NetworkConfig.objects.create(
|
|
node=self,
|
|
label=label,
|
|
networks=networks,
|
|
aggregation=aggregation,
|
|
parents=parents,
|
|
)
|
|
|
|
# NEW
|
|
def add_volumes(self, volumes):
|
|
for vol_params in volumes:
|
|
self.add_volume(
|
|
**vol_params
|
|
)
|
|
|
|
# NEW
|
|
def add_volume(self, name, device='disk', bus='virtio', **params):
|
|
cls = self.driver.get_model_class('Volume')
|
|
volume = cls.objects.create(
|
|
node=self,
|
|
name=name,
|
|
**params
|
|
)
|
|
DiskDevice.node_attach_volume(
|
|
node=self,
|
|
volume=volume,
|
|
device=device,
|
|
bus=bus,
|
|
)
|
|
return volume
|
|
|
|
# NEW
|
|
def get_volume(self, **kwargs):
|
|
return self.volume_set.get(**kwargs)
|
|
|
|
# NEW
|
|
def get_volumes(self, **kwargs):
|
|
return self.volume_set.filter(**kwargs)
|
|
|
|
# NEW
|
|
def erase_volumes(self):
|
|
for volume in self.get_volumes():
|
|
volume.erase()
|