summaryrefslogtreecommitdiffstats
path: root/compass-tasks/db/models.py
diff options
context:
space:
mode:
authorHarry Huang <huangxiangyu5@huawei.com>2017-11-17 14:53:44 +0800
committerHarry Huang <huangxiangyu5@huawei.com>2017-12-21 16:36:30 +0800
commit8646b8d62cf4ca7b6bccae537a0c9e72ba45eab3 (patch)
tree73a9a983e0dd1423e9df928a78a5023a09d5a7f9 /compass-tasks/db/models.py
parent6234176ae292a75dcda5520324cb7857d6105988 (diff)
Merge compass-tasks-osa and compass-tasks-k8s
JIRA: COMPASS-568 rename compass-tasks to compass-tasks-base. add both osa and k8s support in compass-tasks Change-Id: I438f5b17e509d4cb751ced0ffe640ec70899882f Signed-off-by: Harry Huang <huangxiangyu5@huawei.com>
Diffstat (limited to 'compass-tasks/db/models.py')
-rw-r--r--compass-tasks/db/models.py1924
1 files changed, 0 insertions, 1924 deletions
diff --git a/compass-tasks/db/models.py b/compass-tasks/db/models.py
deleted file mode 100644
index d4b0324..0000000
--- a/compass-tasks/db/models.py
+++ /dev/null
@@ -1,1924 +0,0 @@
-# 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 copy
-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.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):
- """Provides table fields for each row created/updated timestamp."""
- 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):
- """Provides general fuctions for all compass table models."""
-
- def initialize(self):
- self.update()
-
- def update(self):
- pass
-
- @staticmethod
- def type_compatible(value, column_type):
- """Check if value type is compatible with the 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):
- """Generate validate function to make sure the record is legal."""
- 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):
- """General function to convert record to dict.
-
- Convert all columns not starting with '_' to
- {<column_name>: <column_value>}
- """
- 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 StateMixin(TimestampMixin, HelperMixin):
- """Provides general fields and functions for state related table."""
-
- state = Column(
- Enum(
- 'UNINITIALIZED', 'INITIALIZED', 'UPDATE_PREPARING',
- 'INSTALLING', 'SUCCESSFUL', 'ERROR'
- ),
- ColumnDefault('UNINITIALIZED')
- )
- percentage = Column(Float, default=0.0)
- message = Column(Text, default='')
- severity = Column(
- Enum('INFO', 'WARNING', 'ERROR'),
- ColumnDefault('INFO')
- )
- ready = Column(Boolean, default=False)
-
- def update(self):
- # In state table, some field information is redundant.
- # The update function to make sure all related fields
- # are set to correct state.
- if self.ready:
- self.state = 'SUCCESSFUL'
- 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 LogHistoryMixin(TimestampMixin, HelperMixin):
- """Provides general fields and functions for LogHistory related tables."""
- 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 validate(self):
- # TODO(xicheng): some validation can be moved to column.
- if not self.filename:
- raise exception.InvalidParameter(
- 'filename is not set in %s' % self.id
- )
-
-
-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), nullable=False)
- subnet_id = Column(
- Integer,
- ForeignKey('subnet.id', onupdate='CASCADE', ondelete='CASCADE')
- )
- user_id = Column(Integer, ForeignKey('user.id'))
- ip_int = Column(BigInteger, nullable=False)
- is_mgmt = Column(Boolean, default=False)
- is_promiscuous = Column(Boolean, default=False)
-
- __table_args__ = (
- UniqueConstraint('host_id', 'interface', name='interface_constraint'),
- UniqueConstraint('ip_int', 'user_id', name='ip_constraint')
- )
-
- def __init__(self, host_id, interface, user_id, **kwargs):
- self.host_id = host_id
- self.interface = interface
- self.user_id = user_id
- super(HostNetwork, self).__init__(**kwargs)
-
- def __str__(self):
- return 'HostNetwork[%s=%s]' % (self.interface, self.ip)
-
- @property
- def ip(self):
- return str(netaddr.IPAddress(self.ip_int))
-
- @ip.setter
- def ip(self, value):
- self.ip_int = int(netaddr.IPAddress(value))
-
- @property
- def netmask(self):
- return str(netaddr.IPNetwork(self.subnet.subnet).netmask)
-
- def update(self):
- self.host.config_validated = False
-
- def validate(self):
- # TODO(xicheng): some validation can be moved to column.
- 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.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.subnet
- dict_info['user_id'] = self.user_id
- return dict_info
-
-
-class ClusterHostLogHistory(BASE, LogHistoryMixin):
- """clusterhost installing log history for each file.
-
- """
- __tablename__ = 'clusterhost_log_history'
-
- clusterhost_id = Column(
- 'id', Integer,
- ForeignKey('clusterhost.id', onupdate='CASCADE', ondelete='CASCADE'),
- primary_key=True
- )
- filename = Column(String(80), primary_key=True, nullable=False)
- cluster_id = Column(
- Integer,
- ForeignKey('cluster.id')
- )
- host_id = Column(
- Integer,
- ForeignKey('host.id')
- )
-
- def __init__(self, clusterhost_id, filename, **kwargs):
- self.clusterhost_id = clusterhost_id
- self.filename = filename
- super(ClusterHostLogHistory, self).__init__(**kwargs)
-
- def __str__(self):
- return 'ClusterHostLogHistory[%s:%s]' % (
- self.clusterhost_id, self.filename
- )
-
- def initialize(self):
- self.cluster_id = self.clusterhost.cluster_id
- self.host_id = self.clusterhost.host_id
- super(ClusterHostLogHistory, self).initialize()
-
-
-class HostLogHistory(BASE, LogHistoryMixin):
- """host installing log history for each file.
-
- """
- __tablename__ = 'host_log_history'
-
- id = Column(
- Integer,
- ForeignKey('host.id', onupdate='CASCADE', ondelete='CASCADE'),
- primary_key=True)
- filename = Column(String(80), primary_key=True, nullable=False)
-
- def __init__(self, id, filename, **kwargs):
- self.id = id
- self.filename = filename
- super(HostLogHistory, self).__init__(**kwargs)
-
- def __str__(self):
- return 'HostLogHistory[%s:%s]' % (self.id, self.filename)
-
-
-class ClusterHostState(BASE, StateMixin):
- """ClusterHost state table."""
- __tablename__ = 'clusterhost_state'
-
- id = Column(
- Integer,
- ForeignKey(
- 'clusterhost.id',
- onupdate='CASCADE', ondelete='CASCADE'
- ),
- primary_key=True
- )
-
- def __str__(self):
- return 'ClusterHostState[%s state %s percentage %s]' % (
- self.id, self.state, self.percentage
- )
-
- def update(self):
- """Update clusterhost state.
-
- When clusterhost state is updated, the underlying host state
- may be updated accordingly.
- """
- super(ClusterHostState, self).update()
- host_state = self.clusterhost.host.state
- if self.state == 'INITIALIZED':
- if host_state.state in ['UNINITIALIZED', 'UPDATE_PREPARING']:
- host_state.state = 'INITIALIZED'
- host_state.update()
- elif self.state == 'INSTALLING':
- if host_state.state in [
- 'UNINITIALIZED', 'UPDATE_PREPARING', 'INITIALIZED'
- ]:
- host_state.state = 'INSTALLING'
- host_state.update()
- elif self.state == 'SUCCESSFUL':
- if host_state.state != 'SUCCESSFUL':
- host_state.state = 'SUCCESSFUL'
- host_state.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')
- )
- # the list of role names.
- _roles = Column('roles', JSONEncoded, default=[])
- _patched_roles = Column('patched_roles', 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={})
-
- log_histories = relationship(
- ClusterHostLogHistory,
- passive_deletes=True, passive_updates=True,
- cascade='all, delete-orphan',
- backref=backref('clusterhost')
- )
-
- __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 __str__(self):
- return 'ClusterHost[%s:%s]' % (self.clusterhost_id, self.name)
-
- 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.cluster.update()
- self.host.update()
- self.state.update()
- super(ClusterHost, self).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):
- package_config = copy.deepcopy(self.package_config)
- self.package_config = util.merge_dict(package_config, value)
- logging.debug(
- 'patch clusterhost %s package_config: %s',
- self.clusterhost_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 = copy.deepcopy(self.package_config)
- package_config.update(value)
- self.package_config = package_config
- logging.debug(
- 'put clusterhost %s package_config: %s',
- self.clusterhost_id, value
- )
- 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
-
- @property
- def deployed_os_config(self):
- return self.host.deployed_os_config
-
- @deployed_os_config.setter
- def deployed_os_config(self, value):
- host = self.host
- host.deployed_os_config = value
-
- @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.hostname
-
- @hostname.expression
- def hostname(cls):
- return Host.hostname
-
- @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):
- # only the role exists in flavor roles will be returned.
- # the role will be sorted as the order defined in flavor
- # roles.
- # duplicate role names will be removed.
- # The returned value is a list of dict like
- # [{'name': 'allinone', 'optional': False}]
- role_names = list(self._roles)
- if not role_names:
- return []
- cluster_roles = self.cluster.flavor['roles']
- if not cluster_roles:
- return []
- roles = []
- for cluster_role in cluster_roles:
- if cluster_role['name'] in role_names:
- roles.append(cluster_role)
- return roles
-
- @roles.setter
- def roles(self, value):
- """value should be a list of role name."""
- self._roles = list(value)
- self.config_validated = False
-
- @property
- def patched_roles(self):
- patched_role_names = list(self._patched_roles)
- if not patched_role_names:
- return []
- cluster_roles = self.cluster.flavor['roles']
- if not cluster_roles:
- return []
- roles = []
- for cluster_role in cluster_roles:
- if cluster_role['name'] in patched_role_names:
- roles.append(cluster_role)
- return roles
-
- @patched_roles.setter
- def patched_roles(self, value):
- """value should be a list of role name."""
- # if value is an empty list, we empty the field
- if value:
- roles = list(self._roles)
- roles.extend(value)
- self._roles = roles
- patched_roles = list(self._patched_roles)
- patched_roles.extend(value)
- self._patched_roles = patched_roles
- self.config_validated = False
- else:
- self._patched_roles = list(value)
- 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):
- """Get clusterhost state dict.
-
- The clusterhost state_dict is different from
- clusterhost.state.to_dict. The main difference is state_dict
- show the progress of both installing os on host and installing
- distributed system on clusterhost. While clusterhost.state.to_dict
- only shows the progress of installing distributed system on
- clusterhost.
- """
- cluster = self.cluster
- host = self.host
- host_state = host.state_dict()
- if not cluster.flavor_name:
- return host_state
- clusterhost_state = self.state.to_dict()
- if clusterhost_state['state'] in ['ERROR', 'SUCCESSFUL']:
- return clusterhost_state
- if (
- clusterhost_state['state'] in 'INSTALLING' and
- clusterhost_state['percentage'] > 0
- ):
- clusterhost_state['percentage'] = min(
- 1.0, (
- 0.5 + clusterhost_state['percentage'] / 2
- )
- )
- return clusterhost_state
-
- host_state['percentage'] = host_state['percentage'] / 2
- if host_state['state'] == 'SUCCESSFUL':
- host_state['state'] = 'INSTALLING'
- return host_state
-
- 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_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']
- })
- dict_info['roles'] = self.roles
- dict_info['patched_roles'] = self.patched_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 __str__(self):
- return 'HostState[%s state %s percentage %s]' % (
- self.id, self.state, self.percentage
- )
-
- def update(self):
- """Update host state.
-
- When host state is updated, all clusterhosts on the
- host will update their state if necessary.
- """
- super(HostState, self).update()
- 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 == 'UPDATE_PREPARING':
- for clusterhost in self.host.clusterhosts:
- if clusterhost.state in [
- 'INITIALIZED', 'INSTALLING', 'SUCCESSFUL', 'ERROR'
- ]:
- clusterhost.state = 'UPDATE_PREPARING'
- 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()
-
-
-class Host(BASE, TimestampMixin, HelperMixin):
- """Host table."""
- __tablename__ = 'host'
-
- name = Column(String(80), nullable=True)
- 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 = Column(JSONEncoded, default={})
-
- __table_args__ = (
- UniqueConstraint('name', 'owner', name='constraint'),
- )
-
- 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')
- )
- log_histories = relationship(
- HostLogHistory,
- passive_deletes=True, passive_updates=True,
- cascade='all, delete-orphan',
- backref=backref('host')
- )
-
- def __str__(self):
- return 'Host[%s:%s]' % (self.id, self.name)
-
- @hybrid_property
- def mac(self):
- machine = self.machine
- if machine:
- return machine.mac
- else:
- return None
-
- @property
- def os_id(self):
- return self.os_name
-
- @os_id.setter
- def os_id(self, value):
- self.os_name = value
-
- @hybrid_property
- def hostname(self):
- return self.name
-
- @hostname.expression
- def hostname(cls):
- return cls.name
-
- @property
- def patched_os_config(self):
- return self.os_config
-
- @patched_os_config.setter
- def patched_os_config(self, value):
- os_config = copy.deepcopy(self.os_config)
- self.os_config = util.merge_dict(os_config, value)
- logging.debug('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 = copy.deepcopy(self.os_config)
- os_config.update(value)
- self.os_config = os_config
- logging.debug('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 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()
- self.state.update()
- super(Host, self).update()
-
- def validate(self):
- # TODO(xicheng): some validation can be moved to the column in future.
- super(Host, self).validate()
- creator = self.creator
- if not creator:
- raise exception.InvalidParameter(
- 'creator is not set in host %s' % self.id
- )
- os_name = self.os_name
- if not os_name:
- 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
- )
-
- @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):
- """Host dict contains its underlying machine dict."""
- dict_info = self.machine.to_dict()
- dict_info.update(super(Host, self).to_dict())
- state_dict = self.state_dict()
- ip = None
- for host_network in self.host_networks:
- if host_network.is_mgmt:
- ip = host_network.ip
- dict_info.update({
- 'machine_id': self.machine.id,
- 'os_installed': self.os_installed,
- 'hostname': self.hostname,
- 'ip': ip,
- 'networks': [
- host_network.to_dict()
- for host_network in self.host_networks
- ],
- 'os_id': self.os_id,
- '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 __init__(self, **kwargs):
- super(ClusterState, self).__init__(**kwargs)
-
- def __str__(self):
- return 'ClusterState[%s state %s percentage %s]' % (
- self.id, self.state, self.percentage
- )
-
- 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):
- # all fields of cluster state should be calculated by
- # its each underlying clusterhost state.
- cluster = self.cluster
- clusterhosts = cluster.clusterhosts
- self.total_hosts = len(clusterhosts)
- self.installing_hosts = 0
- self.failed_hosts = 0
- self.completed_hosts = 0
- if not cluster.flavor_name:
- 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:
- if self.completed_hosts == self.total_hosts:
- self.percentage = 1.0
- else:
- self.percentage = (
- float(self.completed_hosts)
- /
- float(self.total_hosts)
- )
- if self.state == 'SUCCESSFUL':
- self.state = 'INSTALLING'
- self.ready = False
- self.message = (
- 'total %s, installing %s, completed: %s, error %s'
- ) % (
- self.total_hosts, self.installing_hosts,
- self.completed_hosts, self.failed_hosts
- )
- if self.failed_hosts:
- self.severity = 'ERROR'
-
- super(ClusterState, self).update()
- if self.state == 'INSTALLING':
- cluster.reinstall_distributed_system = False
-
-
-class Cluster(BASE, TimestampMixin, HelperMixin):
- """Cluster table."""
- __tablename__ = 'cluster'
-
- id = Column(Integer, primary_key=True)
- name = Column(String(80), nullable=False)
- reinstall_distributed_system = Column(Boolean, default=True)
- config_step = Column(String(80), default='')
- os_name = Column(String(80))
- flavor_name = Column(String(80), nullable=True)
- # flavor dict got from flavor id.
- flavor = Column(JSONEncoded, default={})
- 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_name = Column(String(80))
- 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')
- )
- __table_args__ = (
- UniqueConstraint('name', 'creator_id', name='constraint'),
- )
-
- def __init__(self, name, creator_id, **kwargs):
- self.name = name
- self.creator_id = creator_id
- self.state = ClusterState()
- super(Cluster, self).__init__(**kwargs)
-
- def __str__(self):
- return 'Cluster[%s:%s]' % (self.id, self.name)
-
- 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()
- self.state.update()
- super(Cluster, self).update()
-
- def validate(self):
- # TODO(xicheng): some validation can be moved to column.
- super(Cluster, self).validate()
- creator = self.creator
- if not creator:
- raise exception.InvalidParameter(
- 'creator is not set in cluster %s' % self.id
- )
- os_name = self.os_name
- if not os_name:
- raise exception.InvalidParameter(
- 'os is not set in cluster %s' % self.id
- )
- adapter_name = self.adapter_name
- if not adapter_name:
- raise exception.InvalidParameter(
- 'adapter is not set in cluster %s' % self.id
- )
- flavor_name = self.flavor_name
- if flavor_name:
- if 'name' not in self.flavor:
- raise exception.InvalidParameter(
- 'key name does not exist in flavor %s' % (
- self.flavor
- )
- )
- if flavor_name != self.flavor['name']:
- raise exception.InvalidParameter(
- 'flavor name %s is not match '
- 'the name key in flavor %s' % (
- flavor_name, self.flavor
- )
- )
- else:
- if self.flavor:
- raise exception.InvalidParameter(
- 'flavor %s is not empty' % self.flavor
- )
-
- @property
- def os_id(self):
- return self.os_name
-
- @os_id.setter
- def os_id(self, value):
- self.os_name = value
-
- @property
- def adapter_id(self):
- return self.adapter_name
-
- @adapter_id.setter
- def adapter_id(self, value):
- self.adapter_name = value
-
- @property
- def flavor_id(self):
- if self.flavor_name:
- return '%s:%s' % (self.adapter_name, self.flavor_name)
- else:
- return None
-
- @flavor_id.setter
- def flavor_id(self, value):
- if value:
- _, flavor_name = value.split(':', 1)
- self.flavor_name = flavor_name
- else:
- self.flavor_name = value
-
- @property
- def patched_os_config(self):
- return self.os_config
-
- @patched_os_config.setter
- def patched_os_config(self, value):
- os_config = copy.deepcopy(self.os_config)
- self.os_config = util.merge_dict(os_config, value)
- logging.debug('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 = copy.deepcopy(self.os_config)
- os_config.update(value)
- self.os_config = os_config
- logging.debug('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 = copy.deepcopy(self.package_config)
- self.package_config = util.merge_dict(package_config, value)
- logging.debug('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.debug('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
- )
- dict_info['os_id'] = self.os_id
- dict_info['adapter_id'] = self.adapter_id
- dict_info['flavor_id'] = self.flavor_id
- 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
-
- def __str__(self):
- return 'UserPermission[%s:%s]' % (self.id, self.name)
-
- @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, nullable=False)
- 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)
-
- def __str__(self):
- return 'Permission[%s:%s]' % (self.id, self.name)
-
-
-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, nullable=False)
- expire_timestamp = Column(DateTime, nullable=True)
-
- def __init__(self, token, **kwargs):
- self.token = token
- super(UserToken, self).__init__(**kwargs)
-
- def validate(self):
- # TODO(xicheng): some validation can be moved to column.
- 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):
- # TODO(xicheng): some validation can be moved to column.
- 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, nullable=False)
- 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 __str__(self):
- return 'User[%s]' % self.email
-
- def validate(self):
- # TODO(xicheng): some validation can be moved to column.
- 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):
- # password stored in database is crypted.
- 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
-
-
-class SwitchMachine(BASE, HelperMixin, TimestampMixin):
- """Switch Machine table."""
- __tablename__ = 'switch_machine'
- switch_machine_id = Column(
- 'id', 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')
- )
- owner_id = Column(Integer, ForeignKey('user.id'))
- 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 __str__(self):
- return 'SwitchMachine[%s port %s]' % (
- self.switch_machine_id, self.port
- )
-
- def validate(self):
- # TODO(xicheng): some validation can be moved to column.
- 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):
- """Check if switch machine should be filtered.
-
- port should be composed with <port_prefix><port_number><port_suffix>
- For each filter in switch machine filters,
- if filter_type is allow and port match the pattern, the switch
- machine is allowed to be got by api. If filter_type is deny and
- port match the pattern, the switch machine is not allowed to be got
- by api.
- If not filter is matched, if the last filter is allow, deny all
- unmatched switch machines, if the last filter is deny, allow all
- unmatched switch machines.
- If no filter defined, allow all switch machines.
- if ports defined in filter and 'all' in ports, the switch machine is
- matched. if ports defined in filter and 'all' not in ports,
- the switch machine with the port name in ports will be matched.
- If the port pattern matches
- <<port_prefix><port_number><port_suffix> and port number is in the
- range of [port_start, port_end], the switch machine is matched.
- """
- filters = self.switch.machine_filters
- port = self.port
- unmatched_allowed = True
- ports_pattern = re.compile(r'(\D*)(\d+)-(\d+)(\D*)')
- port_pattern = re.compile(r'(\D*)(\d+)(\D*)')
- port_match = port_pattern.match(port)
- if port_match:
- port_prefix = port_match.group(1)
- port_number = int(port_match.group(2))
- port_suffix = port_match.group(3)
- else:
- port_prefix = ''
- port_number = 0
- port_suffix = ''
- for port_filter in filters:
- filter_type = port_filter.get('filter_type', 'allow')
- denied = filter_type != 'allow'
- unmatched_allowed = denied
- if 'ports' in port_filter:
- if 'all' in port_filter['ports']:
- return denied
- if port in port_filter['ports']:
- return denied
- if port_match:
- for port_or_ports in port_filter['ports']:
- ports_match = ports_pattern.match(port_or_ports)
- if ports_match:
- filter_port_prefix = ports_match.group(1)
- filter_port_start = int(ports_match.group(2))
- filter_port_end = int(ports_match.group(3))
- filter_port_suffix = ports_match.group(4)
- if (
- filter_port_prefix == port_prefix and
- filter_port_suffix == port_suffix and
- filter_port_start <= port_number and
- port_number <= filter_port_end
- ):
- return denied
- else:
- filter_port_prefix = port_filter.get('port_prefix', '')
- filter_port_suffix = port_filter.get('port_suffix', '')
- if (
- port_match and
- port_prefix == filter_port_prefix and
- port_suffix == filter_port_suffix
- ):
- 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']
- ):
- return denied
- return not unmatched_allowed
-
- 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, nullable=False)
- ipmi_credentials = Column(JSONEncoded, default={})
- tag = Column(JSONEncoded, default={})
- location = Column(JSONEncoded, default={})
- owner_id = Column(Integer, ForeignKey('user.id'))
- machine_attributes = 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 __str__(self):
- return 'Machine[%s:%s]' % (self.id, self.mac)
-
- def validate(self):
- # TODO(xicheng): some validation can be moved to column.
- 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):
- if not value:
- return
- ipmi_credentials = copy.deepcopy(self.ipmi_credentials)
- self.ipmi_credentials = util.merge_dict(ipmi_credentials, value)
-
- @property
- def patched_tag(self):
- return self.tag
-
- @patched_tag.setter
- def patched_tag(self, value):
- if not value:
- return
- tag = copy.deepcopy(self.tag)
- tag.update(value)
- self.tag = value
-
- @property
- def patched_location(self):
- return self.location
-
- @patched_location.setter
- def patched_location(self, value):
- if not value:
- return
- location = copy.deepcopy(self.location)
- location.update(value)
- self.location = location
-
- def to_dict(self):
- # TODO(xicheng): move the filling of switches
- # to db/api.
- 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, nullable=False)
- 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 is json formatted list, each element has following format:
- # keys: ['filter_type', 'ports', 'port_prefix', 'port_suffix',
- # 'port_start', 'port_end'].
- # each port name is divided into <port_prefix><port_number><port_suffix>
- # filter_type is one of ['allow', 'deny'], default is 'allow'
- # ports is a list of port name.
- # port_prefix is the prefix that filtered port should start with.
- # port_suffix is the suffix that filtered posrt should end with.
- # port_start is integer that the port number should start with.
- # port_end is the integer that the port number should end with.
- _filters = Column('filters', JSONEncoded, default=[])
- switch_machines = relationship(
- SwitchMachine,
- passive_deletes=True, passive_updates=True,
- cascade='all, delete-orphan',
- backref=backref('switch')
- )
-
- def __str__(self):
- return 'Switch[%s:%s]' % (self.id, self.ip)
-
- @classmethod
- def parse_filters(cls, filters):
- """parse filters set from outside to standard format.
-
- api can set switch filters with the flexible format, this
- function will parse the flexible format filters.
-
- Supported format:
- as string:
- allow ports ae10,ae20
- allow port_prefix ae port_start 30 port_end 40
- deny ports all
- as python object:
- [{
- 'filter_type': 'allow',
- 'ports': ['ae10', 'ae20']
- },{
- 'filter_type': 'allow',
- 'port_prefix': 'ae',
- 'port_suffix': '',
- 'port_start': 30,
- 'port_end': 40
- },{
- 'filter_type': 'deny',
- 'ports': ['all']
- }]
- """
- if isinstance(filters, basestring):
- filters = filters.replace('\r\n', '\n').replace('\n', ';')
- filters = [
- machine_filter for machine_filter in filters.split(';')
- if machine_filter
- ]
- if not isinstance(filters, list):
- filters = [filters]
- machine_filters = []
- for machine_filter in filters:
- if not machine_filter:
- continue
- if isinstance(machine_filter, basestring):
- filter_dict = {}
- filter_items = [
- item for item in machine_filter.split() if item
- ]
- if filter_items[0] in ['allow', 'deny']:
- filter_dict['filter_type'] = filter_items[0]
- filter_items = filter_items[1:]
- elif filter_items[0] not in [
- 'ports', 'port_prefix', 'port_suffix',
- 'port_start', 'port_end'
- ]:
- raise exception.InvalidParameter(
- 'unrecognized filter type %s' % filter_items[0]
- )
- while filter_items:
- if len(filter_items) >= 2:
- filter_dict[filter_items[0]] = filter_items[1]
- filter_items = filter_items[2:]
- else:
- filter_dict[filter_items[0]] = ''
- filter_items = filter_items[1:]
- machine_filter = filter_dict
- if not isinstance(machine_filter, dict):
- raise exception.InvalidParameter(
- 'filter %s is not dict' % machine_filter
- )
- if 'filter_type' in machine_filter:
- if machine_filter['filter_type'] not in ['allow', 'deny']:
- raise exception.InvalidParameter(
- 'filter_type should be `allow` or `deny` in %s' % (
- machine_filter
- )
- )
- if 'ports' in machine_filter:
- if isinstance(machine_filter['ports'], basestring):
- machine_filter['ports'] = [
- port_or_ports
- for port_or_ports in machine_filter['ports'].split(',')
- if port_or_ports
- ]
- if not isinstance(machine_filter['ports'], list):
- raise exception.InvalidParameter(
- '`ports` type is not list in filter %s' % (
- machine_filter
- )
- )
- for port_or_ports in machine_filter['ports']:
- if not isinstance(port_or_ports, basestring):
- raise exception.InvalidParameter(
- '%s type is not basestring in `ports` %s' % (
- port_or_ports, machine_filter['ports']
- )
- )
- for key in ['port_start', 'port_end']:
- if key in machine_filter:
- if isinstance(machine_filter[key], basestring):
- if machine_filter[key].isdigit():
- machine_filter[key] = int(machine_filter[key])
- if not isinstance(machine_filter[key], (int, long)):
- raise exception.InvalidParameter(
- '`%s` type is not int in filer %s' % (
- key, machine_filter
- )
- )
- machine_filters.append(machine_filter)
- return machine_filters
-
- @classmethod
- def format_filters(cls, filters):
- """format json formatted filters to string."""
- filter_strs = []
- for machine_filter in filters:
- filter_properties = []
- filter_properties.append(
- machine_filter.get('filter_type', 'allow')
- )
- if 'ports' in machine_filter:
- filter_properties.append(
- 'ports ' + ','.join(machine_filter['ports'])
- )
- if 'port_prefix' in machine_filter:
- filter_properties.append(
- 'port_prefix ' + machine_filter['port_prefix']
- )
- if 'port_suffix' in machine_filter:
- filter_properties.append(
- 'port_suffix ' + machine_filter['port_suffix']
- )
- if 'port_start' in machine_filter:
- filter_properties.append(
- 'port_start ' + str(machine_filter['port_start'])
- )
- if 'port_end' in machine_filter:
- filter_properties.append(
- 'port_end ' + str(machine_filter['port_end'])
- )
- filter_strs.append(' '.join(filter_properties))
- return ';'.join(filter_strs)
-
- 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):
- if not value:
- return
- credentials = copy.deepcopy(self.credentials)
- self.credentials = util.merge_dict(credentials, value)
-
- @property
- def machine_filters(self):
- return self._filters
-
- @machine_filters.setter
- def machine_filters(self, value):
- if not value:
- return
- self._filters = self.parse_filters(value)
-
- @property
- def put_machine_filters(self):
- return self._filters
-
- @put_machine_filters.setter
- def put_machine_filters(self, value):
- if not value:
- return
- self._filters = self.parse_filters(value)
-
- @property
- def patched_machine_filters(self):
- return self._filters
-
- @patched_machine_filters.setter
- def patched_machine_filters(self, value):
- if not value:
- return
- filters = list(self.machine_filters)
- self._filters = self.parse_filters(value) + filters
-
- def to_dict(self):
- dict_info = super(Switch, self).to_dict()
- dict_info['ip'] = self.ip
- dict_info['filters'] = self.format_filters(self._filters)
- return dict_info
-
-
-class Subnet(BASE, TimestampMixin, HelperMixin):
- """network table."""
- __tablename__ = 'subnet'
-
- id = Column(Integer, primary_key=True)
- name = Column(String(80), unique=True, nullable=True)
- subnet = Column(String(80), unique=True, nullable=False)
-
- 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 __str__(self):
- return 'Subnet[%s:%s]' % (self.id, self.subnet)
-
- def to_dict(self):
- dict_info = super(Subnet, self).to_dict()
- if not self.name:
- dict_info['name'] = self.subnet
- return dict_info
-
-
-# TODO(grace): move this global variable into HealthCheckReport.
-HEALTH_REPORT_STATES = ('verifying', 'success', 'finished', 'error')
-
-
-class HealthCheckReport(BASE, HelperMixin):
- """Health check report table."""
- __tablename__ = 'health_check_report'
-
- cluster_id = Column(
- Integer,
- ForeignKey('cluster.id', onupdate='CASCADE', ondelete='CASCADE'),
- primary_key=True
- )
- name = Column(String(80), nullable=False, primary_key=True)
- display_name = Column(String(100))
- report = Column(JSONEncoded, default={})
- category = Column(String(80), default='')
- state = Column(
- Enum(*HEALTH_REPORT_STATES, name='report_state'),
- ColumnDefault('verifying'),
- nullable=False
- )
- error_message = Column(Text, default='')
-
- def __init__(self, cluster_id, name, **kwargs):
- self.cluster_id = cluster_id
- self.name = name
- if 'state' in kwargs and kwargs['state'] not in HEALTH_REPORT_STATES:
- err_msg = 'State value %s is not accepted.' % kwargs['state']
- raise exception.InvalidParameter(err_msg)
-
- super(HealthCheckReport, self).__init__(**kwargs)
-
- def __str__(self):
- return 'HealthCheckReport[cluster_id: %s, name: %s]' % (
- self.cluster_id, self.name
- )