-
Notifications
You must be signed in to change notification settings - Fork 354
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add dell_emc unity device information collection (#416)
- Loading branch information
1 parent
87e65f3
commit 34bdef6
Showing
8 changed files
with
1,149 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
# Copyright 2020 The SODA Authors. | ||
# | ||
# 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. | ||
import time | ||
|
||
import six | ||
from oslo_log import log | ||
|
||
from delfin import exception | ||
from delfin.common import alert_util | ||
from delfin.common import constants | ||
from delfin.i18n import _ | ||
|
||
LOG = log.getLogger(__name__) | ||
|
||
|
||
class AlertHandler(object): | ||
|
||
TIME_PATTERN = "%Y-%m-%dT%H:%M:%S.%fZ" | ||
|
||
OID_SEVERITY = '1.3.6.1.6.3.1.1.4.1.0' | ||
OID_NODE = '1.3.6.1.4.1.1139.103.1.18.1.1' | ||
OID_COMPONENT = '1.3.6.1.4.1.1139.103.1.18.1.2' | ||
OID_SYMPTOMID = '1.3.6.1.4.1.1139.103.1.18.1.3' | ||
OID_SYMPTOMTEXT = '1.3.6.1.4.1.1139.103.1.18.1.4' | ||
OID_TIMESTAMP = '1.3.6.1.4.1.1139.103.1.18.1.5' | ||
|
||
ALERT_LEVEL_MAP = {0: constants.Severity.CRITICAL, | ||
1: constants.Severity.CRITICAL, | ||
2: constants.Severity.CRITICAL, | ||
3: constants.Severity.MAJOR, | ||
4: constants.Severity.WARNING, | ||
5: constants.Severity.FATAL, | ||
6: constants.Severity.INFORMATIONAL, | ||
7: constants.Severity.NOT_SPECIFIED | ||
} | ||
|
||
TRAP_LEVEL_MAP = {'1.3.6.1.4.1.1139.103.1.18.2.0': | ||
constants.Severity.CRITICAL, | ||
'1.3.6.1.4.1.1139.103.1.18.2.1': | ||
constants.Severity.CRITICAL, | ||
'1.3.6.1.4.1.1139.103.1.18.2.2': | ||
constants.Severity.CRITICAL, | ||
'1.3.6.1.4.1.1139.103.1.18.2.3': | ||
constants.Severity.MAJOR, | ||
'1.3.6.1.4.1.1139.103.1.18.2.4': | ||
constants.Severity.WARNING, | ||
'1.3.6.1.4.1.1139.103.1.18.2.5': | ||
constants.Severity.FATAL, | ||
'1.3.6.1.4.1.1139.103.1.18.2.6': | ||
constants.Severity.INFORMATIONAL, | ||
'1.3.6.1.4.1.1139.103.1.18.2.7': | ||
constants.Severity.NOT_SPECIFIED | ||
} | ||
SECONDS_TO_MS = 1000 | ||
|
||
@staticmethod | ||
def parse_alert(context, alert): | ||
try: | ||
alert_model = dict() | ||
alert_model['alert_id'] = alert.get(AlertHandler.OID_SYMPTOMID) | ||
alert_model['alert_name'] = alert.get(AlertHandler.OID_COMPONENT) | ||
alert_model['severity'] = AlertHandler.TRAP_LEVEL_MAP.get( | ||
alert.get(AlertHandler.OID_SEVERITY), | ||
constants.Severity.INFORMATIONAL) | ||
alert_model['category'] = constants.Category.FAULT | ||
alert_model['type'] = constants.EventType.EQUIPMENT_ALARM | ||
occur_time = int(time.time()) * AlertHandler.SECONDS_TO_MS | ||
alert_model['occur_time'] = occur_time | ||
alert_model['description'] = alert.get( | ||
AlertHandler.OID_SYMPTOMTEXT) | ||
alert_model['resource_type'] = constants.DEFAULT_RESOURCE_TYPE | ||
alert_model['location'] = alert.get(AlertHandler.OID_NODE) | ||
|
||
return alert_model | ||
except Exception as e: | ||
LOG.error(e) | ||
msg = (_("Failed to build alert model as some attributes missing " | ||
"in alert message.")) | ||
raise exception.InvalidResults(msg) | ||
|
||
def parse_queried_alerts(self, alert_model_list, alert_list, query_para): | ||
alerts = alert_list.get('entries') | ||
for alert in alerts: | ||
try: | ||
occur_time = int(time.mktime(time.strptime( | ||
alert.get('content').get('timestamp'), | ||
self.TIME_PATTERN))) | ||
if not alert_util.is_alert_in_time_range( | ||
query_para, int(occur_time * | ||
AlertHandler.SECONDS_TO_MS)): | ||
continue | ||
|
||
alert_model = {} | ||
location = '' | ||
resource_type = constants.DEFAULT_RESOURCE_TYPE | ||
if 'component' in alert: | ||
resource_type = alert.get( | ||
'content').get('component').get('resource') | ||
location = alert.get( | ||
'content').get('component').get('id') | ||
|
||
alert_model['alert_id'] = alert.get( | ||
'content').get('messageId') | ||
alert_model['alert_name'] = alert.get( | ||
'content').get('message') | ||
alert_model['severity'] = self.ALERT_LEVEL_MAP.get( | ||
alert.get('content').get('severity'), | ||
constants.Severity.INFORMATIONAL) | ||
alert_model['category'] = constants.Category.FAULT | ||
alert_model['type'] = constants.EventType.EQUIPMENT_ALARM | ||
alert_model['sequence_number'] = alert.get('content').get('id') | ||
alert_model['occur_time'] = int(occur_time * | ||
AlertHandler.SECONDS_TO_MS) | ||
alert_model['description'] = alert.get('content').get( | ||
'description') | ||
alert_model['resource_type'] = resource_type | ||
alert_model['location'] = location | ||
|
||
alert_model_list.append(alert_model) | ||
except Exception as e: | ||
LOG.error(e) | ||
err_msg = "Failed to build alert model as some attributes " \ | ||
"missing in queried alerts: %s" % (six.text_type(e)) | ||
raise exception.InvalidResults(err_msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Copyright 2020 The SODA Authors. | ||
# 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. | ||
SOCKET_TIMEOUT = 10 | ||
ERROR_SESSION_INVALID_CODE = 403 | ||
ERROR_SESSION_IS_BEING_USED_CODE = 409 | ||
HEALTH_OK = (5, 7) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
# Copyright 2020 The SODA Authors. | ||
# 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. | ||
|
||
import requests | ||
import six | ||
from oslo_log import log as logging | ||
|
||
from delfin import cryptor | ||
from delfin import exception | ||
from delfin import ssl_utils | ||
from delfin.drivers.dell_emc.unity import consts | ||
from delfin.drivers.utils.rest_client import RestClient | ||
|
||
LOG = logging.getLogger(__name__) | ||
|
||
|
||
class RestHandler(RestClient): | ||
REST_AUTH_URL = '/api/types/loginSessionInfo/instances' | ||
REST_LOGOUT_URL = '/api/types/loginSessionInfo/action/logout' | ||
REST_STORAGE_URL = '/api/types/system/instances' | ||
REST_CAPACITY_URL = '/api/types/systemCapacity/instances' | ||
REST_POOLS_URL = '/api/types/pool/instances' | ||
REST_LUNS_URL = '/api/types/lun/instances' | ||
REST_ALERTS_URL = '/api/types/alert/instances' | ||
REST_DEL_ALERTS_URL = '/api/instances/alert/' | ||
REST_DISK_URL = '/api/types/disk/instances' | ||
REST_SOFT_VERSION_URL = '/api/types/installedSoftwareVersion/instances' | ||
REST_AUTH_KEY = 'EMC-CSRF-TOKEN' | ||
|
||
def __init__(self, **kwargs): | ||
super(RestHandler, self).__init__(**kwargs) | ||
|
||
def call(self, url, data=None, method=None): | ||
try: | ||
res = self.do_call(url, data, method, | ||
calltimeout=consts.SOCKET_TIMEOUT) | ||
if (res.status_code == consts.ERROR_SESSION_INVALID_CODE | ||
or res.status_code == | ||
consts.ERROR_SESSION_IS_BEING_USED_CODE): | ||
LOG.error( | ||
"Failed to get token=={0}=={1},get it again".format( | ||
res.status_code, res.text)) | ||
if RestHandler.REST_LOGOUT_URL in url: | ||
return res | ||
self.rest_auth_token = None | ||
access_session = self.login() | ||
# if get token,Revisit url | ||
if access_session is not None: | ||
res = self. \ | ||
do_call(url, data, method, | ||
calltimeout=consts.SOCKET_TIMEOUT) | ||
else: | ||
LOG.error('Login session is none') | ||
elif res.status_code == 503: | ||
raise exception.InvalidResults(res.text) | ||
return res | ||
except Exception as e: | ||
err_msg = "Get restHandler.call failed: %s" % (six.text_type(e)) | ||
LOG.error(err_msg) | ||
raise e | ||
|
||
def get_rest_info(self, url, data=None, method='GET'): | ||
result_json = None | ||
res = self.call(url, data, method) | ||
if res.status_code == 200: | ||
result_json = res.json() | ||
return result_json | ||
|
||
def init_rest_client(self): | ||
if self.session: | ||
self.session.close() | ||
self.session = requests.Session() | ||
self.session.headers.update({ | ||
'Accept': 'application/json', | ||
"Content-Type": "application/json", | ||
"X-EMC-REST-CLIENT": "true"}) | ||
self.session.auth = requests.auth.HTTPBasicAuth( | ||
self.rest_username, | ||
cryptor.decode(self.rest_password)) | ||
if not self.verify: | ||
self.session.verify = False | ||
else: | ||
LOG.debug("Enable certificate verification, ca_path: {0}".format( | ||
self.verify)) | ||
self.session.verify = self.verify | ||
self.session.trust_env = False | ||
self.session.mount("https://", ssl_utils.HostNameIgnoreAdapter()) | ||
|
||
def login(self): | ||
try: | ||
access_session = self.rest_auth_token | ||
if self.rest_auth_token is None: | ||
url = RestHandler.REST_AUTH_URL | ||
data = {} | ||
self.init_rest_client() | ||
res = self. \ | ||
do_call(url, data, 'GET', | ||
calltimeout=consts.SOCKET_TIMEOUT) | ||
if res.status_code == 200: | ||
access_session = res.headers['EMC-CSRF-TOKEN'] | ||
self.rest_auth_token = access_session | ||
self.session.headers[ | ||
RestHandler.REST_AUTH_KEY] = access_session | ||
else: | ||
LOG.error("Login error. URL: %(url)s\n" | ||
"Reason: %(reason)s.", | ||
{"url": url, "reason": res.text}) | ||
if 'invalid username or password' in res.text: | ||
raise exception.InvalidUsernameOrPassword() | ||
else: | ||
raise exception.BadResponse(res.text) | ||
else: | ||
LOG.error('Login Parameter error') | ||
return access_session | ||
except Exception as e: | ||
LOG.error("Login error: %s", six.text_type(e)) | ||
raise e | ||
|
||
def logout(self): | ||
try: | ||
url = RestHandler.REST_LOGOUT_URL | ||
if self.rest_auth_token is not None: | ||
url = '%s/%s' % (url, self.rest_auth_token) | ||
self.rest_auth_token = None | ||
if self.san_address: | ||
self.call(url, method='POST') | ||
if self.session: | ||
self.session.close() | ||
except exception.DelfinException as e: | ||
err_msg = "Logout error: %s" % (e.msg) | ||
LOG.error(err_msg) | ||
raise e | ||
except Exception as e: | ||
err_msg = "Logout error: %s" % (six.text_type(e)) | ||
LOG.error(err_msg) | ||
raise e | ||
|
||
def get_storage(self): | ||
url = '%s?%s' % (RestHandler.REST_STORAGE_URL, | ||
'fields=name,model,serialNumber,health') | ||
result_json = self.get_rest_info(url) | ||
return result_json | ||
|
||
def get_capacity(self): | ||
url = '%s?%s' % (RestHandler.REST_CAPACITY_URL, | ||
'fields=sizeFree,sizeTotal,sizeUsed,' | ||
'sizeSubscribed,totalLogicalSize') | ||
result_json = self.get_rest_info(url) | ||
return result_json | ||
|
||
def get_all_pools(self): | ||
url = '%s?%s' % (RestHandler.REST_POOLS_URL, | ||
'fields=id,name,health,type,sizeFree,' | ||
'sizeTotal,sizeUsed,sizeSubscribed') | ||
result_json = self.get_rest_info(url) | ||
return result_json | ||
|
||
def get_all_luns(self, page_size): | ||
url = '%s?%s&page=%s' % (RestHandler.REST_LUNS_URL, | ||
'fields=id,name,health,type,sizeAllocated,' | ||
'sizeTotal,sizeUsed,pool,wwn,isThinEnabled', | ||
page_size) | ||
result_json = self.get_rest_info(url) | ||
return result_json | ||
|
||
def get_all_alerts(self, page_size): | ||
url = '%s?%s&page=%s' % (RestHandler.REST_ALERTS_URL, | ||
'fields=id,timestamp,severity,component,' | ||
'messageId,message,description,descriptionId', | ||
page_size) | ||
result_json = self.get_rest_info(url) | ||
return result_json | ||
|
||
def get_soft_version(self): | ||
url = '%s?%s' % (RestHandler.REST_SOFT_VERSION_URL, | ||
'fields=version') | ||
result_json = self.get_rest_info(url) | ||
return result_json | ||
|
||
def get_disk_info(self): | ||
url = '%s?%s' % (RestHandler.REST_DISK_URL, | ||
'fields=rawSize') | ||
result_json = self.get_rest_info(url) | ||
return result_json | ||
|
||
def remove_alert(self, alert_id): | ||
url = RestHandler.REST_DEL_ALERTS_URL % alert_id | ||
result_json = self.get_rest_info(url, method='DELETE') | ||
return result_json |
Oops, something went wrong.