compass-core/compass/db/models.py
leilei9547 63e44f9164 Update db layer api tests
Change-Id: I10b594d95b148fb9233c040da31c1ba6d9beaf72
2014-08-19 17:13:19 -07:00

2288 lines
66 KiB
Python

# Copyright 2014 Huawei Technologies Co. Ltd
#
# 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.
"""Database model"""
import datetime
import logging
import netaddr
import re
import simplejson as json
from sqlalchemy import BigInteger
from sqlalchemy import Boolean
from sqlalchemy import Column
from sqlalchemy import ColumnDefault
from sqlalchemy import DateTime
from sqlalchemy import Enum
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import Float
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy.orm import relationship, backref
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import Text
from sqlalchemy.types import TypeDecorator
from sqlalchemy import UniqueConstraint
from compass.db import exception
from compass.db import validator
from compass.utils import util
BASE = declarative_base()
class JSONEncoded(TypeDecorator):
"""Represents an immutable structure as a json-encoded string."""
impl = Text
def process_bind_param(self, value, dialect):
if value is not None:
value = json.dumps(value)
return value
def process_result_value(self, value, dialect):
if value is not None:
value = json.loads(value)
return value
class TimestampMixin(object):
created_at = Column(DateTime, default=lambda: datetime.datetime.now())
updated_at = Column(DateTime, default=lambda: datetime.datetime.now(),
onupdate=lambda: datetime.datetime.now())
class HelperMixin(object):
def initialize(self):
self.update()
def update(self):
pass
@staticmethod
def type_compatible(value, column_type):
if value is None:
return True
if not hasattr(column_type, 'python_type'):
return True
column_python_type = column_type.python_type
if isinstance(value, column_python_type):
return True
if issubclass(column_python_type, basestring):
return isinstance(value, basestring)
if column_python_type in [int, long]:
return type(value) in [int, long]
if column_python_type in [float]:
return type(value) in [float]
if column_python_type in [bool]:
return type(value) in [bool]
return False
def validate(self):
columns = self.__mapper__.columns
for key, column in columns.items():
value = getattr(self, key)
if not self.type_compatible(value, column.type):
raise exception.InvalidParameter(
'column %s value %r type is unexpected: %s' % (
key, value, column.type
)
)
def to_dict(self):
keys = self.__mapper__.columns.keys()
dict_info = {}
for key in keys:
if key.startswith('_'):
continue
value = getattr(self, key)
if value is not None:
if isinstance(value, datetime.datetime):
value = util.format_datetime(value)
dict_info[key] = value
return dict_info
class MetadataMixin(HelperMixin):
name = Column(String(80))
display_name = Column(String(80))
path = Column(String(256))
description = Column(Text)
is_required = Column(Boolean, default=False)
required_in_whole_config = Column(Boolean, default=False)
mapping_to = Column(String(80), default='')
validator_data = Column('validator', Text)
js_validator = Column(Text)
default_value = Column(JSONEncoded)
options = Column(JSONEncoded, default=[])
required_in_options = Column(Boolean, default=False)
def initialize(self):
if not self.display_name:
if self.name:
self.display_name = self.name
super(MetadataMixin, self).initialize()
@property
def validator(self):
if not self.name:
raise exception.InvalidParamter(
'name is not set in os metadata %s' % self.id
)
if not self.validator_data:
return None
func = eval(
self.validator_data,
validator.VALIDATOR_GLOBALS,
validator.VALIDATOR_LOCALS
)
if not callable(func):
raise Exception(
'%s is not callable' % self.validator_data
)
return func
@validator.setter
def validator(self, value):
if not value:
self.validator_data = None
elif isinstance(value, basestring):
self.validator_data = value
elif callable(value):
self.validator_data = value.func_name
else:
raise Exception(
'%s is not callable' % value
)
def to_dict(self):
self_dict_info = {}
if self.field:
self_dict_info.update(self.field.to_dict())
else:
self_dict_info['field_type_data'] = 'dict'
self_dict_info['field_type'] = dict
self_dict_info.update(super(MetadataMixin, self).to_dict())
validator = self.validator
if validator:
self_dict_info['validator_data'] = self.validator_data
self_dict_info['validator'] = validator
js_validator = self.js_validator
if js_validator:
self_dict_info['js_validator'] = js_validator
dict_info = {
'_self': self_dict_info
}
for child in self.children:
dict_info.update(child.to_dict())
return {
self.name: dict_info
}
return dict_info
class FieldMixin(HelperMixin):
id = Column(Integer, primary_key=True)
field = Column(String(80), unique=True)
field_type_data = Column(
'field_type',
Enum('basestring', 'int', 'float', 'list', 'bool'),
ColumnDefault('basestring')
)
display_type = Column(
Enum(
'checkbox', 'radio', 'select',
'multiselect', 'combobox', 'text',
'multitext', 'password'
),
ColumnDefault('text')
)
validator_data = Column('validator', Text)
js_validator = Column(Text)
description = Column(Text)
@property
def field_type(self):
if not self.field_type_data:
return None
field_type = eval(self.field_type_data)
if not type(field_type) == type:
raise Exception(
'%s is not type' % self.field_type_data
)
return field_type
@field_type.setter
def field_type(self, value):
if not value:
self.field_type_data = None
elif isinstance(value, basestring):
self.field_type_data = value
elif type(value) == type:
self.field_type_data = value.__name__
else:
raise Exception(
'%s is not type' % value
)
@property
def validator(self):
if not self.validator_data:
return None
func = eval(
self.validator_data,
validator.VALIDATOR_GLOBALS,
validator.VALIDATOR_LOCALS
)
if not callable(func):
raise Exception(
'%s is not callable' % self.validator_data
)
return func
@validator.setter
def validator(self, value):
if not value:
self.validator_data = None
elif isinstance(value, basestring):
self.validator_data = value
elif callable(value):
self.validator_data = value.func_name
else:
raise Exception(
'%s is not callable' % value
)
def to_dict(self):
dict_info = super(FieldMixin, self).to_dict()
dict_info['field_type'] = self.field_type
validator = self.validator
if validator:
dict_info['validator'] = self.validator
js_validator = self.js_validator
if js_validator:
dict_info['js_validator'] = self.js_validator
return dict_info
class InstallerMixin(HelperMixin):
name = Column(String(80))
alias = Column(String(80), unique=True)
settings = Column(JSONEncoded, default={})
def validate(self):
super(InstallerMixin, self).validate()
if not self.name:
raise exception.InvalidParameter(
'name is not set in installer %s' % self.name
)
class StateMixin(TimestampMixin, HelperMixin):
state = Column(
Enum(
'UNINITIALIZED', 'INITIALIZED',
'INSTALLING', 'SUCCESSFUL', 'ERROR'
),
ColumnDefault('UNINITIALIZED')
)
percentage = Column(Float, default=0.0)
message = Column(Text, default='')
severity = Column(
Enum('INFO', 'WARNING', 'ERROR'),
ColumnDefault('INFO')
)
def update(self):
if self.state in ['UNINITIALIZED', 'INITIALIZED']:
self.percentage = 0.0
self.severity = 'INFO'
self.message = ''
if self.state == 'INSTALLING':
if self.severity == 'ERROR':
self.state = 'ERROR'
elif self.percentage >= 1.0:
self.state = 'SUCCESSFUL'
self.percentage = 1.0
if self.state == 'SUCCESSFUL':
self.percentage = 1.0
super(StateMixin, self).update()
class HostNetwork(BASE, TimestampMixin, HelperMixin):
"""Host network table."""
__tablename__ = 'host_network'
id = Column(Integer, primary_key=True)
host_id = Column(
Integer,
ForeignKey('host.id', onupdate='CASCADE', ondelete='CASCADE')
)
interface = Column(
String(80))
subnet_id = Column(
Integer,
ForeignKey('subnet.id', onupdate='CASCADE', ondelete='CASCADE')
)
ip_int = Column(BigInteger, unique=True)
is_mgmt = Column(Boolean, default=False)
is_promiscuous = Column(Boolean, default=False)
__table_args__ = (
UniqueConstraint('host_id', 'interface', name='constraint'),
)
def __init__(self, host_id, interface, **kwargs):
self.host_id = host_id
self.interface = interface
super(HostNetwork, self).__init__(**kwargs)
@property
def ip(self):
return str(netaddr.IPAddress(self.ip_int))
@ip.setter
def ip(self, value):
self.ip_int = int(netaddr.IPAddress(value))
@hybrid_property
def subnet(self):
return self.subnet.subnet
@subnet.expression
def subnet(cls):
return cls.subnet.subnet
@property
def netmask(self):
return str(netaddr.IPNetwork(self.subnet).netmask)
def update(self):
self.host.config_validated = False
def validate(self):
super(HostNetwork, self).validate()
if not self.subnet:
raise exception.InvalidParameter(
'subnet is not set in %s interface %s' % (
self.host_id, self.interface
)
)
if not self.ip_int:
raise exception.InvalidParameter(
'ip is not set in %s interface %s' % (
self.host_id, self.interface
)
)
ip = netaddr.IPAddress(self.ip_int)
subnet = netaddr.IPNetwork(self.subnet)
if ip not in subnet:
raise exception.InvalidParameter(
'ip %s is not in subnet %s' % (
str(ip), str(subnet)
)
)
def to_dict(self):
dict_info = super(HostNetwork, self).to_dict()
dict_info['ip'] = self.ip
dict_info['interface'] = self.interface
dict_info['netmask'] = self.netmask
dict_info['subnet'] = self.subnet
return dict_info
class ClusterHostState(BASE, StateMixin):
"""ClusterHost state table."""
__tablename__ = 'clusterhost_state'
id = Column(
Integer,
ForeignKey(
'clusterhost.id',
onupdate='CASCADE', ondelete='CASCADE'
),
primary_key=True
)
def update(self):
host_state = self.clusterhost.host.state
if self.state == 'INITIALIZED':
if host_state.state in ['UNINITIALIZED']:
host_state.state = 'INITIALIZED'
host_state.update()
elif self.state == 'INSTALLING':
if host_state.state in ['UNINITIALIZED', 'INITIALIZED']:
host_state.state = 'INSTALLING'
host_state.update()
elif self.state == 'SUCCESSFUL':
if host_state.state != 'SUCCESSFUL':
host_state.state = 'SUCCESSFUL'
host_state.update()
super(ClusterHostState, self).update()
class ClusterHost(BASE, TimestampMixin, HelperMixin):
"""ClusterHost table."""
__tablename__ = 'clusterhost'
clusterhost_id = Column('id', Integer, primary_key=True)
cluster_id = Column(
Integer,
ForeignKey('cluster.id', onupdate='CASCADE', ondelete='CASCADE')
)
host_id = Column(
Integer,
ForeignKey('host.id', onupdate='CASCADE', ondelete='CASCADE')
)
_roles = Column(JSONEncoded, default=[])
config_step = Column(String(80), default='')
package_config = Column(JSONEncoded, default={})
config_validated = Column(Boolean, default=False)
deployed_package_config = Column(JSONEncoded, default={})
__table_args__ = (
UniqueConstraint('cluster_id', 'host_id', name='constraint'),
)
state = relationship(
ClusterHostState,
uselist=False,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('clusterhost')
)
def __init__(self, cluster_id, host_id, **kwargs):
self.cluster_id = cluster_id
self.host_id = host_id
self.state = ClusterHostState()
super(ClusterHost, self).__init__(**kwargs)
def update(self):
if self.host.reinstall_os:
if self.state in ['SUCCESSFUL', 'ERROR']:
if self.config_validated:
self.state.state = 'INITIALIZED'
else:
self.state.state = 'UNINITIALIZED'
self.state.update()
@property
def name(self):
return '%s.%s' % (self.host.name, self.cluster.name)
@property
def patched_package_config(self):
return self.package_config
@patched_package_config.setter
def patched_package_config(self, value):
self.package_config = util.merge_dict(dict(self.package_config), value)
self.config_validated = False
@property
def put_package_config(self):
return self.package_config
@put_package_config.setter
def put_package_config(self, value):
package_config = dict(self.package_config)
package_config.update(value)
self.package_config = package_config
self.config_validated = False
@property
def patched_os_config(self):
return self.host.os_config
@patched_os_config.setter
def patched_os_config(self, value):
host = self.host
host.patched_os_config = value
@property
def put_os_config(self):
return self.host.os_config
@put_os_config.setter
def put_os_config(self, value):
host = self.host
host.put_os_config = value
@hybrid_property
def distributed_system_name(self):
return self.cluster.distributed_system_name
@distributed_system_name.expression
def distributed_system_name(cls):
return cls.cluster.distributed_system_name
@hybrid_property
def os_name(self):
return self.host.os_name
@os_name.expression
def os_name(cls):
return cls.host.os_name
@hybrid_property
def clustername(self):
return self.cluster.name
@clustername.expression
def clustername(cls):
return cls.cluster.name
@hybrid_property
def hostname(self):
return self.host.name
@hostname.expression
def hostname(cls):
return cls.host.name
@property
def distributed_system_installed(self):
return self.state.state == 'SUCCESSFUL'
@property
def resintall_os(self):
return self.host.reinstall_os
@property
def reinstall_distributed_system(self):
return self.cluster.reinstall_distributed_system
@property
def os_installed(self):
return self.host.os_installed
@property
def roles(self):
role_names = list(self._roles)
if not role_names:
return None
flavor = self.cluster.flavor
if not flavor:
return None
roles = []
for flavor_role in flavor.flavor_roles:
role = flavor_role.role
if role.name in role_names:
roles.append(role)
return roles
@roles.setter
def roles(self, value):
self._roles = value
self.config_validated = False
@property
def patched_roles(self):
return self.roles
@patched_roles.setter
def patched_roles(self, value):
roles = list(self._roles)
roles.extend(value)
self._roles = roles
self.config_validated = False
@hybrid_property
def owner(self):
return self.cluster.owner
@owner.expression
def owner(cls):
return cls.cluster.owner
def state_dict(self):
cluster = self.cluster
host = self.host
if (
not cluster.distributed_system or
host.state.state != 'SUCCESSFUL'
):
return host.state_dict()
else:
return self.state.to_dict()
def to_dict(self):
dict_info = self.host.to_dict()
dict_info.update(super(ClusterHost, self).to_dict())
state_dict = self.state_dict()
dict_info.update({
'distributed_system_name': self.distributed_system_name,
'distributed_system_installed': self.distributed_system_installed,
'reinstall_distributed_system': self.reinstall_distributed_system,
'owner': self.owner,
'clustername': self.clustername,
'name': self.name,
'state': state_dict['state']
})
roles = self.roles
if roles:
dict_info['roles'] = [
role.to_dict() for role in roles
]
return dict_info
class HostState(BASE, StateMixin):
"""Host state table."""
__tablename__ = 'host_state'
id = Column(
Integer,
ForeignKey('host.id', onupdate='CASCADE', ondelete='CASCADE'),
primary_key=True
)
def update(self):
host = self.host
if self.state == 'INSTALLING':
host.reinstall_os = False
for clusterhost in self.host.clusterhosts:
if clusterhost.state in [
'SUCCESSFUL', 'ERROR'
]:
clusterhost.state = 'INSTALLING'
clusterhost.state.update()
elif self.state == 'UNINITIALIZED':
for clusterhost in self.host.clusterhosts:
if clusterhost.state in [
'INITIALIZED', 'INSTALLING', 'SUCCESSFUL', 'ERROR'
]:
clusterhost.state = 'UNINITIALIZED'
clusterhost.state.update()
elif self.state == 'INITIALIZED':
for clusterhost in self.host.clusterhosts:
if clusterhost.state in [
'INSTALLING', 'SUCCESSFUL', 'ERROR'
]:
clusterhost.state = 'INITIALIZED'
clusterhost.state.update()
super(HostState, self).update()
class Host(BASE, TimestampMixin, HelperMixin):
"""Host table."""
__tablename__ = 'host'
name = Column(String(80), unique=True)
os_id = Column(Integer, ForeignKey('os.id'))
config_step = Column(String(80), default='')
os_config = Column(JSONEncoded, default={})
config_validated = Column(Boolean, default=False)
deployed_os_config = Column(JSONEncoded, default={})
os_name = Column(String(80))
creator_id = Column(Integer, ForeignKey('user.id'))
owner = Column(String(80))
os_installer_id = Column(
Integer,
ForeignKey('os_installer.id')
)
id = Column(
Integer,
ForeignKey('machine.id', onupdate='CASCADE', ondelete='CASCADE'),
primary_key=True
)
reinstall_os = Column(Boolean, default=True)
host_networks = relationship(
HostNetwork,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('host')
)
clusterhosts = relationship(
ClusterHost,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('host')
)
state = relationship(
HostState,
uselist=False,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('host')
)
@hybrid_property
def mac(self):
machine = self.machine
if machine:
return machine.mac
else:
return None
@property
def patched_os_config(self):
return self.os_config
@patched_os_config.setter
def patched_os_config(self, value):
self.os_config = util.merge_dict(dict(self.os_config), value)
logging.info('patch host os config in %s: %s', self.id, value)
self.config_validated = False
@property
def put_os_config(self):
return self.os_config
@put_os_config.setter
def put_os_config(self, value):
os_config = dict(self.os_config)
os_config.update(value)
self.os_config = os_config
logging.info('put host os config in %s: %s', self.id, value)
self.config_validated = False
def __init__(self, id, **kwargs):
self.id = id
self.state = HostState()
super(Host, self).__init__(**kwargs)
def initialize(self):
if not self.name:
self.name = str(self.id)
super(Host, self).initialize()
def update(self):
creator = self.creator
if creator:
self.owner = creator.email
if self.reinstall_os:
if self.state in ['SUCCESSFUL', 'ERROR']:
if self.config_validated:
self.state.state = 'INITIALIZED'
else:
self.state.state = 'UNINITIALIZED'
self.state.update()
os = self.os
if os:
self.os_name = os.name
else:
self.os_name = None
super(Host, self).update()
def validate(self):
super(Host, self).validate()
creator = self.creator
if not creator:
raise exception.InvalidParameter(
'creator is not set in host %s' % self.id
)
os = self.os
if not os:
raise exception.InvalidParameter(
'os is not set in host %s' % self.id
)
os_installer = self.os_installer
if not os_installer:
raise exception.Invalidparameter(
'os_installer is not set in host %s' % self.id
)
if not os.deployable:
raise exception.InvalidParameter(
'os %s is not deployable in host %s' % (os.name, self.id)
)
@property
def os_installed(self):
return self.state.state == 'SUCCESSFUL'
@property
def clusters(self):
return [clusterhost.cluster for clusterhost in self.clusterhosts]
def state_dict(self):
return self.state.to_dict()
def to_dict(self):
dict_info = self.machine.to_dict()
dict_info.update(super(Host, self).to_dict())
state_dict = self.state_dict()
dict_info.update({
'machine_id': self.machine.id,
'os_installed': self.os_installed,
'hostname': self.name,
'networks': [
host_network.to_dict()
for host_network in self.host_networks
],
'os_installer': self.os_installer.to_dict(),
'clusters': [cluster.to_dict() for cluster in self.clusters],
'state': state_dict['state']
})
return dict_info
class ClusterState(BASE, StateMixin):
"""Cluster state table."""
__tablename__ = 'cluster_state'
id = Column(
Integer,
ForeignKey('cluster.id', onupdate='CASCADE', ondelete='CASCADE'),
primary_key=True
)
total_hosts = Column(
Integer,
default=0
)
installing_hosts = Column(
Integer,
default=0
)
completed_hosts = Column(
Integer,
default=0
)
failed_hosts = Column(
Integer,
default=0
)
def to_dict(self):
dict_info = super(ClusterState, self).to_dict()
dict_info['status'] = {
'total_hosts': self.total_hosts,
'installing_hosts': self.installing_hosts,
'completed_hosts': self.completed_hosts,
'failed_hosts': self.failed_hosts
}
return dict_info
def update(self):
cluster = self.cluster
clusterhosts = cluster.clusterhosts
self.total_hosts = len(clusterhosts)
if self.state in ['UNINITIALIZED', 'INITIALIZED']:
self.installing_hosts = 0
self.failed_hosts = 0
self.completed_hosts = 0
if self.state == 'INSTALLING':
cluster.reinstall_distributed_system = False
if not cluster.distributed_system:
for clusterhost in clusterhosts:
host = clusterhost.host
host_state = host.state.state
if host_state == 'INSTALLING':
self.installing_hosts += 1
elif host_state == 'ERROR':
self.failed_hosts += 1
elif host_state == 'SUCCESSFUL':
self.completed_hosts += 1
else:
for clusterhost in clusterhosts:
clusterhost_state = clusterhost.state.state
if clusterhost_state == 'INSTALLING':
self.installing_hosts += 1
elif clusterhost_state == 'ERROR':
self.failed_hosts += 1
elif clusterhost_state == 'SUCCESSFUL':
self.completed_hosts += 1
if self.total_hosts:
self.percentage = (
float(self.completed_hosts)
/
float(self.total_hosts)
)
self.message = (
'total %s, installing %s, completed: %s, error %s'
) % (
self.total_hosts, self.completed_hosts,
self.installing_hosts, self.failed_hosts
)
if self.failed_hosts:
self.severity = 'ERROR'
super(ClusterState, self).update()
class Cluster(BASE, TimestampMixin, HelperMixin):
"""Cluster table."""
__tablename__ = 'cluster'
id = Column(Integer, primary_key=True)
name = Column(String(80), unique=True)
reinstall_distributed_system = Column(Boolean, default=True)
config_step = Column(String(80), default='')
os_id = Column(Integer, ForeignKey('os.id'))
os_name = Column(String(80))
flavor_id = Column(
Integer,
ForeignKey('adapter_flavor.id'),
nullable=True
)
flavor_name = Column(String(80), nullable=True)
distributed_system_id = Column(
Integer, ForeignKey('distributed_system.id'),
nullable=True
)
distributed_system_name = Column(
String(80), nullable=True
)
os_config = Column(JSONEncoded, default={})
package_config = Column(JSONEncoded, default={})
deployed_os_config = Column(JSONEncoded, default={})
deployed_package_config = Column(JSONEncoded, default={})
config_validated = Column(Boolean, default=False)
adapter_id = Column(Integer, ForeignKey('adapter.id'))
adapter_name = Column(String(80), nullable=True)
creator_id = Column(Integer, ForeignKey('user.id'))
owner = Column(String(80))
clusterhosts = relationship(
ClusterHost,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('cluster')
)
state = relationship(
ClusterState,
uselist=False,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('cluster')
)
def __init__(self, name, **kwargs):
self.name = name
self.state = ClusterState()
super(Cluster, self).__init__(**kwargs)
def initialize(self):
super(Cluster, self).initialize()
def update(self):
creator = self.creator
if creator:
self.owner = creator.email
if self.reinstall_distributed_system:
if self.state in ['SUCCESSFUL', 'ERROR']:
if self.config_validated:
self.state.state = 'INITIALIZED'
else:
self.state.state = 'UNINITIALIZED'
self.state.update()
os = self.os
if os:
self.os_name = os.name
else:
self.os_name = None
self.os_config = {}
adapter = self.adapter
if adapter:
self.adapter_name = adapter.name
self.distributed_system = adapter.adapter_distributed_system
self.distributed_system_name = self.distributed_system.name
flavor = self.flavor
if flavor:
self.flavor_name = flavor.name
else:
self.flavor_name = None
else:
self.adapter_name = None
self.distributed_system = None
self.distributed_system_name = None
self.flavor = None
self.flavor_name = None
super(Cluster, self).update()
def validate(self):
super(Cluster, self).validate()
creator = self.creator
if not creator:
raise exception.InvalidParameter(
'creator is not set in cluster %s' % self.id
)
os = self.os
if not os:
raise exception.InvalidParameter(
'os is not set in cluster %s' % self.id
)
if not os.deployable:
raise exception.InvalidParameter(
'os %s is not deployable' % os.name
)
adapter = self.adapter
if not adapter:
raise exception.InvalidParameter(
'adapter is not set in cluster %s' % self.id
)
if not adapter.deployable:
raise exception.InvalidParameter(
'adapter %s is not deployable' % adapter.name
)
supported_os_ids = [
adapter_os.os.id for adapter_os in adapter.supported_oses
]
if os.id not in supported_os_ids:
raise exception.InvalidParameter(
'os %s is not supported' % os.name
)
distributed_system = self.distributed_system
if distributed_system:
if not distributed_system.deployable:
raise exception.InvalidParamerter(
'distributed system %s is not deployable' % (
distributed_system.name
)
)
flavor = self.flavor
if not flavor:
raise exception.InvalidParameter(
'flavor is not set in cluster %s' % self.id
)
flavor_adapter_id = flavor.adapter_id
adapter_id = self.adapter_id
logging.info(
'flavor adapter type %s value %s',
type(flavor_adapter_id), flavor_adapter_id
)
logging.info(
'adapter type %s value %s',
type(adapter_id), adapter_id
)
if flavor_adapter_id != adapter_id:
raise exception.InvalidParameter(
'flavor adapter id %s does not match adapter id %s' % (
flavor_adapter_id, adapter_id
)
)
@property
def patched_os_config(self):
return self.os_config
@patched_os_config.setter
def patched_os_config(self, value):
self.os_config = util.merge_dict(dict(self.os_config), value)
logging.info('patch cluster %s os config: %s', self.id, value)
self.config_validated = False
@property
def put_os_config(self):
return self.os_config
@put_os_config.setter
def put_os_config(self, value):
os_config = dict(self.os_config)
os_config.update(value)
self.os_config = os_config
logging.info('put cluster %s os config: %s', self.id, value)
self.config_validated = False
@property
def patched_package_config(self):
return self.package_config
@patched_package_config.setter
def patched_package_config(self, value):
package_config = dict(self.package_config)
self.package_config = util.merge_dict(package_config, value)
logging.info('patch cluster %s package config: %s', self.id, value)
self.config_validated = False
@property
def put_package_config(self):
return self.package_config
@put_package_config.setter
def put_package_config(self, value):
package_config = dict(self.package_config)
package_config.update(value)
self.package_config = package_config
logging.info('put cluster %s package config: %s', self.id, value)
self.config_validated = False
@property
def distributed_system_installed(self):
return self.state.state == 'SUCCESSFUL'
def state_dict(self):
return self.state.to_dict()
def to_dict(self):
dict_info = super(Cluster, self).to_dict()
dict_info['distributed_system_installed'] = (
self.distributed_system_installed
)
if self.flavor:
dict_info['flavor'] = self.flavor.to_dict()
return dict_info
# User, Permission relation table
class UserPermission(BASE, HelperMixin, TimestampMixin):
"""User permission table."""
__tablename__ = 'user_permission'
id = Column(Integer, primary_key=True)
user_id = Column(
Integer,
ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE')
)
permission_id = Column(
Integer,
ForeignKey('permission.id', onupdate='CASCADE', ondelete='CASCADE')
)
__table_args__ = (
UniqueConstraint('user_id', 'permission_id', name='constraint'),
)
def __init__(self, user_id, permission_id, **kwargs):
self.user_id = user_id
self.permission_id = permission_id
@hybrid_property
def name(self):
return self.permission.name
def to_dict(self):
dict_info = self.permission.to_dict()
dict_info.update(super(UserPermission, self).to_dict())
return dict_info
class Permission(BASE, HelperMixin, TimestampMixin):
"""Permission table."""
__tablename__ = 'permission'
id = Column(Integer, primary_key=True)
name = Column(String(80), unique=True)
alias = Column(String(100))
description = Column(Text)
user_permissions = relationship(
UserPermission,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('permission')
)
def __init__(self, name, **kwargs):
self.name = name
super(Permission, self).__init__(**kwargs)
class UserToken(BASE, HelperMixin):
"""user token table."""
__tablename__ = 'user_token'
id = Column(Integer, primary_key=True)
user_id = Column(
Integer,
ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE')
)
token = Column(String(256), unique=True)
expire_timestamp = Column(
DateTime, default=lambda: datetime.datetime.now()
)
def __init__(self, token, **kwargs):
self.token = token
super(UserToken, self).__init__(**kwargs)
def validate(self):
super(UserToken, self).validate()
if not self.user:
raise exception.InvalidParameter(
'user is not set in token: %s' % self.token
)
class UserLog(BASE, HelperMixin):
"""User log table."""
__tablename__ = 'user_log'
id = Column(Integer, primary_key=True)
user_id = Column(
Integer,
ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE')
)
action = Column(Text)
timestamp = Column(DateTime, default=lambda: datetime.datetime.now())
@hybrid_property
def user_email(self):
return self.user.email
def validate(self):
super(UserLog, self).validate()
if not self.user:
raise exception.InvalidParameter(
'user is not set in user log: %s' % self.id
)
class User(BASE, HelperMixin, TimestampMixin):
"""User table."""
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
email = Column(String(80), unique=True)
crypted_password = Column('password', String(225))
firstname = Column(String(80))
lastname = Column(String(80))
is_admin = Column(Boolean, default=False)
active = Column(Boolean, default=True)
user_permissions = relationship(
UserPermission,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('user')
)
user_logs = relationship(
UserLog,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('user')
)
user_tokens = relationship(
UserToken,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('user')
)
clusters = relationship(
Cluster,
backref=backref('creator')
)
hosts = relationship(
Host,
backref=backref('creator')
)
def __init__(self, email, **kwargs):
self.email = email
super(User, self).__init__(**kwargs)
def validate(self):
super(User, self).validate()
if not self.crypted_password:
raise exception.InvalidParameter(
'password is not set in user : %s' % self.email
)
@property
def password(self):
return '***********'
@password.setter
def password(self, password):
self.crypted_password = util.encrypt(password)
@hybrid_property
def permissions(self):
permissions = []
for user_permission in self.user_permissions:
permissions.append(user_permission.permission)
return permissions
def to_dict(self):
dict_info = super(User, self).to_dict()
dict_info['permissions'] = [
permission.to_dict()
for permission in self.permissions
]
return dict_info
def __str__(self):
return '%s[email:%s,is_admin:%s,active:%s]' % (
self.__class__.__name__,
self.email, self.is_admin, self.active
)
class SwitchMachine(BASE, HelperMixin, TimestampMixin):
"""Switch Machine table."""
__tablename__ = 'switch_machine'
id = Column(
Integer, primary_key=True
)
switch_id = Column(
Integer,
ForeignKey('switch.id', onupdate='CASCADE', ondelete='CASCADE')
)
machine_id = Column(
Integer,
ForeignKey('machine.id', onupdate='CASCADE', ondelete='CASCADE')
)
port = Column(String(80), nullable=True)
vlans = Column(JSONEncoded, default=[])
__table_args__ = (
UniqueConstraint('switch_id', 'machine_id', name='constraint'),
)
def __init__(self, switch_id, machine_id, **kwargs):
self.switch_id = switch_id
self.machine_id = machine_id
super(SwitchMachine, self).__init__(**kwargs)
def validate(self):
super(SwitchMachine, self).validate()
if not self.switch:
raise exception.InvalidParameter(
'switch is not set in %s' % self.id
)
if not self.machine:
raise exception.Invalidparameter(
'machine is not set in %s' % self.id
)
if not self.port:
raise exception.InvalidParameter(
'port is not set in %s' % self.id
)
@hybrid_property
def mac(self):
return self.machine.mac
@hybrid_property
def tag(self):
return self.machine.tag
@property
def switch_ip(self):
return self.switch.ip
@hybrid_property
def switch_ip_int(self):
return self.switch.ip_int
@switch_ip_int.expression
def switch_ip_int(cls):
return Switch.ip_int
@hybrid_property
def switch_vendor(self):
return self.switch.vendor
@switch_vendor.expression
def switch_vendor(cls):
return Switch.vendor
@property
def patched_vlans(self):
return self.vlans
@patched_vlans.setter
def patched_vlans(self, value):
if not value:
return
vlans = list(self.vlans)
for item in value:
if item not in vlans:
vlans.append(item)
self.vlans = vlans
@property
def filtered(self):
filters = self.switch.filters
port = self.port
for port_filter in filters:
logging.debug('apply filter %s on port %s', port_filter, port)
denied = port_filter['filter_type'] != 'allow'
if 'ports' in port_filter:
if port in port_filter['ports']:
logging.debug('port %s is allowed? %s', port, not denied)
return denied
port_prefix = port_filter.get('port_prefix', '')
port_suffix = port_filter.get('port_suffix', '')
pattern = re.compile(r'%s(\d+)%s' % (port_prefix, port_suffix))
match = pattern.match(port)
if match:
logging.debug(
'port %s matches pattern %s',
port, pattern.pattern
)
port_number = match.group(1)
if (
'port_start' not in port_filter or
port_number >= port_filter['port_start']
) and (
'port_end' not in port_filter or
port_number <= port_filter['port_end']
):
logging.debug('port %s is allowed? %s', port, not denied)
return denied
else:
logging.debug(
'port %s does not match pattern %s',
port, pattern.pattern
)
logging.debug('port %s is allowed', port)
return False
def to_dict(self):
dict_info = self.machine.to_dict()
dict_info.update(super(SwitchMachine, self).to_dict())
dict_info['switch_ip'] = self.switch.ip
return dict_info
class Machine(BASE, HelperMixin, TimestampMixin):
"""Machine table."""
__tablename__ = 'machine'
id = Column(Integer, primary_key=True)
mac = Column(String(24), unique=True)
ipmi_credentials = Column(JSONEncoded, default={})
tag = Column(JSONEncoded, default={})
location = Column(JSONEncoded, default={})
switch_machines = relationship(
SwitchMachine,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('machine')
)
host = relationship(
Host,
uselist=False,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('machine')
)
def __init__(self, mac, **kwargs):
self.mac = mac
super(Machine, self).__init__(**kwargs)
def validate(self):
super(Machine, self).validate()
try:
netaddr.EUI(self.mac)
except Exception:
raise exception.InvalidParameter(
'mac address %s format uncorrect' % self.mac
)
@property
def patched_ipmi_credentials(self):
return self.ipmi_credentials
@patched_ipmi_credentials.setter
def patched_ipmi_credentials(self, value):
self.ipmi_credentials = (
util.merge_dict(dict(self.ipmi_credentials), value)
)
@property
def patched_tag(self):
return self.tag
@patched_tag.setter
def patched_tag(self, value):
tag = dict(self.tag)
tag.update(value)
self.tag = value
@property
def patched_location(self):
return self.location
@patched_location.setter
def patched_location(self, value):
location = dict(self.location)
location.update(value)
self.location = location
def to_dict(self):
dict_info = {}
dict_info['switches'] = [
{
'switch_ip': switch_machine.switch_ip,
'port': switch_machine.port,
'vlans': switch_machine.vlans
}
for switch_machine in self.switch_machines
if not switch_machine.filtered
]
if dict_info['switches']:
dict_info.update(dict_info['switches'][0])
dict_info.update(super(Machine, self).to_dict())
return dict_info
class Switch(BASE, HelperMixin, TimestampMixin):
"""Switch table."""
__tablename__ = 'switch'
id = Column(Integer, primary_key=True)
ip_int = Column('ip', BigInteger, unique=True)
credentials = Column(JSONEncoded, default={})
vendor = Column(String(256), nullable=True)
state = Column(Enum('initialized', 'unreachable', 'notsupported',
'repolling', 'error', 'under_monitoring',
name='switch_state'),
ColumnDefault('initialized'))
filters = Column(JSONEncoded, default=[])
switch_machines = relationship(
SwitchMachine,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('switch')
)
def __init__(self, ip_int, **kwargs):
self.ip_int = ip_int
super(Switch, self).__init__(**kwargs)
@property
def ip(self):
return str(netaddr.IPAddress(self.ip_int))
@ip.setter
def ip(self, ipaddr):
self.ip_int = int(netaddr.IPAddress(ipaddr))
@property
def patched_credentials(self):
return self.credentials
@patched_credentials.setter
def patched_credentials(self, value):
self.credentials = util.merge_dict(dict(self.credentials), value)
@property
def patched_filters(self):
return self.filters
@patched_filters.setter
def patched_filters(self, value):
if not value:
return
filters = list(self.filters)
for item in value:
found_filter = False
for switch_filter in filters:
if switch_filter['filter_name'] == item['filter_name']:
switch_filter.update(item)
found_filter = True
break
if not found_filter:
filters.append(item)
self.filters = filters
def to_dict(self):
dict_info = super(Switch, self).to_dict()
dict_info['ip'] = self.ip
return dict_info
class OSConfigMetadata(BASE, MetadataMixin):
"""OS config metadata."""
__tablename__ = "os_config_metadata"
id = Column(Integer, primary_key=True)
os_id = Column(
Integer,
ForeignKey(
'os.id', onupdate='CASCADE', ondelete='CASCADE'
)
)
parent_id = Column(
Integer,
ForeignKey(
'os_config_metadata.id', onupdate='CASCADE', ondelete='CASCADE'
)
)
field_id = Column(
Integer,
ForeignKey(
'os_config_field.id', onupdate='CASCADE', ondelete='CASCADE'
)
)
children = relationship(
'OSConfigMetadata',
passive_deletes=True, passive_updates=True,
backref=backref('parent', remote_side=id)
)
__table_args__ = (
UniqueConstraint('path', 'os_id', name='constraint'),
)
def __init__(self, path, **kwargs):
self.path = path
super(OSConfigMetadata, self).__init__(**kwargs)
def validate(self):
super(OSConfigMetadata, self).validate()
if not self.os:
raise exception.InvalidParameter(
'os is not set in os metadata %s' % self.id
)
class OSConfigField(BASE, FieldMixin):
"""OS config fields."""
__tablename__ = 'os_config_field'
metadatas = relationship(
OSConfigMetadata,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('field'))
def __init__(self, field, **kwargs):
self.field = field
super(OSConfigField, self).__init__(**kwargs)
class AdapterOS(BASE, HelperMixin):
"""Adapter OS table."""
__tablename__ = 'adapter_os'
id = Column(Integer, primary_key=True)
os_id = Column(
Integer,
ForeignKey(
'os.id',
onupdate='CASCADE', ondelete='CASCADE'
)
)
adapter_id = Column(
Integer,
ForeignKey(
'adapter.id',
onupdate='CASCADE', ondelete='CASCADE'
)
)
def __init__(self, os_id, adapter_id, **kwargs):
self.os_id = os_id
self.adapter_id = adapter_id
super(AdapterOS, self).__init__(**kwargs)
def to_dict(self):
dict_info = self.os.to_dict()
dict_info.update(super(AdapterOS, self).to_dict())
return dict_info
class OperatingSystem(BASE, HelperMixin):
"""OS table."""
__tablename__ = 'os'
id = Column(Integer, primary_key=True)
parent_id = Column(
Integer,
ForeignKey('os.id', onupdate='CASCADE', ondelete='CASCADE'),
nullable=True
)
name = Column(String(80), unique=True)
deployable = Column(Boolean, default=False)
metadatas = relationship(
OSConfigMetadata,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('os')
)
clusters = relationship(
Cluster,
backref=backref('os')
)
hosts = relationship(
Host,
backref=backref('os')
)
children = relationship(
'OperatingSystem',
passive_deletes=True, passive_updates=True,
backref=backref('parent', remote_side=id)
)
supported_adapters = relationship(
AdapterOS,
passive_deletes=True, passive_updates=True,
backref=backref('os')
)
def __init__(self, name):
self.name = name
super(OperatingSystem, self).__init__()
@property
def root_metadatas(self):
return [
metadata for metadata in self.metadatas
if metadata.parent_id is None
]
def metadata_dict(self):
dict_info = {}
if self.parent:
dict_info.update(self.parent.metadata_dict())
for metadata in self.root_metadatas:
dict_info.update(metadata.to_dict())
return dict_info
class AdapterFlavorRole(BASE, HelperMixin):
"""Adapter flavor roles."""
__tablename__ = 'adapter_flavor_role'
flavor_id = Column(
Integer,
ForeignKey(
'adapter_flavor.id',
onupdate='CASCADE', ondelete='CASCADE'
),
primary_key=True
)
role_id = Column(
Integer,
ForeignKey(
'adapter_role.id',
onupdate='CASCADE', ondelete='CASCADE'
),
primary_key=True
)
def __init__(self, flavor_id, role_id):
self.flavor_id = flavor_id
self.role_id = role_id
super(AdapterFlavorRole, self).__init__()
def validate(self):
super(AdapterFlavorRole, self).validate()
flavor_adapter_id = self.flavor.adapter_id
role_adapter_id = self.role.adapter_id
if flavor_adapter_id != role_adapter_id:
raise exception.InvalidParameter(
'flavor adapter %s and role adapter %s does not match' % (
flavor_adapter_id, role_adapter_id
)
)
def to_dict(self):
dict_info = super(AdapterFlavorRole, self).to_dict()
dict_info.update(
self.role.to_dict()
)
return dict_info
class AdapterFlavor(BASE, HelperMixin):
"""Adapter's flavors."""
__tablename__ = 'adapter_flavor'
id = Column(Integer, primary_key=True)
adapter_id = Column(
Integer,
ForeignKey('adapter.id', onupdate='CASCADE', ondelete='CASCADE')
)
name = Column(String(80), unique=True)
display_name = Column(String(80))
template = Column(String(80))
flavor_roles = relationship(
AdapterFlavorRole,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('flavor')
)
clusters = relationship(
Cluster,
backref=backref('flavor')
)
__table_args__ = (
UniqueConstraint('name', 'adapter_id', name='constraint'),
)
def __init__(self, name, adapter_id, **kwargs):
self.name = name
self.adapter_id = adapter_id
super(AdapterFlavor, self).__init__(**kwargs)
def initialize(self):
if not self.display_name:
self.display_name = self.name
super(AdapterFlavor, self).initialize()
def validate(self):
super(AdapterFlavor, self).validate()
if not self.template:
raise exception.InvalidParameter(
'template is not set in adapter flavor %s' % self.id
)
def to_dict(self):
dict_info = super(AdapterFlavor, self).to_dict()
dict_info['roles'] = [
flavor_role.to_dict() for flavor_role in self.flavor_roles
]
return dict_info
class AdapterRole(BASE, HelperMixin):
"""Adapter's roles."""
__tablename__ = "adapter_role"
id = Column(Integer, primary_key=True)
name = Column(String(80))
display_name = Column(String(80))
description = Column(Text)
optional = Column(Boolean)
adapter_id = Column(
Integer,
ForeignKey(
'adapter.id',
onupdate='CASCADE',
ondelete='CASCADE'
)
)
flavor_roles = relationship(
AdapterFlavorRole,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('role')
)
__table_args__ = (
UniqueConstraint('name', 'adapter_id', name='constraint'),
)
def __init__(self, name, adapter_id, **kwargs):
self.name = name
self.adapter_id = adapter_id
super(AdapterRole, self).__init__(**kwargs)
def initialize(self):
if not self.description:
self.description = self.name
if not self.display_name:
self.display_name = self.name
super(AdapterRole, self).initialize()
class PackageConfigMetadata(BASE, MetadataMixin):
"""package config metadata."""
__tablename__ = "package_config_metadata"
id = Column(Integer, primary_key=True)
adapter_id = Column(
Integer,
ForeignKey(
'adapter.id',
onupdate='CASCADE', ondelete='CASCADE'
)
)
parent_id = Column(
Integer,
ForeignKey(
'package_config_metadata.id',
onupdate='CASCADE', ondelete='CASCADE'
)
)
field_id = Column(
Integer,
ForeignKey(
'package_config_field.id',
onupdate='CASCADE', ondelete='CASCADE'
)
)
children = relationship(
'PackageConfigMetadata',
passive_deletes=True, passive_updates=True,
backref=backref('parent', remote_side=id)
)
__table_args__ = (
UniqueConstraint('path', 'adapter_id', name='constraint'),
)
def __init__(
self, path, **kwargs
):
self.path = path
super(PackageConfigMetadata, self).__init__(**kwargs)
def validate(self):
super(PackageConfigMetadata, self).validate()
if not self.adapter:
raise exception.InvalidParameter(
'adapter is not set in package metadata %s' % self.id
)
class PackageConfigField(BASE, FieldMixin):
"""Adapter cofig metadata fields."""
__tablename__ = "package_config_field"
metadatas = relationship(
PackageConfigMetadata,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('field'))
def __init__(self, field, **kwargs):
self.field = field
super(PackageConfigField, self).__init__(**kwargs)
class Adapter(BASE, HelperMixin):
"""Adapter table."""
__tablename__ = 'adapter'
id = Column(Integer, primary_key=True)
name = Column(String(80), unique=True)
display_name = Column(String(80))
parent_id = Column(
Integer,
ForeignKey(
'adapter.id',
onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
distributed_system_id = Column(
Integer,
ForeignKey(
'distributed_system.id',
onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
os_installer_id = Column(
Integer,
ForeignKey(
'os_installer.id',
onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
package_installer_id = Column(
Integer,
ForeignKey(
'package_installer.id',
onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
deployable = Column(
Boolean, default=False
)
supported_oses = relationship(
AdapterOS,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('adapter')
)
roles = relationship(
AdapterRole,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('adapter')
)
flavors = relationship(
AdapterFlavor,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('adapter')
)
children = relationship(
'Adapter',
passive_deletes=True, passive_updates=True,
backref=backref('parent', remote_side=id)
)
metadatas = relationship(
PackageConfigMetadata,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('adapter')
)
clusters = relationship(
Cluster,
backref=backref('adapter')
)
__table_args__ = (
UniqueConstraint(
'distributed_system_id',
'os_installer_id', 'package_installer_id', name='constraint'
),
)
def __init__(
self, name, **kwargs
):
self.name = name
super(Adapter, self).__init__(**kwargs)
def initialize(self):
if not self.display_name:
self.display_name = self.name
super(Adapter, self).initialize()
@property
def root_metadatas(self):
return [
metadata for metadata in self.metadatas
if metadata.parent_id is None
]
def metadata_dict(self):
dict_info = {}
if self.parent:
dict_info.update(self.parent.metadata_dict())
for metadata in self.root_metadatas:
dict_info.update(metadata.to_dict())
return dict_info
@property
def adapter_package_installer(self):
if self.package_installer:
return self.package_installer
elif self.parent:
return self.parent.adapter_package_installer
else:
return None
@property
def adapter_os_installer(self):
if self.os_installer:
return self.os_installer
elif self.parent:
return self.parent.adapter_os_installer
else:
return None
@property
def adapter_distributed_system(self):
distributed_system = self.distributed_system
if distributed_system:
return distributed_system
parent = self.parent
if parent:
return parent.adapter_distributed_system
else:
return None
@property
def adapter_supported_oses(self):
supported_oses = self.supported_oses
if supported_oses:
return supported_oses
parent = self.parent
if parent:
return parent.adapter_supported_oses
else:
return []
@property
def adapter_roles(self):
roles = self.roles
if roles:
return roles
parent = self.parent
if parent:
return parent.adapter_roles
else:
return []
@property
def adapter_flavors(self):
flavors = self.flavors
if flavors:
return flavors
parent = self.parent
if parent:
return parent.adapter_flavors
else:
return []
def to_dict(self):
dict_info = super(Adapter, self).to_dict()
dict_info.update({
'roles': [
role.to_dict() for role in self.adapter_roles
],
'supported_oses': [
adapter_os.to_dict()
for adapter_os in self.adapter_supported_oses
],
'flavors': [
flavor.to_dict() for flavor in self.adapter_flavors
]
})
distributed_system = self.adapter_distributed_system
if distributed_system:
dict_info['distributed_system_id'] = distributed_system.id
dict_info['distributed_system_name'] = distributed_system.name
os_installer = self.adapter_os_installer
if os_installer:
dict_info['os_installer'] = os_installer.to_dict()
package_installer = self.adapter_package_installer
if package_installer:
dict_info['package_installer'] = package_installer.to_dict()
return dict_info
class DistributedSystem(BASE, HelperMixin):
"""distributed system table."""
__tablename__ = 'distributed_system'
id = Column(Integer, primary_key=True)
parent_id = Column(
Integer,
ForeignKey(
'distributed_system.id',
onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
name = Column(String(80), unique=True)
deployable = Column(Boolean, default=False)
adapters = relationship(
Adapter,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('distributed_system')
)
clusters = relationship(
Cluster,
backref=backref('distributed_system')
)
children = relationship(
'DistributedSystem',
passive_deletes=True, passive_updates=True,
backref=backref('parent', remote_side=id)
)
def __init__(self, name):
self.name = name
super(DistributedSystem, self).__init__()
class OSInstaller(BASE, InstallerMixin):
"""OS installer table."""
__tablename__ = 'os_installer'
id = Column(Integer, primary_key=True)
adpaters = relationship(
Adapter,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('os_installer')
)
hosts = relationship(
Host,
backref=backref('os_installer')
)
def __init__(self, alias, **kwargs):
self.alias = alias
super(OSInstaller, self).__init__(**kwargs)
class PackageInstaller(BASE, InstallerMixin):
"""package installer table."""
__tablename__ = 'package_installer'
id = Column(Integer, primary_key=True)
adapters = relationship(
Adapter,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('package_installer')
)
def __init__(self, alias, **kwargs):
self.alias = alias
super(PackageInstaller, self).__init__(**kwargs)
class Subnet(BASE, TimestampMixin, HelperMixin):
"""network table."""
__tablename__ = 'subnet'
id = Column(Integer, primary_key=True)
name = Column(String(80), unique=True)
subnet = Column(String(80), unique=True)
host_networks = relationship(
HostNetwork,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('subnet')
)
def __init__(self, subnet, **kwargs):
self.subnet = subnet
super(Subnet, self).__init__(**kwargs)
def initialize(self):
if not self.name:
self.name = self.subnet
super(Subnet, self).initialize()
class LogProgressingHistory(BASE, TimestampMixin, HelperMixin):
"""host installing log history for each file.
:param id: int, identity as primary key.
:param pathname: str, the full path of the installing log file. unique.
:param position: int, the position of the log file it has processed.
:param partial_line: str, partial line of the log.
:param progressing: float, indicate the installing progress between 0 to 1.
:param message: str, str, the installing message.
:param severity: Enum, the installing message severity.
('ERROR', 'WARNING', 'INFO')
:param line_matcher_name: str, the line matcher name of the log processor.
"""
__tablename__ = 'log_progressing_history'
id = Column(Integer, primary_key=True)
pathname = Column(String(80), unique=True)
position = Column(Integer, default=0)
partial_line = Column(Text, default='')
percentage = Column(Float, default=0.0)
message = Column(Text, default='')
severity = Column(
Enum('ERROR', 'WARNING', 'INFO'),
ColumnDefault('INFO')
)
line_matcher_name = Column(
String(80), default='start'
)
def __init__(self, pathname, **kwargs):
self.pathname = pathname
super(LogProgressingHistory, self).__init__(**kwargs)
def __repr__(self):
return (
'LogProgressingHistory[%r: position %r,'
'partial_line %r,percentage %r,message %r,'
'severity %r]'
) % (
self.pathname, self.position,
self.partial_line,
self.percentage,
self.message,
self.severity
)