compass-core/compass/db/models.py
xiaodongwang ffed509623 update apiv2 code
Change-Id: I30057083bd29f2324f1ad4c0be34b60b696db2c6
2014-07-22 11:51:30 -07:00

1878 lines
52 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 netaddr
import simplejson as json
from sqlalchemy import BigInteger
from sqlalchemy import Boolean
from sqlalchemy import Column
from sqlalchemy import DateTime
from sqlalchemy import Enum
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.ext.mutable import MutableDict
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):
pass
def validate(self):
pass
def to_dict(self):
keys = self.__mapper__.columns.keys()
dict_info = {}
for key in keys:
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(JSONEncoded)
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:
self.display_name = self.name
if self.parent:
self.path = '%s/%s' % (self.parent.path, self.name)
else:
self.path = self.name
super(MetadataMixin, self).initialize()
def validate(self):
if not self.adapter:
raise exception.InvalidParameter(
'adapter is not set in os metadata %s' % self.id
)
super(MetadataMixin, self).validate()
@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):
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'),
default='basestring'
)
display_type = Column(
Enum(
'checkbox', 'radio', 'select',
'multiselect', 'combobox', 'text',
'multitext', 'password'
),
default='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 AdapterMixin(HelperMixin):
name = Column(String(80), unique=True)
@property
def root_metadatas(self):
return [
metadata for metadata in self.metadatas
if metadata.parent_id is None
]
@property
def adapter_installer(self):
if self.installer:
return self.installer
elif self.parent:
return self.parent.adapter_installer
else:
return None
@property
def installer_name(self):
installer = self.adapter_installer
if installer:
return installer.name
else:
return ''
@property
def installer_type(self):
installer = self.adapter_installer
if installer:
return installer.installer_type
else:
return None
@property
def installer_config(self):
installer = self.adapter_installer
if installer:
return installer.config
else:
return 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
def to_dict(self):
dict_info = super(AdapterMixin, self).to_dict()
dict_info.update({
'installer_name': self.installer_name,
'installer_type': self.installer_type,
'installer_config': self.installer_config
})
return dict_info
class InstallerMixin(HelperMixin):
name = Column(String(80), unique=True)
installer_type = Column(String(80))
config = Column(MutableDict.as_mutable(JSONEncoded), default={})
def validate(self):
if not self.installer_type:
raise exception.InvalidParameter(
'installer_type is not set in installer %s' % self.name
)
super(InstallerMixin, self).validate()
class StateMixin(TimestampMixin, HelperMixin):
state = Column(
Enum(
'INITIALIZED', 'INSTALLING', 'SUCCESSFUL', 'ERROR'
),
default='INITIALIZED'
)
progress = Column(Float, default=0.0)
message = Column(Text, default='')
severity = Column(
Enum('INFO', 'WARNING', 'ERROR'),
default='INFO'
)
def initialize(self):
if self.severity == 'ERROR':
self.state = 'ERROR'
elif self.progress >= 1.0:
self.state = 'SUCCESSFUL'
self.progress = 1.0
super(StateMixin, self).initialize()
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('network.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, **kwargs):
self.host_id = host_id
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.network.subnet
@property
def netmask(self):
return str(netaddr.IPNetwork(self.subnet).netmask)
def validate(self):
if not self.interface:
raise exception.InvalidParameter(
'interface is not set in host %s network' % self.host_id
)
if not self.network:
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
)
)
try:
netaddr.IPAddress(self.ip_int)
except Exception:
raise exception.InvalidParameter(
'ip %s format is uncorrect in %s interface %s' % (
self.ip_int, 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)
)
)
super(HostNetwork, self).validate()
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
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
)
class ClusterHost(BASE, TimestampMixin, HelperMixin):
"""ClusterHost table."""
__tablename__ = 'clusterhost'
id = Column(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')
)
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('host')
)
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)
@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)
@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
@hybrid_property
def distributed_system_name(self):
cluster = self.cluster
if cluster:
return cluster.distributed_system_name
else:
return None
@hybrid_property
def os_name(self):
host = self.host
if host:
return host.os_name
else:
return None
@hybrid_property
def clustername(self):
cluster = self.cluster
if cluster:
return cluster.name
else:
return None
@hybrid_property
def hostname(self):
host = self.host
if host:
return host.name
else:
return None
@property
def distributed_system_installed(self):
state = self.state
if state:
return state.state == 'SUCCESSFUL'
else:
return False
@property
def os_installed(self):
host = self.host
if host:
return host.os_installed
else:
return None
@property
def owner(self):
cluster = self.cluster
if cluster:
return cluster.owner
else:
return None
def state_dict(self):
state = self.state
if state.progress <= 0.0:
host = self.host
if host:
dict_info = host.state_dict()
else:
dict_info = {}
cluster = self.cluster
if cluster and cluster.distributed_system:
dict_info['state'] = state.state
else:
dict_info = state.to_dict()
return dict_info
def to_dict(self):
dict_info = self.host.to_dict()
dict_info.update(super(ClusterHost, self).to_dict())
dict_info.update({
'distributed_system_name': self.distributed_system_name,
'distributed_system_installed': self.distributed_system_installed,
'reinstall_distributed_system': (
self.cluster.reinstall_distributed_system
),
'owner': self.owner,
'name': self.name
})
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 initialize(self):
if self.state == 'INSTALLING':
self.host.reinstall_os = False
super(HostState, self).initialize()
class Host(BASE, TimestampMixin, HelperMixin):
"""Host table."""
__tablename__ = 'host'
name = Column(String(80), unique=True)
adapter_id = Column(Integer, ForeignKey('os_adapter.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_id = Column(
Integer,
ForeignKey('os.id')
)
creator_id = Column(Integer, ForeignKey('user.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)
@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
def __init__(self, id, **kwargs):
self.id = id
super(Host, self).__init__(**kwargs)
def initialize(self):
if not self.name:
self.name = str(self.id)
if not self.state or self.reinstall_os:
self.state = HostState()
super(Host, self).initialize()
def validate(self):
adapter = self.adapter
if not adapter:
raise exception.InvalidParameter(
'adapter is not set in host %s' % self.id
)
if not self.os:
if adapter:
self.os = adapter.adapter_os
else:
raise exception.InvalidParameter(
'os is not set in host %s' % self.id
)
if not self.creator:
raise exception.InvalidParameter(
'creator is not set in host %s' % self.id
)
super(Host, self).validate()
@hybrid_property
def os_name(self):
os = self.os
if os:
return os.name
else:
return None
@hybrid_property
def owner(self):
creator = self.creator
if creator:
return creator.email
else:
return None
@property
def os_installed(self):
state = self.state
if state:
return state.state == 'SUCCESSFUL'
else:
return False
def state_dict(self):
state = self.state
if state:
return state.to_dict()
else:
return {}
def to_dict(self):
dict_info = self.machine.to_dict()
dict_info.update(super(Host, self).to_dict())
dict_info.update({
'os_name': self.os_name,
'owner': self.owner,
'os_installed': self.os_installed,
})
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
)
def initialize(self):
cluster = self.cluster
if self.state == 'INSTALLING':
cluster.reinstall_distributed_system = False
clusterhosts = cluster.clusterhosts
total_clusterhosts = 0
failed_clusterhosts = 0
installing_clusterhosts = 0
finished_clusterhosts = 0
progress = 0
if not cluster.distributed_system:
for clusterhost in clusterhosts:
host = clusterhost.host
host_state = host.state.state
total_clusterhosts += 1
progress += host.state.progress
if host_state == 'SUCCESSFUL':
finished_clusterhosts += 1
elif host_state == 'INSTALLING':
installing_clusterhosts += 1
elif host_state == 'ERROR':
failed_clusterhosts += 1
else:
for clusterhost in clusterhosts:
clusterhost_state = clusterhost.state.state
total_clusterhosts += 1
progress += clusterhost.state.progress
if clusterhost_state == 'SUCCESSFUL':
finished_clusterhosts += 1
elif clusterhost_state == 'INSTALLING':
installing_clusterhosts += 1
elif clusterhost_state == 'ERROR':
failed_clusterhosts += 1
self.progress = progress / total_clusterhosts
self.message = (
'toal %s, installing %s, finished %s, error $s'
) % (
total_clusterhosts, installing_clusterhosts,
finished_clusterhosts, failed_clusterhosts
)
if failed_clusterhosts:
self.severity = 'ERROR'
super(ClusterState, self).initialize()
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'), nullable=True)
distributed_system_id = Column(
Integer, ForeignKey('distributed_system.id'),
nullable=True
)
os_config = Column(JSONEncoded, default={})
package_config = Column(JSONEncoded, default={})
config_validated = Column(Boolean, default=False)
adapter_id = Column(Integer, ForeignKey('adapter.id'))
creator_id = Column(Integer, ForeignKey('user.id'))
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
super(Cluster, self).__init__(**kwargs)
def initialize(self):
if not self.state or self.reinstall_distributed_system:
self.state = ClusterState()
super(Cluster, self).initialize()
def validate(self):
adapter = self.adapter
if not adapter:
raise exception.InvalidParameter(
'adapter is not set in cluster %s' % self.id
)
creator = self.creator
if not creator:
raise exception.InvalidParameter(
'creator is not set in cluster %s' % self.id
)
os = self.os
if not os:
os_adapter = adapter.os_adapter
if os_adapter:
self.os = os_adapter.adapter_os
else:
self.os = None
if not self.distributed_system:
package_adapter = adapter.package_adapter
if package_adapter:
self.distributed_system = (
package_adapter.adapter_distributed_system
)
else:
self.distributed_system = None
super(Cluster, self).validate()
@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)
@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
@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)
@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
@hybrid_property
def owner(self):
creator = self.creator
if creator:
return creator.email
else:
return None
@hybrid_property
def os_name(self):
os = self.os
if os:
return os.name
else:
return None
@hybrid_property
def distributed_system_name(self):
distributed_system = self.distributed_system
if distributed_system:
return distributed_system.name
else:
return None
@property
def distributed_system_installed(self):
state = self.state
if state:
return self.state.state == 'SUCCESSFUL'
else:
return False
def state_dict(self):
state = self.state
if state:
return self.state.to_dict()
else:
return {}
def to_dict(self):
dict_info = super(Cluster, self).to_dict()
dict_info.update({
'os_name': self.os_name,
'distributed_system_name': self.distributed_system_name,
'distributed_system_installed': self.distributed_system_installed,
'owner': self.owner,
})
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):
if not self.user:
raise exception.InvalidParameter(
'user is not set in token: %s' % self.token
)
super(UserToken, self).validate()
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):
if not self.user:
raise exception.InvalidParameter(
'user is not set in user log: %s' % self.id
)
super(UserLog, self).validate()
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):
if not self.crypted_password:
raise exception.InvalidParameter(
'password is not set in user : %s' % self.email
)
super(User, self).validate()
@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)
@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
@hybrid_property
def switch_vendor(self):
return self.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
def to_dict(self):
dict_info = self.machine.to_dict()
dict_info.update(super(SwitchMachine, self).to_dict())
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):
try:
netaddr.EUI(self.mac)
except Exception:
raise exception.InvalidParameter(
'mac address %s format uncorrect' % self.mac
)
super(Machine, self).validate()
@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
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'),
default='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 Adapter(BASE, HelperMixin):
"""Adpater table."""
__tablename__ = 'adapter'
id = Column(Integer, primary_key=True)
package_adapter_id = Column(
Integer,
ForeignKey(
'package_adapter.id', onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
os_adapter_id = Column(
Integer,
ForeignKey(
'os_adapter.id', onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
__table_args__ = (
UniqueConstraint(
'package_adapter_id', 'os_adapter_id', name='constraint'
),
)
clusters = relationship(
Cluster,
backref=backref('adapter')
)
def __init__(self, os_adapter_id, package_adapter_id, **kwargs):
self.os_adapter_id = os_adapter_id
self.package_adapter_id = package_adapter_id
super(Adapter, self).__init__(**kwargs)
def metadata_dict(self):
dict_info = {}
if self.os_adapter:
dict_info['os_config'] = self.os_adapter.metadata_dict()
if self.package_adapter:
dict_info['package_config'] = self.package_adapter.metadata_dict()
return dict_info
def to_dict(self):
dict_info = super(Adapter, self).to_dict()
os_adapter = self.os_adapter
if os_adapter:
dict_info['os_adapter'] = os_adapter.to_dict()
package_adapter = self.package_adapter
if package_adapter:
dict_info['package_adapter'] = package_adapter.to_dict()
return dict_info
class OSConfigMetadata(BASE, MetadataMixin):
"""OS config metadata."""
__tablename__ = "os_config_metadata"
id = Column(Integer, primary_key=True)
adapter_id = Column(
Integer,
ForeignKey(
'os_adapter.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', 'adapter_id', name='constraint'),
)
def __init__(self, name, **kwargs):
self.name = name
super(OSConfigMetadata, self).__init__(**kwargs)
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 OSAdapter(BASE, AdapterMixin):
"""OS adpater table."""
__tablename__ = 'os_adapter'
id = Column(Integer, primary_key=True)
parent_id = Column(
Integer,
ForeignKey('os_adapter.id', onupdate='CASCADE', ondelete='CASCADE'),
nullable=True
)
os_id = Column(
Integer,
ForeignKey('os.id', onupdate='CASCADE', ondelete='CASCADE'),
nullable=True
)
installer_id = Column(
Integer,
ForeignKey('os_installer.id', onupdate='CASCADE', ondelete='CASCADE'),
nullable=True
)
children = relationship(
'OSAdapter',
passive_deletes=True, passive_updates=True,
backref=backref('parent', remote_side=id)
)
adapters = relationship(
Adapter,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('os_adapter')
)
metadatas = relationship(
OSConfigMetadata,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('adapter')
)
hosts = relationship(
Host,
backref=backref('adapter')
)
__table_args__ = (
UniqueConstraint('os_id', 'installer_id', name='constraint'),
)
def __init__(self, name, **kwargs):
self.name = name
super(OSAdapter, self).__init__(**kwargs)
@property
def deployable(self):
os = self.adapter_os
installer = self.adapter_installer
if (
os and os.deployable and installer
):
return True
else:
return False
@property
def adapter_os(self):
os = self.os
if os:
return os
parent = self.parent
if parent:
return parent.adapter_os
else:
return None
@property
def os_name(self):
os = self.adapter_os
if os:
return os.name
else:
return ''
def to_dict(self):
dict_info = super(OSAdapter, self).to_dict()
dict_info['os_name'] = self.os_name
return dict_info
class OSInstaller(BASE, InstallerMixin):
"""OS installer table."""
__tablename__ = 'os_installer'
id = Column(Integer, primary_key=True)
adpaters = relationship(
OSAdapter,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('installer')
)
def __init__(self, name, **kwargs):
self.name = name
super(OSInstaller, self).__init__(**kwargs)
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)
adapters = relationship(
OSAdapter,
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)
)
def __init__(self, name):
self.name = name
super(OperatingSystem, self).__init__()
class PackageAdapterRole(BASE, HelperMixin):
"""Adapter's roles."""
__tablename__ = "package_adapter_role"
id = Column(Integer, primary_key=True)
name = Column(String(80))
description = Column(Text)
optional = Column(Boolean)
adapter_id = Column(
Integer,
ForeignKey(
'package_adapter.id',
onupdate='CASCADE',
ondelete='CASCADE'
)
)
__table_args__ = (
UniqueConstraint('name', 'adapter_id', name='constraint'),
)
def __init__(self, name, adapter_id, **kwargs):
self.name = name
self.adapter_id = adapter_id
super(PackageAdapterRole, self).__init__(**kwargs)
class PackageConfigMetadata(BASE, MetadataMixin):
"""package config metadata."""
__tablename__ = "package_config_metadata"
id = Column(Integer, primary_key=True)
adapter_id = Column(
Integer,
ForeignKey(
'package_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, name, **kwargs
):
self.name = name
super(PackageConfigMetadata, self).__init__(**kwargs)
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 PackageAdapter(BASE, AdapterMixin):
"""Adapter table."""
__tablename__ = 'package_adapter'
id = Column(Integer, primary_key=True)
name = Column(String(80), unique=True)
parent_id = Column(
Integer,
ForeignKey(
'package_adapter.id',
onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
distributed_system_id = Column(
Integer,
ForeignKey(
'distributed_system.id',
onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
installer_id = Column(
Integer,
ForeignKey(
'package_installer.id',
onupdate='CASCADE', ondelete='CASCADE'
),
nullable=True
)
supported_os_patterns = Column(JSONEncoded, nullable=True)
roles = relationship(
PackageAdapterRole,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('adapter')
)
children = relationship(
'PackageAdapter',
passive_deletes=True, passive_updates=True,
backref=backref('parent', remote_side=id)
)
adapters = relationship(
Adapter,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('package_adapter')
)
metadatas = relationship(
PackageConfigMetadata,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('adapter')
)
__table_args__ = (
UniqueConstraint(
'distributed_system_id',
'installer_id', name='constraint'
),
)
def __init__(
self, name, **kwargs
):
self.name = name
super(PackageAdapter, self).__init__(**kwargs)
@property
def deployable(self):
distributed_system = self.adapter_distributed_system
installer = self.adapter_installer
if (
distributed_system and distributed_system.deployable and
installer
):
return True
else:
return False
@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 distributed_system_name(self):
distributed_system = self.adapter_distributed_system
if distributed_system:
return distributed_system.name
else:
return ''
@property
def adapter_supported_os_patterns(self):
supported_os_patterns = self.supported_os_patterns
if supported_os_patterns:
return supported_os_patterns
parent = self.parent
if parent:
return parent.adapter_supported_os_patterns
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 []
def to_dict(self):
dict_info = super(PackageAdapter, self).to_dict()
roles = []
for role in self.adapter_roles:
roles.append(role.to_dict())
dict_info['roles'] = roles
dict_info['supported_os_patterns'] = self.adapter_supported_os_patterns
dict_info['distributed_system'] = self.distributed_system_name
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(
PackageAdapter,
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 PackageInstaller(BASE, InstallerMixin):
"""package installer table."""
__tablename__ = 'package_installer'
id = Column(Integer, primary_key=True)
adapters = relationship(
PackageAdapter,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('installer')
)
def __init__(self, name, **kwargs):
self.name = name
super(PackageInstaller, self).__init__(**kwargs)
class Network(BASE, TimestampMixin, HelperMixin):
"""network table."""
__tablename__ = 'network'
id = Column(Integer, primary_key=True)
subnet = Column(String(80), unique=True)
host_networks = relationship(
HostNetwork,
passive_deletes=True, passive_updates=True,
cascade='all, delete-orphan',
backref=backref('network')
)
def __init__(self, subnet, **kwargs):
self.subnet = subnet
super(Network, self).__init__(**kwargs)
def intialize(self):
try:
netaddr.IPNetwork(self.subnet)
except Exception:
raise exception.InvalidParameter(
'subnet %s format is uncorrect' % self.subnet
)
super(Network, self).intialize()