Skip to content

Commit 61fedb2

Browse files
authored
Merge pull request #850 from joseph-v/centrl_mgr
Add Centralized Manager support
2 parents a44794d + c0ab246 commit 61fedb2

15 files changed

Lines changed: 154 additions & 20 deletions

File tree

delfin/alert_manager/alert_processor.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,61 @@ def process_alert_info(self, alert):
4848
alert)
4949
# Fill storage specific info
5050
if alert_model:
51+
storage = self.get_storage_from_parsed_alert(
52+
ctxt, storage, alert_model)
5153
alert_util.fill_storage_attributes(alert_model, storage)
5254
except exception.IncompleteTrapInformation as e:
5355
LOG.warn(e)
5456
threading.Thread(target=self.sync_storage_alert,
5557
args=(ctxt, alert['storage_id'])).start()
58+
except exception.AlertSourceNotFound:
59+
LOG.info("Could not identify alert source from parsed alert. "
60+
"Skipping the dispatch of alert")
61+
return
5662
except Exception as e:
5763
LOG.error(e)
5864
raise exception.InvalidResults(
5965
"Failed to fill the alert model from driver.")
6066

6167
# Export to base exporter which handles dispatch for all exporters
6268
if alert_model:
63-
self.exporter_manager.dispatch(ctxt, alert_model)
69+
LOG.info("Dispatching one SNMP Trap to {} with sn {}".format(
70+
alert_model['storage_id'], alert_model['serial_number']))
71+
self.exporter_manager.dispatch(ctxt, [alert_model])
72+
73+
def get_storage_from_parsed_alert(self, ctxt, storage, alert_model):
74+
# If parse_alert sets 'serial_number' or 'storage_name' in the
75+
# alert_model, we need to get corresponding storage details
76+
# from the db and fill that in alert_model
77+
storage_sn = alert_model.get('serial_number')
78+
storage_name = alert_model.get('storage_name')
79+
filters = {
80+
"vendor": storage['vendor'],
81+
"model": storage['model'],
82+
}
83+
try:
84+
if storage_sn and storage_sn != storage['serial_number']:
85+
filters['serial_number'] = storage_sn
86+
elif storage_name and storage_name != storage['name']:
87+
filters['name'] = storage_name
88+
else:
89+
return storage
90+
91+
storage_list = db.storage_get_all(ctxt, filters=filters)
92+
if not storage_list:
93+
msg = "Failed to get destination storage for SNMP Trap. " \
94+
"Storage with serial number {} or storage name {} " \
95+
"not found in DB".format(storage_sn, storage_name)
96+
raise exception.AlertSourceNotFound(msg)
97+
db.alert_source_get(ctxt, storage_list[0]['id'])
98+
storage = storage_list[0]
99+
except exception.AlertSourceNotFound:
100+
LOG.info("Storage with serial number {} or name {} "
101+
"is not registered for receiving "
102+
"SNMP Trap".format(storage_sn, storage_name))
103+
raise
104+
105+
return storage
64106

65107
@coordination.synchronized('sync-trap-{storage_id}', blocking=False)
66108
def sync_storage_alert(self, context, storage_id):

delfin/alert_manager/trap_receiver.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,7 @@ def _get_alert_source_by_host(source_ip):
182182
if not alert_source:
183183
raise exception.AlertSourceNotFoundWithHost(source_ip)
184184

185-
# This is to make sure unique host is configured each alert source
186-
if len(alert_source) > 1:
187-
msg = (_("Failed to get unique alert source with host %s.")
188-
% source_ip)
189-
raise exception.InvalidResults(msg)
190-
185+
# Return first configured source that can handle the trap
191186
return alert_source[0]
192187

193188
def _cb_fun(self, state_reference, context_engine_id, context_name,

delfin/api/schemas/storages.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
'properties': {
2020
'vendor': {'type': 'string', 'minLength': 1, 'maxLength': 255},
2121
'model': {'type': 'string', 'minLength': 1, 'maxLength': 255},
22+
'storage_name': {'type': 'string', 'minLength': 1, 'maxLength': 255},
2223
'rest': {
2324
'type': 'object',
2425
'properties': {

delfin/api/v1/access_info.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
import copy
15+
1416
from delfin import db
1517
from delfin import cryptor
1618
from delfin.api import validation
1719
from delfin.api.common import wsgi
1820
from delfin.api.schemas import access_info as schema_access_info
1921
from delfin.api.views import access_info as access_info_viewer
22+
from delfin.db.sqlalchemy.models import AccessInfo
2023
from delfin.common import constants
2124
from delfin.drivers import api as driverapi
2225

@@ -34,11 +37,38 @@ def show(self, req, id):
3437
access_info = db.access_info_get(ctxt, id)
3538
return self._view_builder.show(access_info)
3639

40+
def _cm_access_info_update(self, ctxt, access_info, body):
41+
access_info_dict = copy.deepcopy(access_info)
42+
unused = ['created_at', 'updated_at', 'storage_name',
43+
'storage_id', 'extra_attributes']
44+
access_info_dict = AccessInfo.to_dict(access_info_dict)
45+
for field in unused:
46+
if access_info_dict.get(field):
47+
access_info_dict.pop(field)
48+
for access in constants.ACCESS_TYPE:
49+
if access_info_dict.get(access):
50+
access_info_dict.pop(access)
51+
52+
access_info_list = db.access_info_get_all(
53+
ctxt, filters=access_info_dict)
54+
55+
for cm_access_info in access_info_list:
56+
if cm_access_info['storage_id'] == access_info['storage_id']:
57+
continue
58+
for access in constants.ACCESS_TYPE:
59+
if cm_access_info.get(access):
60+
cm_access_info[access]['password'] = cryptor.decode(
61+
cm_access_info[access]['password'])
62+
if body.get(access):
63+
cm_access_info[access].update(body[access])
64+
self.driver_api.update_access_info(ctxt, cm_access_info)
65+
3766
@validation.schema(schema_access_info.update)
3867
def update(self, req, id, body):
3968
"""Update storage access information."""
4069
ctxt = req.environ.get('delfin.context')
4170
access_info = db.access_info_get(ctxt, id)
71+
self._cm_access_info_update(ctxt, access_info, body)
4272
for access in constants.ACCESS_TYPE:
4373
if access_info.get(access):
4474
access_info[access]['password'] = cryptor.decode(

delfin/api/v1/storages.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,9 @@ def sync(self, req, id):
186186

187187
def _storage_exist(self, context, access_info):
188188
access_info_dict = copy.deepcopy(access_info)
189+
access_info_list = access_info_filter(
190+
context, access_info_dict)
189191

190-
# Remove unrelated query fields
191-
unrelated_fields = ['username', 'password']
192-
for access in constants.ACCESS_TYPE:
193-
if access_info_dict.get(access):
194-
for key in unrelated_fields:
195-
access_info_dict[access].pop(key)
196-
197-
# Check if storage is registered
198-
access_info_list = db.access_info_get_all(context,
199-
filters=access_info_dict)
200192
for _access_info in access_info_list:
201193
try:
202194
storage = db.storage_get(context, _access_info['storage_id'])
@@ -260,3 +252,31 @@ def _set_synced_if_ok(context, storage_id, resource_count):
260252
storage['sync_status'] = resource_count * constants.ResourceSync.START
261253
storage['updated_at'] = current_time
262254
db.storage_update(context, storage['id'], storage)
255+
256+
257+
def access_info_filter(context, access_info):
258+
access_info_dict = copy.deepcopy(access_info)
259+
260+
for access in constants.ACCESS_TYPE:
261+
if access_info_dict.get(access):
262+
access_info_dict.pop(access)
263+
264+
# Check if storage is registered
265+
access_info_list = db.access_info_get_all(context,
266+
filters=access_info_dict)
267+
filtered_list = []
268+
for access_info_db in access_info_list:
269+
match = True
270+
for access in constants.ACCESS_TYPE:
271+
access_filter = access_info.get(access)
272+
access_db = access_info_db.get(access)
273+
if match and access_filter:
274+
if not access_db or\
275+
access_filter['host'] != access_db['host'] or\
276+
access_filter['port'] != access_db['port']:
277+
match = False
278+
break
279+
if match:
280+
filtered_list.append(access_info_db)
281+
282+
return filtered_list

delfin/context.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def __init__(self, user_id=None, project_id=None, is_admin=None,
7171
self.user_id = self.user
7272
self.tenant = project_id or tenant
7373
self.project_id = self.tenant
74+
self.storage_id = None
7475

7576
self.read_deleted = read_deleted
7677
self.remote_address = remote_address
@@ -107,6 +108,7 @@ def to_dict(self):
107108
values.update({
108109
'user_id': getattr(self, 'user_id', None),
109110
'project_id': getattr(self, 'project_id', None),
111+
'storage_id': getattr(self, 'storage_id', None),
110112
'read_deleted': getattr(self, 'read_deleted', None),
111113
'remote_address': getattr(self, 'remote_address', None),
112114
'timestamp': self.timestamp.isoformat() if hasattr(

delfin/db/sqlalchemy/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class AccessInfo(BASE, DelfinBase):
5151
"""Represent access info required for storage accessing."""
5252
__tablename__ = "access_info"
5353
storage_id = Column(String(36), primary_key=True)
54+
storage_name = Column(String(255))
5455
vendor = Column(String(255))
5556
model = Column(String(255))
5657
rest = Column(JsonEncodedDict)

delfin/drivers/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ def update_access_info(self, context, access_info):
6868

6969
def remove_storage(self, context, storage_id):
7070
"""Clear driver instance from driver factory."""
71+
driver = self.driver_manager.get_driver(context, storage_id=storage_id)
72+
driver.delete_storage(context)
7173
self.driver_manager.remove_driver(storage_id)
7274

7375
def get_storage(self, context, storage_id):

delfin/drivers/driver.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ def __init__(self, **kwargs):
2828
"""
2929
self.storage_id = kwargs.get('storage_id', None)
3030

31+
def delete_storage(self, context):
32+
"""Cleanup storage device information from driver"""
33+
pass
34+
35+
def add_storage(self, kwargs):
36+
"""Add storage device information to driver"""
37+
pass
38+
3139
@abc.abstractmethod
3240
def reset_connection(self, context, **kwargs):
3341
""" Reset connection with backend with new args """

delfin/drivers/manager.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from delfin import exception
2424
from delfin import utils
2525
from delfin import ssl_utils
26+
from delfin.common import constants
2627

2728
LOG = log.getLogger(__name__)
2829

@@ -54,6 +55,7 @@ def get_driver(self, context, invoke_on_load=True,
5455
:type cache_on_load: bool
5556
:param kwargs: Parameters from access_info.
5657
"""
58+
context.storage_id = kwargs.get('storage_id')
5759
kwargs = copy.deepcopy(kwargs)
5860
kwargs['verify'] = False
5961
ca_path = ssl_utils.get_storage_ca_path()
@@ -89,6 +91,7 @@ def _get_driver_obj(self, context, cache_on_load=True, **kwargs):
8991

9092
if kwargs['verify']:
9193
ssl_utils.reload_certificate(kwargs['verify'])
94+
9295
access_info = copy.deepcopy(kwargs)
9396
storage_id = access_info.pop('storage_id')
9497
access_info.pop('verify')
@@ -98,6 +101,28 @@ def _get_driver_obj(self, context, cache_on_load=True, **kwargs):
98101
else:
99102
access_info = db.access_info_get(
100103
context, storage_id).to_dict()
104+
105+
access_info_dict = copy.deepcopy(access_info)
106+
remove_fields = ['created_at', 'updated_at',
107+
'storage_id', 'storage_name',
108+
'extra_attributes']
109+
# Remove unrelated query fields
110+
for field in remove_fields:
111+
if access_info_dict.get(field):
112+
access_info_dict.pop(field)
113+
for access in constants.ACCESS_TYPE:
114+
if access_info_dict.get(access):
115+
access_info_dict.pop(access)
116+
117+
access_info_list = db.access_info_get_all(
118+
context, filters=access_info_dict)
119+
for _access_info in access_info_list:
120+
if _access_info['storage_id'] in self.driver_factory:
121+
driver = self.driver_factory[
122+
_access_info['storage_id']]
123+
driver.add_storage(access_info)
124+
self.driver_factory[storage_id] = driver
125+
return driver
101126
access_info['verify'] = kwargs.get('verify')
102127
cls = self._get_driver_cls(**access_info)
103128
driver = cls(**access_info)

0 commit comments

Comments
 (0)