|
| 1 | +#!/usr/bin/python3 |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | + |
| 4 | +import os |
| 5 | +import json |
| 6 | +import requests |
| 7 | +import argparse |
| 8 | +import time |
| 9 | +import subprocess |
| 10 | +import tempfile |
| 11 | +import sys |
| 12 | +import urllib3 |
| 13 | +import logging |
| 14 | +import logging.handlers |
| 15 | +#urllib3.disable_warnings() |
| 16 | +#urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) |
| 17 | +from requests.packages.urllib3.exceptions import InsecureRequestWarning |
| 18 | +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | + |
| 23 | + |
| 24 | +LOG_FILENAME = "/tmp/huawei_state.log" |
| 25 | +HUAWEI_NAME = sys.argv[5][15:] |
| 26 | +huawei_Logger = logging.getLogger("huawei_logger") |
| 27 | +huawei_Logger.setLevel(logging.INFO) |
| 28 | + |
| 29 | +huawei_Handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=(1024**2)*5, backupCount=5) |
| 30 | +huawei_Formatter = logging.Formatter("{0} - %(asctime)s - %(name)s - %(levelname)s - %(message)s".format(HUAWEI_NAME), datefmt = '%Y-%m-%d %H:%M:%S') |
| 31 | + |
| 32 | +huawei_Handler.setFormatter(huawei_Formatter) |
| 33 | +huawei_Logger.addHandler(huawei_Handler) |
| 34 | + |
| 35 | + |
| 36 | + |
| 37 | + |
| 38 | + |
| 39 | +def api_connect(api_user, api_password, api_ip, api_port): |
| 40 | + api_url = "https://{0}:{1}/deviceManager/rest/xxxxx/sessions".format(api_ip, api_port) |
| 41 | + api_data = json.dumps({'scope': '0', 'username': api_user, 'password': api_password}) |
| 42 | + headers = {'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Accept': 'application/json'} |
| 43 | + |
| 44 | + try: |
| 45 | + api_connect = requests.post(api_url, verify=False, data = api_data, headers = headers, timeout=3) |
| 46 | + api_connect_info = (json.loads(api_connect.content.decode('utf8')),api_connect.cookies) #Decode нужен потому что объект имеет тип bytes |
| 47 | + except requests.exceptions.ConnectTimeout: |
| 48 | + huawei_Logger.error("*********************** Connection Timeout Error Occurs***********************\n\n\n") |
| 49 | + sys.exit("102") |
| 50 | + except Exception as oops: |
| 51 | + exc_type, exc_value, exc_traceback = sys.exc_info() |
| 52 | + huawei_Logger.error("Exception type - {0}\nException value - {1}\nException traceback - {2}".format(exc_type, exc_value, exc_traceback)) |
| 53 | + huawei_Logger.error("*********************** Connect to API is failed ***********************\n\n\n") |
| 54 | + sys.exit("103") |
| 55 | + return api_connect_info |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +def api_logout(api_ip, api_port, api_cookies, device_id, iBaseToken): |
| 60 | + headers = {'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Accept': 'application/json', 'iBaseToken':iBaseToken} |
| 61 | + api_url_exit = "https://{0}:{1}/deviceManager/rest/{2}/sessions".format(api_ip, api_port, device_id) |
| 62 | + exit = requests.delete(api_url_exit, verify=False, headers = headers, cookies = api_cookies) |
| 63 | + |
| 64 | + convert_exit = json.loads(exit.content.decode('utf8')) |
| 65 | + return convert_exit['error']['code'] |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +def convert_to_zabbix_json(data): |
| 70 | + output = json.dumps({"data": data}, indent = None, separators = (',',': ')) |
| 71 | + return output |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +def send_data_to_zabbix(zabbix_data, storage_name): |
| 76 | + sender_command = "/usr/bin/zabbix_sender" |
| 77 | + config_path = "/etc/zabbix/zabbix_agentd.conf" |
| 78 | + time_of_create_file = int(time.time()) |
| 79 | + temp_file = "/tmp/{0}_{1}.tmp".format(storage_name, time_of_create_file) |
| 80 | + |
| 81 | + with open(temp_file, "w") as f: |
| 82 | + f.write("") |
| 83 | + f.write("\n".join(zabbix_data)) |
| 84 | + |
| 85 | + send_to_zabbix = subprocess.Popen([sender_command, "-vv", "-c", config_path, "-s", storage_name, "-T", "-i", temp_file], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) |
| 86 | + send_to_zabbix.wait() |
| 87 | + try: |
| 88 | + debug_of_send_to_zabbix = send_to_zabbix.communicate(timeout=10) |
| 89 | + huawei_Logger.info("{0}{1}".format(debug_of_send_to_zabbix[0].decode("utf-8"), debug_of_send_to_zabbix[1].decode("utf-8"))) |
| 90 | + except subprocess.TimeoutExpired: |
| 91 | + send_to_zabbix.kill() |
| 92 | + huawei_Logger.error("------------- Shit happens ----------------\n\n\n") |
| 93 | + huawei_Logger.info("ReturnCode of zabbix_sender = {0}".format(send_to_zabbix.returncode)) |
| 94 | + os.remove(temp_file) |
| 95 | + return send_to_zabbix.returncode |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +def discovering_resources(api_user, api_password, api_ip, api_port, storage_name, list_resources): |
| 100 | + api_connection = api_connect(api_user, api_password, api_ip, api_port) |
| 101 | + |
| 102 | + device_id = api_connection[0]['data']['deviceid'] |
| 103 | + iBaseToken = api_connection[0]['data']['iBaseToken'] |
| 104 | + api_cookies = api_connection[1] |
| 105 | + headers = {'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Accept': 'application/json', 'iBaseToken':iBaseToken} |
| 106 | + |
| 107 | + something = [] |
| 108 | + try: |
| 109 | + for resource in list_resources: |
| 110 | + resource_url = "https://{0}:{1}/deviceManager/rest/{2}/{3}".format(api_ip, api_port, device_id, resource) |
| 111 | + resource_info = requests.get(resource_url, verify=False, cookies = api_cookies, headers = headers) |
| 112 | + resource_info = json.loads(resource_info.content.decode('cp1251')) # Конвертация из JSON-объекта в словарь |
| 113 | + |
| 114 | + |
| 115 | + discovered_resource = [] |
| 116 | + for one_res in resource_info['data']: |
| 117 | + resources_list = dict() |
| 118 | + if resource == 'disk': |
| 119 | + resources_list["{#ID}"] = one_res['ID'] |
| 120 | + resources_list["{#LOCATION}"] = one_res['LOCATION'].replace(' ', '_') |
| 121 | + discovered_resource.append(resources_list) |
| 122 | + elif ['diskpool', 'storagepool', 'lun'].count(resource) == 1: |
| 123 | + resources_list["{#NAME}"] = one_res['NAME'].replace(' ','_') |
| 124 | + discovered_resource.append(resources_list) |
| 125 | + else: |
| 126 | + resources_list["{#NAME}"] = one_res['NAME'].replace(' ','_') |
| 127 | + resources_list["{#ID}"] = one_res['ID'] |
| 128 | + resources_list["{#LOCATION}"] = one_res['LOCATION'].replace(' ','_') |
| 129 | + discovered_resource.append(resources_list) |
| 130 | + |
| 131 | + converted_resource = convert_to_zabbix_json(discovered_resource) |
| 132 | + timestampnow = int(time.time()) |
| 133 | + something.append("%s %s %s %s" % (storage_name, resource, timestampnow, converted_resource)) |
| 134 | + |
| 135 | + exit_code = api_logout(api_ip, api_port, api_cookies, device_id, iBaseToken) |
| 136 | + except Exception as oops: |
| 137 | + exc_type, exc_value, exc_traceback = sys.exc_info() |
| 138 | + huawei_Logger.error("Exception type - {0}\nException value - {1}\nException traceback - {2}".format(exc_type, exc_value, exc_traceback)) |
| 139 | + huawei_Logger.error("*********************** Discovering is failed ***********************\n\n\n") |
| 140 | + sys.exit("1000") |
| 141 | +# if exit_code != 0: |
| 142 | +# return 5 |
| 143 | + |
| 144 | + return send_data_to_zabbix(something, storage_name) |
| 145 | + |
| 146 | + |
| 147 | + |
| 148 | + |
| 149 | +def get_status_resources(api_user, api_password, api_ip, api_port, storage_name, list_resources): |
| 150 | + api_connection = api_connect(api_user, api_password, api_ip, api_port) |
| 151 | + |
| 152 | + device_id = api_connection[0]['data']['deviceid'] |
| 153 | + iBaseToken = api_connection[0]['data']['iBaseToken'] |
| 154 | + api_cookies = api_connection[1] |
| 155 | + headers = {'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Accept': 'application/json', 'iBaseToken':iBaseToken} |
| 156 | + |
| 157 | + |
| 158 | + state_resources = [] # В этот список будут складываться состояние каждого ресурса (объекта) в формате zabbix |
| 159 | + try: |
| 160 | + for resource in list_resources: |
| 161 | + resource_url = "https://{0}:{1}/deviceManager/rest/{2}/{3}".format(api_ip, api_port, device_id, resource) |
| 162 | + resource_info = requests.get(resource_url, verify=False, cookies = api_cookies, headers = headers) |
| 163 | + resource_info = json.loads(resource_info.content.decode('cp1251')) # Конвертация из JSON-объекта в словарь |
| 164 | + timestampnow = int(time.time()) |
| 165 | + |
| 166 | + if ['fc_port','sas_port', 'eth_port'].count(resource) == 1: |
| 167 | + for one_res in resource_info['data']: |
| 168 | + key_health = "health.{0}.[{1}]".format(resource, one_res['LOCATION'].replace(' ','_')) # Создаем ключ для каждого объекта и заменяем пробелы |
| 169 | + key_status = "running.{0}.[{1}]".format(resource, one_res['LOCATION'].replace(' ','_')) # Создаем ключ для каждого объекта и заменяем пробелы |
| 170 | + state_resources.append("%s %s %s %s" % (storage_name, key_health, timestampnow, one_res['HEALTHSTATUS'])) |
| 171 | + state_resources.append("%s %s %s %s" % (storage_name, key_status, timestampnow, one_res['RUNNINGSTATUS'])) |
| 172 | + elif ['diskpool'].count(resource) == 1: |
| 173 | + for one_res in resource_info['data']: |
| 174 | + key_health = "health.{0}.[{1}]".format(resource, one_res['NAME']) |
| 175 | + key_status = "running.{0}.[{1}]".format(resource, one_res['NAME']) |
| 176 | + state_resources.append("%s %s %s %s" % (storage_name, key_health, timestampnow, one_res['HEALTHSTATUS'])) |
| 177 | + state_resources.append("%s %s %s %s" % (storage_name, key_status, timestampnow, one_res['RUNNINGSTATUS'])) |
| 178 | + elif ['lun'].count(resource) == 1: |
| 179 | + subscribed_capacity_pools = {} # Здесь будет: имя пула - сумма объемов лунов, принадлежащих этому пулу |
| 180 | + for one_res in resource_info['data']: |
| 181 | + key_health = "health.{0}.[{1}]".format(resource, one_res['NAME']) |
| 182 | + key_status = "running.{0}.[{1}]".format(resource, one_res['NAME']) |
| 183 | + state_resources.append("%s %s %s %s" % (storage_name, key_health, timestampnow, one_res['HEALTHSTATUS'])) |
| 184 | + state_resources.append("%s %s %s %s" % (storage_name, key_status, timestampnow, one_res['RUNNINGSTATUS'])) |
| 185 | + |
| 186 | + |
| 187 | + # Блок кода, подсчитывает сумму "выделенных объемов" лунов для каждого пула. Нужно для подсчета подписанного места на каждом пуле |
| 188 | + pool_name = one_res['PARENTNAME'] |
| 189 | + abr = list(subscribed_capacity_pools.keys()) #Конвертируем ключи словаря в список, что бы было легче сравнивать |
| 190 | + if abr.count(pool_name) == 0: #А есть ли в словаре такой лун? |
| 191 | + subscribed_capacity_pools[pool_name] = 0 #Нету луна? добавь его имя как ключ в словарь. Начальное значение = 0, сюда суммируются объемы лунов |
| 192 | + capacity_lun_in_blocks = int(one_res['CAPACITY']) # Конвертируем из строки в число |
| 193 | + size_block = int(one_res['SECTORSIZE']) |
| 194 | + subscribed_capacity_pools[pool_name] += capacity_lun_in_blocks * size_block # Умножением переводим из блоков в байты |
| 195 | + |
| 196 | + |
| 197 | + # Формируется формат zabbix для подписанного места из словаря |
| 198 | + for key in subscribed_capacity_pools.keys(): |
| 199 | + key_subscribed_capacity = "subscribed.capacity.{0}.[{1}]".format('storagepool', key) # key - принимает имя пула |
| 200 | + state_resources.append("%s %s %s %s" %(storage_name, key_subscribed_capacity, timestampnow, subscribed_capacity_pools[key])) |
| 201 | + |
| 202 | + elif ['storagepool'].count(resource) == 1: |
| 203 | + for one_res in resource_info['data']: |
| 204 | + key_health = "health.{0}.[{1}]".format(resource, one_res['NAME']) |
| 205 | + key_status = "running.{0}.[{1}]".format(resource, one_res['NAME']) |
| 206 | + key_total_capacity = "total.capacity.{0}.[{1}]".format(resource, one_res['NAME']) |
| 207 | + key_free_capacity = "free.capacity.{0}.[{1}]".format(resource, one_res['NAME']) |
| 208 | + key_used_capacity = "used.capacity.{0}.[{1}]".format(resource, one_res['NAME']) |
| 209 | + |
| 210 | + state_resources.append("%s %s %s %s" % (storage_name, key_health, timestampnow, one_res['HEALTHSTATUS'])) |
| 211 | + state_resources.append("%s %s %s %s" % (storage_name, key_status, timestampnow, one_res['RUNNINGSTATUS'])) |
| 212 | + state_resources.append("%s %s %s %s" % (storage_name, key_total_capacity, timestampnow, int(one_res['USERTOTALCAPACITY']) * 512)) |
| 213 | + state_resources.append("%s %s %s %s" % (storage_name, key_free_capacity, timestampnow, int(one_res['USERFREECAPACITY']) * 512)) |
| 214 | + state_resources.append("%s %s %s %s" % (storage_name, key_used_capacity, timestampnow, int(one_res['USERCONSUMEDCAPACITY']) * 512)) |
| 215 | + |
| 216 | + """ |
| 217 | + Обход момента, когда в пуле нет лунов (т.е. used_capacity пула равен нулю). |
| 218 | + Когда нет лунов в пуле, невозможно вычислить подписанное место, |
| 219 | + т.к. подписанное место вычисляется в разделе лунов путем сложения total_capacity всех лунов, принадлежащих данному пулу. |
| 220 | + """ |
| 221 | + if int(one_res['USERCONSUMEDCAPACITY']) == 0: |
| 222 | + key_subscribed_capacity = "subscribed.capacity.{0}.[{1}]".format(resource,one_res['NAME']) |
| 223 | + state_resources.append("%s %s %s %s" % (storage_name, key_subscribed_capacity, timestampnow, 0)) |
| 224 | + |
| 225 | + else: |
| 226 | + for one_res in resource_info['data']: |
| 227 | + key_health = "health.{0}.[{1}]".format(resource, one_res['LOCATION'].replace(' ','_')) # Создаем ключ для каждого объекта и заменяем пробелы |
| 228 | + key_status = "running.{0}.[{1}]".format(resource, one_res['LOCATION'].replace(' ','_')) |
| 229 | + state_resources.append("%s %s %s %s" % (storage_name, key_health, timestampnow, one_res['HEALTHSTATUS'])) |
| 230 | + state_resources.append("%s %s %s %s" % (storage_name, key_status, timestampnow, one_res['RUNNINGSTATUS'])) |
| 231 | + |
| 232 | + exit_code = api_logout(api_ip, api_port, api_cookies, device_id, iBaseToken) |
| 233 | + except Exception as oops: |
| 234 | + exc_type, exc_value, exc_traceback = sys.exc_info() |
| 235 | + huawei_Logger.error("Exception type - {0}\nException value - {1}\nException traceback - {2}".format(exc_type, exc_value, exc_traceback)) |
| 236 | + huawei_Logger.error("*********************** Get status is failed ***********************\n\n\n") |
| 237 | + sys.exit("1000") |
| 238 | +# if exit_code != 0: |
| 239 | +# return 5 |
| 240 | +# print (state_resources) |
| 241 | + return send_data_to_zabbix(state_resources, storage_name) |
| 242 | + |
| 243 | + |
| 244 | +def main(): |
| 245 | + |
| 246 | + huawei_parser = argparse.ArgumentParser() |
| 247 | + huawei_parser.add_argument('--api_ip', action="store", help="Where to connect", required=True) |
| 248 | + huawei_parser.add_argument('--api_port', action="store", required=True) |
| 249 | + huawei_parser.add_argument('--api_user', action="store", required=True) |
| 250 | + huawei_parser.add_argument('--api_password', action="store", required=True) |
| 251 | + huawei_parser.add_argument('--storage_name', action="store", required=True) |
| 252 | + |
| 253 | + group = huawei_parser.add_mutually_exclusive_group(required=True) |
| 254 | + group.add_argument('--discovery', action ='store_true') |
| 255 | + group.add_argument('--status', action='store_true') |
| 256 | + arguments = huawei_parser.parse_args() |
| 257 | + |
| 258 | + |
| 259 | + list_resources = ['disk', 'power', 'enclosure', 'controller', 'backup_power', 'expboard', 'intf_module', 'eth_port', 'sas_port', 'fc_port', 'fan', 'lun', 'diskpool', 'storagepool'] |
| 260 | + |
| 261 | + if arguments.discovery: |
| 262 | + huawei_Logger.info("********************************* Discovery is starting *********************************") |
| 263 | + result_discovery = discovering_resources(arguments.api_user, arguments.api_password, arguments.api_ip, arguments.api_port, arguments.storage_name, list_resources) |
| 264 | + huawei_Logger.info("********************************* Discovery is ended *********************************\n\n\n") |
| 265 | + print (result_discovery) |
| 266 | + elif arguments.status: |
| 267 | + huawei_Logger.info("********************************* Get State is starting *********************************") |
| 268 | + result_status = get_status_resources(arguments.api_user, arguments.api_password, arguments.api_ip, arguments.api_port, arguments.storage_name, list_resources) |
| 269 | + huawei_Logger.info("********************************* Get State is ended *********************************\n\n\n") |
| 270 | + print (result_status) |
| 271 | + |
| 272 | + |
| 273 | +if __name__ == "__main__": |
| 274 | + main() |
0 commit comments