3ef1fad8f8
This is to remove unused config.CONF to keep code clean. Change-Id: I6e8d19ec06e7df84b2bbd86dd3b99b4489402ddd
187 lines
7.8 KiB
Python
187 lines
7.8 KiB
Python
# Copyright (c) 2016 Red Hat, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from oslo_versionedobjects import fields
|
|
|
|
from cinder import db
|
|
from cinder import exception
|
|
from cinder.i18n import _
|
|
from cinder import objects
|
|
from cinder.objects import base
|
|
from cinder import utils
|
|
|
|
|
|
@base.CinderObjectRegistry.register
|
|
class Cluster(base.CinderPersistentObject, base.CinderObject,
|
|
base.CinderComparableObject):
|
|
"""Cluster Versioned Object.
|
|
|
|
Method get_by_id supports as additional named arguments:
|
|
- get_services: If we want to load all services from this cluster.
|
|
- services_summary: If we want to load num_nodes and num_down_nodes
|
|
fields.
|
|
- is_up: Boolean value to filter based on the cluster's up status.
|
|
- read_deleted: Filtering based on delete status. Default value "no".
|
|
- Any other cluster field will be used as a filter.
|
|
"""
|
|
# Version 1.0: Initial version
|
|
VERSION = '1.0'
|
|
OPTIONAL_FIELDS = ('num_hosts', 'num_down_hosts', 'services')
|
|
|
|
# NOTE(geguileo): We don't want to expose race_preventer field at the OVO
|
|
# layer since it is only meant for the DB layer internal mechanism to
|
|
# prevent races.
|
|
fields = {
|
|
'id': fields.IntegerField(),
|
|
'name': fields.StringField(nullable=False),
|
|
'binary': fields.StringField(nullable=False),
|
|
'disabled': fields.BooleanField(default=False, nullable=True),
|
|
'disabled_reason': fields.StringField(nullable=True),
|
|
'num_hosts': fields.IntegerField(default=0, read_only=True),
|
|
'num_down_hosts': fields.IntegerField(default=0, read_only=True),
|
|
'last_heartbeat': fields.DateTimeField(nullable=True, read_only=True),
|
|
'services': fields.ObjectField('ServiceList', nullable=True,
|
|
read_only=True),
|
|
}
|
|
|
|
@classmethod
|
|
def _get_expected_attrs(cls, context, *args, **kwargs):
|
|
"""Return expected attributes when getting a cluster.
|
|
|
|
Expected attributes depend on whether we are retrieving all related
|
|
services as well as if we are getting the services summary.
|
|
"""
|
|
expected_attrs = []
|
|
if kwargs.get('get_services'):
|
|
expected_attrs.append('services')
|
|
if kwargs.get('services_summary'):
|
|
expected_attrs.extend(('num_hosts', 'num_down_hosts'))
|
|
return expected_attrs
|
|
|
|
@staticmethod
|
|
def _from_db_object(context, cluster, db_cluster, expected_attrs=None):
|
|
"""Fill cluster OVO fields from cluster ORM instance."""
|
|
expected_attrs = expected_attrs or tuple()
|
|
for name, field in cluster.fields.items():
|
|
# The only field that cannot be assigned using setattr is services,
|
|
# because it is an ObjectField. So we don't assign the value if
|
|
# it's a non expected optional field or if it's services field.
|
|
if ((name in Cluster.OPTIONAL_FIELDS
|
|
and name not in expected_attrs) or name == 'services'):
|
|
continue
|
|
value = getattr(db_cluster, name)
|
|
setattr(cluster, name, value)
|
|
|
|
cluster._context = context
|
|
if 'services' in expected_attrs:
|
|
cluster.services = base.obj_make_list(
|
|
context,
|
|
objects.ServiceList(context),
|
|
objects.Service,
|
|
db_cluster.services)
|
|
|
|
cluster.obj_reset_changes()
|
|
return cluster
|
|
|
|
def obj_load_attr(self, attrname):
|
|
"""Lazy load services attribute."""
|
|
# NOTE(geguileo): We only allow lazy loading services to raise
|
|
# awareness of the high cost of lazy loading num_hosts and
|
|
# num_down_hosts, so if we are going to need this information we should
|
|
# be certain we really need it and it should loaded when retrieving the
|
|
# data from the DB the first time we read the OVO.
|
|
if attrname != 'services':
|
|
raise exception.ObjectActionError(
|
|
action='obj_load_attr',
|
|
reason=_('attribute %s not lazy-loadable') % attrname)
|
|
if not self._context:
|
|
raise exception.OrphanedObjectError(method='obj_load_attr',
|
|
objtype=self.obj_name())
|
|
|
|
self.services = objects.ServiceList.get_all(
|
|
self._context, {'cluster_name': self.name})
|
|
|
|
self.obj_reset_changes(fields=('services',))
|
|
|
|
def create(self):
|
|
if self.obj_attr_is_set('id'):
|
|
raise exception.ObjectActionError(action='create',
|
|
reason=_('already created'))
|
|
updates = self.cinder_obj_get_changes()
|
|
if updates:
|
|
for field in self.OPTIONAL_FIELDS:
|
|
if field in updates:
|
|
raise exception.ObjectActionError(
|
|
action='create', reason=_('%s assigned') % field)
|
|
|
|
db_cluster = db.cluster_create(self._context, updates)
|
|
self._from_db_object(self._context, self, db_cluster)
|
|
|
|
def save(self):
|
|
updates = self.cinder_obj_get_changes()
|
|
if updates:
|
|
for field in self.OPTIONAL_FIELDS:
|
|
if field in updates:
|
|
raise exception.ObjectActionError(
|
|
action='save', reason=_('%s changed') % field)
|
|
db.cluster_update(self._context, self.id, updates)
|
|
self.obj_reset_changes()
|
|
|
|
def destroy(self):
|
|
with self.obj_as_admin():
|
|
updated_values = db.cluster_destroy(self._context, self.id)
|
|
for field, value in updated_values.items():
|
|
setattr(self, field, value)
|
|
self.obj_reset_changes(updated_values.keys())
|
|
|
|
def is_up(self):
|
|
return (self.last_heartbeat and
|
|
self.last_heartbeat >= utils.service_expired_time(True))
|
|
|
|
|
|
@base.CinderObjectRegistry.register
|
|
class ClusterList(base.ObjectListBase, base.CinderObject):
|
|
# Version 1.0: Initial version
|
|
VERSION = '1.0'
|
|
|
|
fields = {'objects': fields.ListOfObjectsField('Cluster')}
|
|
|
|
@classmethod
|
|
def get_all(cls, context, is_up=None, get_services=False,
|
|
services_summary=False, read_deleted='no', **filters):
|
|
"""Get all clusters that match the criteria.
|
|
|
|
:param is_up: Boolean value to filter based on the cluster's up status.
|
|
:param get_services: If we want to load all services from this cluster.
|
|
:param services_summary: If we want to load num_nodes and
|
|
num_down_nodes fields.
|
|
:param read_deleted: Filtering based on delete status. Default value is
|
|
"no".
|
|
:param filters: Field based filters in the form of key/value.
|
|
"""
|
|
|
|
expected_attrs = Cluster._get_expected_attrs(
|
|
context,
|
|
get_services=get_services,
|
|
services_summary=services_summary)
|
|
|
|
clusters = db.cluster_get_all(context, is_up=is_up,
|
|
get_services=get_services,
|
|
services_summary=services_summary,
|
|
read_deleted=read_deleted,
|
|
**filters)
|
|
return base.obj_make_list(context, cls(context), Cluster, clusters,
|
|
expected_attrs=expected_attrs)
|