diff --git a/compass/db/api/network.py b/compass/db/api/network.py index 381df144..4f70a4da 100644 --- a/compass/db/api/network.py +++ b/compass/db/api/network.py @@ -51,20 +51,20 @@ def _check_subnet(subnet): @utils.supported_filters(optional_support_keys=SUPPORTED_FIELDS) @database.run_in_session() @user_api.check_user_permission_in_session( - permission.PERMISSION_LIST_NETWORKS + permission.PERMISSION_LIST_SUBNETS ) @utils.wrap_to_dict(RESP_FIELDS) def list_subnets(session, lister, **filters): """List subnets.""" return utils.list_db_objects( - session, models.Network, **filters + session, models.Subnet, **filters ) @utils.supported_filters([]) @database.run_in_session() @user_api.check_user_permission_in_session( - permission.PERMISSION_LIST_NETWORKS + permission.PERMISSION_LIST_SUBNETS ) @utils.wrap_to_dict(RESP_FIELDS) def get_subnet( @@ -73,7 +73,8 @@ def get_subnet( ): """Get subnet info.""" return utils.get_db_object( - session, models.Network, exception_when_missing, id=subnet_id + session, models.Subnet, + exception_when_missing, id=subnet_id ) @@ -84,7 +85,7 @@ def get_subnet( @utils.input_validates(subnet=_check_subnet) @database.run_in_session() @user_api.check_user_permission_in_session( - permission.PERMISSION_ADD_NETWORK + permission.PERMISSION_ADD_SUBNET ) @utils.wrap_to_dict(RESP_FIELDS) def add_subnet( @@ -93,7 +94,7 @@ def add_subnet( ): """Create a subnet.""" return utils.add_db_object( - session, models.Network, + session, models.Subnet, exception_when_existing, subnet, **kwargs ) @@ -105,26 +106,26 @@ def add_subnet( @utils.input_validates(subnet=_check_subnet) @database.run_in_session() @user_api.check_user_permission_in_session( - permission.PERMISSION_ADD_NETWORK + permission.PERMISSION_ADD_SUBNET ) @utils.wrap_to_dict(RESP_FIELDS) def update_subnet(session, updater, subnet_id, **kwargs): """Update a subnet.""" - network = utils.get_db_object( - session, models.Network, id=subnet_id + subnet = utils.get_db_object( + session, models.Subnet, id=subnet_id ) - return utils.update_db_object(session, network, **kwargs) + return utils.update_db_object(session, subnet, **kwargs) @utils.supported_filters([]) @database.run_in_session() @user_api.check_user_permission_in_session( - permission.PERMISSION_DEL_NETWORK + permission.PERMISSION_DEL_SUBNET ) @utils.wrap_to_dict(RESP_FIELDS) def del_subnet(session, deleter, subnet_id, **kwargs): """Delete a subnet.""" - network = utils.get_db_object( - session, models.Network, id=subnet_id + subnet = utils.get_db_object( + session, models.Subnet, id=subnet_id ) - return utils.del_db_object(session, network) + return utils.del_db_object(session, subnet) diff --git a/compass/db/api/permission.py b/compass/db/api/permission.py index f77a77f0..4def53c4 100644 --- a/compass/db/api/permission.py +++ b/compass/db/api/permission.py @@ -89,14 +89,14 @@ PERMISSION_LIST_ADAPTERS = PermissionWrapper( PERMISSION_LIST_METADATAS = PermissionWrapper( 'list_metadatas', 'list metadatas', 'list metadatas' ) -PERMISSION_LIST_NETWORKS = PermissionWrapper( - 'list_networks', 'list networks', 'list networks' +PERMISSION_LIST_SUBNETS = PermissionWrapper( + 'list_subnets', 'list subnets', 'list subnets' ) -PERMISSION_ADD_NETWORK = PermissionWrapper( - 'add_network', 'add network', 'add network' +PERMISSION_ADD_SUBNET = PermissionWrapper( + 'add_subnet', 'add subnet', 'add subnet' ) -PERMISSION_DEL_NETWORK = PermissionWrapper( - 'del_network', 'del network', 'del network' +PERMISSION_DEL_SUBNET = PermissionWrapper( + 'del_subnet', 'del subnet', 'del subnet' ) PERMISSION_LIST_CLUSTERS = PermissionWrapper( 'list_clusters', 'list clusters', 'list clusters' @@ -224,9 +224,9 @@ PERMISSIONS = [ PERMISSION_DEL_MACHINE, PERMISSION_LIST_ADAPTERS, PERMISSION_LIST_METADATAS, - PERMISSION_LIST_NETWORKS, - PERMISSION_ADD_NETWORK, - PERMISSION_DEL_NETWORK, + PERMISSION_LIST_SUBNETS, + PERMISSION_ADD_SUBNET, + PERMISSION_DEL_SUBNET, PERMISSION_LIST_CLUSTERS, PERMISSION_ADD_CLUSTER, PERMISSION_DEL_CLUSTER, diff --git a/compass/db/api/utils.py b/compass/db/api/utils.py index ffe2b6f9..c6d524c8 100644 --- a/compass/db/api/utils.py +++ b/compass/db/api/utils.py @@ -151,12 +151,43 @@ def model_filter(query, model, **filters): return query +def replace_output(**output_mapping): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + return _replace_output( + func(*args, **kwargs), **output_mapping + ) + return wrapper + return decorator + + +def _replace_output(data, **output_mapping): + """Helper to replace output data.""" + if isinstance(data, list): + return [ + _replace_output(item, **output_mapping) + for item in data + ] + info = {} + for key, value in data.items(): + if key in output_mapping: + output_key = output_mapping[key] + if isinstance(output_key, basestring): + info[output_key] = value + else: + info[key] = ( + _replace_output(value, **output_key) + ) + else: + info[key] = value + return info + + def wrap_to_dict(support_keys=[], **filters): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): - logging.info('wrap to dict: args: %s', str(args)) - logging.info('wrap to dict: kwargs: %s', kwargs) return _wrapper_dict( func(*args, **kwargs), support_keys, **filters ) @@ -181,12 +212,36 @@ def _wrapper_dict(data, support_keys, **filters): for key in support_keys: if key in data: if key in filters: - info[key] = _wrapper_dict(data[key], filters[key]) + filter_keys = filters[key] + if isinstance(filter_keys, dict): + info[key] = _wrapper_dict( + data[key], filter_keys.keys(), + **filter_keys + ) + else: + info[key] = _wrapper_dict( + data[key], filter_keys + ) else: info[key] = data[key] return info +def replace_input_types(**kwarg_mapping): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + replaced_kwargs = {} + for key, value in kwargs.items(): + if key in kwarg_mapping: + replaced_kwargs[key] = kwarg_mapping[key](value) + else: + replaced_kwargs[key] = value + return func(*args, **replaced_kwargs) + return wrapper + return decorator + + def replace_filters(**filter_mapping): def decorator(func): @functools.wraps(func) diff --git a/compass/db/models.py b/compass/db/models.py index 9b2a9f3c..ff11328e 100644 --- a/compass/db/models.py +++ b/compass/db/models.py @@ -74,8 +74,34 @@ class HelperMixin(object): def update(self): pass + @staticmethod + def type_compatible(value, column_type): + if value is None: + return True + if not hasattr(column_type, 'python_type'): + return True + column_python_type = column_type.python_type + if isinstance(value, column_python_type): + return True + if issubclass(column_python_type, basestring): + return isinstance(value, basestring) + if column_python_type in [int, long]: + return type(value) in [int, long] + if column_python_type in [float]: + return type(value) in [float] + if column_python_type in [bool]: + return type(value) in [bool] + return False + def validate(self): - pass + for key, column in self.__mapper__.columns.items(): + value = getattr(self, key) + if not self.type_compatible(value, column.type): + raise exception.InvalidParameter( + 'column %s value %r type is unexpected: %s' % ( + key, value, column.type + ) + ) def to_dict(self): keys = self.__mapper__.columns.keys() @@ -259,11 +285,11 @@ class InstallerMixin(HelperMixin): settings = Column(JSONEncoded, default={}) def validate(self): + super(InstallerMixin, self).validate() if not self.name: raise exception.InvalidParameter( 'name is not set in installer %s' % self.name ) - super(InstallerMixin, self).validate() class StateMixin(TimestampMixin, HelperMixin): @@ -335,11 +361,11 @@ class HostNetwork(BASE, TimestampMixin, HelperMixin): @hybrid_property def subnet(self): - return self.network.subnet + return self.subnet.subnet @subnet.expression def subnet(cls): - return cls.network.subnet + return cls.subnet.subnet @property def netmask(self): @@ -349,7 +375,8 @@ class HostNetwork(BASE, TimestampMixin, HelperMixin): self.host.config_validated = False def validate(self): - if not self.network: + super(HostNetwork, self).validate() + if not self.subnet: raise exception.InvalidParameter( 'subnet is not set in %s interface %s' % ( self.host_id, self.interface @@ -369,7 +396,6 @@ class HostNetwork(BASE, TimestampMixin, HelperMixin): str(ip), str(subnet) ) ) - super(HostNetwork, self).validate() def to_dict(self): dict_info = super(HostNetwork, self).to_dict() @@ -758,6 +784,7 @@ class Host(BASE, TimestampMixin, HelperMixin): super(Host, self).update() def validate(self): + super(Host, self).validate() creator = self.creator if not creator: raise exception.InvalidParameter( @@ -777,7 +804,6 @@ class Host(BASE, TimestampMixin, HelperMixin): raise exception.InvalidParameter( 'os %s is not deployable in host %s' % (os.name, self.id) ) - super(Host, self).validate() @property def os_installed(self): @@ -981,6 +1007,7 @@ class Cluster(BASE, TimestampMixin, HelperMixin): super(Cluster, self).update() def validate(self): + super(Cluster, self).validate() creator = self.creator if not creator: raise exception.InvalidParameter( @@ -1024,13 +1051,22 @@ class Cluster(BASE, TimestampMixin, HelperMixin): raise exception.InvalidParameter( 'flavor is not set in cluster %s' % self.id ) - if flavor.adapter_id != self.adapter_id: + flavor_adapter_id = flavor.adapter_id + adapter_id = self.adapter_id + logging.info( + 'flavor adapter type %s value %s', + type(flavor_adapter_id), flavor_adapter_id + ) + logging.info( + 'adapter type %s value %s', + type(adapter_id), adapter_id + ) + if flavor_adapter_id != adapter_id: raise exception.InvalidParameter( 'flavor adapter id %s does not match adapter id %s' % ( - flavor.adapter_id, self.adapter_id + flavor_adapter_id, adapter_id ) ) - super(Cluster, self).validate() @property def patched_os_config(self): @@ -1164,11 +1200,11 @@ class UserToken(BASE, HelperMixin): super(UserToken, self).__init__(**kwargs) def validate(self): + super(UserToken, self).validate() if not self.user: raise exception.InvalidParameter( 'user is not set in token: %s' % self.token ) - super(UserToken, self).validate() class UserLog(BASE, HelperMixin): @@ -1188,11 +1224,11 @@ class UserLog(BASE, HelperMixin): return self.user.email def validate(self): + super(UserLog, self).validate() if not self.user: raise exception.InvalidParameter( 'user is not set in user log: %s' % self.id ) - super(UserLog, self).validate() class User(BASE, HelperMixin, TimestampMixin): @@ -1238,11 +1274,11 @@ class User(BASE, HelperMixin, TimestampMixin): super(User, self).__init__(**kwargs) def validate(self): + super(User, self).validate() if not self.crypted_password: raise exception.InvalidParameter( 'password is not set in user : %s' % self.email ) - super(User, self).validate() @property def password(self): @@ -1301,6 +1337,7 @@ class SwitchMachine(BASE, HelperMixin, TimestampMixin): super(SwitchMachine, self).__init__(**kwargs) def validate(self): + super(SwitchMachine, self).validate() if not self.switch: raise exception.InvalidParameter( 'switch is not set in %s' % self.id @@ -1429,13 +1466,13 @@ class Machine(BASE, HelperMixin, TimestampMixin): super(Machine, self).__init__(**kwargs) def validate(self): + super(Machine, self).validate() try: netaddr.EUI(self.mac) except Exception: raise exception.InvalidParameter( 'mac address %s format uncorrect' % self.mac ) - super(Machine, self).validate() @property def patched_ipmi_credentials(self): @@ -1586,11 +1623,11 @@ class OSConfigMetadata(BASE, MetadataMixin): super(OSConfigMetadata, self).__init__(**kwargs) def validate(self): + super(OSConfigMetadata, self).validate() if not self.os: raise exception.InvalidParameter( 'os is not set in os metadata %s' % self.id ) - super(OSConfigMetadata, self).validate() class OSConfigField(BASE, FieldMixin): @@ -1725,6 +1762,7 @@ class AdapterFlavorRole(BASE, HelperMixin): super(AdapterFlavorRole, self).__init__() def validate(self): + super(AdapterFlavorRole, self).validate() flavor_adapter_id = self.flavor.adapter_id role_adapter_id = self.role.adapter_id if flavor_adapter_id != role_adapter_id: @@ -1782,11 +1820,11 @@ class AdapterFlavor(BASE, HelperMixin): super(AdapterFlavor, self).initialize() def validate(self): + super(AdapterFlavor, self).validate() if not self.template: raise exception.InvalidParameter( 'template is not set in adapter flavor %s' % self.id ) - super(AdapterFlavor, self).validate() def to_dict(self): dict_info = super(AdapterFlavor, self).to_dict() @@ -1881,11 +1919,11 @@ class PackageConfigMetadata(BASE, MetadataMixin): super(PackageConfigMetadata, self).__init__(**kwargs) def validate(self): + super(PackageConfigMetadata, self).validate() if not self.adapter: raise exception.InvalidParameter( 'adapter is not set in package metadata %s' % self.id ) - super(PackageConfigMetadata, self).validate() class PackageConfigField(BASE, FieldMixin): @@ -2117,7 +2155,7 @@ class DistributedSystem(BASE, HelperMixin): nullable=True ) name = Column(String(80), unique=True) - deployable = Column(String(80), default=False) + deployable = Column(Boolean, default=False) adapters = relationship( Adapter, @@ -2184,7 +2222,7 @@ class Subnet(BASE, TimestampMixin, HelperMixin): name = Column(String(80), unique=True) subnet = Column(String(80), unique=True) - host_interfaces = relationship( + host_networks = relationship( HostNetwork, passive_deletes=True, passive_updates=True, cascade='all, delete-orphan', diff --git a/compass/tests/api/data/os_metadata/general.conf b/compass/tests/api/data/os_metadata/general.conf index 840bee24..6acc43db 100644 --- a/compass/tests/api/data/os_metadata/general.conf +++ b/compass/tests/api/data/os_metadata/general.conf @@ -30,9 +30,9 @@ METADATA = { 'http_proxy': { '_self': { 'field': 'general', - 'default_value': 'http://$ipaddr:3128', + 'default_value': 'http://127.0.0.1:3128', 'options': [ - 'http://$ipaddr:3128' + 'http://127.0.0.1:3128' ], 'mapping_to': 'http_proxy' } @@ -40,9 +40,9 @@ METADATA = { 'https_proxy': { '_self': { 'field': 'general', - 'default_value': 'http://$ipaddr:3128', + 'default_value': 'http://127.0.0.1:3128', 'options': [ - 'http://$ipaddr:3128' + 'http://127.0.0.1:3128' ], 'mapping_to': 'https_proxy' } @@ -52,13 +52,11 @@ METADATA = { 'field': 'general_list', 'default_value': [ '127.0.0.1', - '$hostname', - '$ipaddr' + 'compass' ], 'options': [ '127.0.0.1', - '$hostname', - '$ipaddr' + 'compass' ], 'mapping_to': 'no_proxy' } @@ -67,9 +65,9 @@ METADATA = { '_self': { 'is_required': True, 'field': 'general', - 'default_value': '$ipaddr', + 'default_value': '127.0.0.1', 'options': [ - '$ipaddr' + '127.0.0.1' ], 'mapping_to': 'ntp_server' } @@ -79,10 +77,10 @@ METADATA = { 'is_required': True, 'field': 'general_list', 'default_value': [ - '$ipaddr', + '127.0.0.1', ], 'options': [ - '$ipaddr' + '127.0.0.1' ], 'mapping_to': 'nameservers' } @@ -91,17 +89,17 @@ METADATA = { '_self': { 'field': 'general', 'is_required' : True, - 'default_value': ['$domain'][0], - 'options': ['$domain'], + 'default_value': 'ods.com', + 'options': ['ods.com'], } }, 'search_path': { '_self': { 'field': 'general_list', 'default_value': [ - '$domain' + 'ods.com' ], - 'options': ['$domain'], + 'options': ['ods.com'], 'mapping_to': 'search_path' } }, @@ -109,7 +107,7 @@ METADATA = { '_self': { 'is_required': True, 'field': 'ip', - 'default_value': '$gateway', + 'default_value': '127.0.0.1', 'mapping_to': 'gateway' } } diff --git a/compass/tests/db/api/data/os_metadata/general.conf b/compass/tests/db/api/data/os_metadata/general.conf index 840bee24..6acc43db 100644 --- a/compass/tests/db/api/data/os_metadata/general.conf +++ b/compass/tests/db/api/data/os_metadata/general.conf @@ -30,9 +30,9 @@ METADATA = { 'http_proxy': { '_self': { 'field': 'general', - 'default_value': 'http://$ipaddr:3128', + 'default_value': 'http://127.0.0.1:3128', 'options': [ - 'http://$ipaddr:3128' + 'http://127.0.0.1:3128' ], 'mapping_to': 'http_proxy' } @@ -40,9 +40,9 @@ METADATA = { 'https_proxy': { '_self': { 'field': 'general', - 'default_value': 'http://$ipaddr:3128', + 'default_value': 'http://127.0.0.1:3128', 'options': [ - 'http://$ipaddr:3128' + 'http://127.0.0.1:3128' ], 'mapping_to': 'https_proxy' } @@ -52,13 +52,11 @@ METADATA = { 'field': 'general_list', 'default_value': [ '127.0.0.1', - '$hostname', - '$ipaddr' + 'compass' ], 'options': [ '127.0.0.1', - '$hostname', - '$ipaddr' + 'compass' ], 'mapping_to': 'no_proxy' } @@ -67,9 +65,9 @@ METADATA = { '_self': { 'is_required': True, 'field': 'general', - 'default_value': '$ipaddr', + 'default_value': '127.0.0.1', 'options': [ - '$ipaddr' + '127.0.0.1' ], 'mapping_to': 'ntp_server' } @@ -79,10 +77,10 @@ METADATA = { 'is_required': True, 'field': 'general_list', 'default_value': [ - '$ipaddr', + '127.0.0.1', ], 'options': [ - '$ipaddr' + '127.0.0.1' ], 'mapping_to': 'nameservers' } @@ -91,17 +89,17 @@ METADATA = { '_self': { 'field': 'general', 'is_required' : True, - 'default_value': ['$domain'][0], - 'options': ['$domain'], + 'default_value': 'ods.com', + 'options': ['ods.com'], } }, 'search_path': { '_self': { 'field': 'general_list', 'default_value': [ - '$domain' + 'ods.com' ], - 'options': ['$domain'], + 'options': ['ods.com'], 'mapping_to': 'search_path' } }, @@ -109,7 +107,7 @@ METADATA = { '_self': { 'is_required': True, 'field': 'ip', - 'default_value': '$gateway', + 'default_value': '127.0.0.1', 'mapping_to': 'gateway' } } diff --git a/compass/tests/db/api/test_machine.py b/compass/tests/db/api/test_machine.py index cf449f63..4a38776e 100644 --- a/compass/tests/db/api/test_machine.py +++ b/compass/tests/db/api/test_machine.py @@ -68,7 +68,7 @@ class TestGetMachine(BaseTest): self.user_object, 1, mac='28:6e:d4:46:c4:25', - port=1 + port='1' ) get_machine = machine.get_machine( self.user_object, @@ -91,7 +91,7 @@ class TestListMachines(BaseTest): self.user_object, 1, mac='28:6e:d4:46:c4:25', - port=1 + port='1' ) list_machine = machine.list_machines(self.user_object) self.assertIsNotNone(list_machine) @@ -111,7 +111,7 @@ class TestUpdateMachine(BaseTest): self.user_object, 1, mac='28:6e:d4:46:c4:25', - port=1 + port='1' ) machine.update_machine( self.user_object, @@ -139,7 +139,7 @@ class TestPatchMachine(BaseTest): self.user_object, 1, mac='28:6e:d4:46:c4:25', - port=1 + port='1' ) machine.patch_machine( self.user_object, @@ -167,7 +167,7 @@ class TestDelMachine(BaseTest): self.user_object, 1, mac='28:6e:d4:46:c4:25', - port=1 + port='1' ) machine.del_machine( self.user_object,