1# Copyright (c) 2017 Veritas Technologies LLC. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# not use this file except in compliance with the License. You may obtain 5# a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations 13# under the License. 14 15import json 16import uuid 17 18from oslo_concurrency import processutils as putils 19from oslo_log import log as logging 20from oslo_utils import excutils 21import six 22 23from cinder import exception 24from cinder import utils 25from cinder.volume.drivers.veritas import hs_constants as constants 26 27LOG = logging.getLogger(__name__) 28 29 30def _populate_message_body(kwargs): 31 message_body = {} 32 # Build message body from kwargs 33 for key, value in kwargs.items(): 34 if value is not None: 35 message_body[key] = value 36 37 return message_body 38 39 40def generate_routingkey(): 41 return six.text_type(uuid.uuid1()) 42 43 44def get_guid_with_curly_brackets(guid): 45 return "{%s}" % guid if guid else guid 46 47 48def get_hyperscale_image_id(): 49 return "{%s}" % uuid.uuid1() 50 51 52def get_hyperscale_version(): 53 54 version = None 55 cmd_err = None 56 try: 57 cmd_arg = {'operation': 'version'} 58 # create a json for cmd argument 59 cmdarg_json = json.dumps(cmd_arg) 60 61 # call hscli for version 62 (cmd_out, cmd_err) = hsexecute(cmdarg_json) 63 64 # cmd_err should be None in case of successful execution of cmd 65 if not cmd_err: 66 processed_output = process_cmd_out(cmd_out) 67 version = processed_output.get('payload') 68 else: 69 LOG.error("Error %s in getting hyperscale version", 70 cmd_err) 71 raise exception.ErrorInHyperScaleVersion(cmd_err=cmd_err) 72 except (exception.UnableToExecuteHyperScaleCmd, 73 exception.UnableToProcessHyperScaleCmdOutput): 74 LOG.error("Exception in running the command for version", 75 exc_info=True) 76 raise exception.UnableToExecuteHyperScaleCmd(message="version") 77 78 return version 79 80 81def get_datanode_id(): 82 83 dnid = None 84 cmd_out = None 85 cmd_err = None 86 try: 87 cmd_arg = {'operation': 'get_datanode_id'} 88 # create a json for cmd argument 89 cmdarg_json = json.dumps(cmd_arg) 90 91 # call hscli for get_datanode_id 92 (cmd_out, cmd_err) = hsexecute(cmdarg_json) 93 94 # cmd_err should be None in case of successful execution of cmd 95 if not cmd_err: 96 processed_output = process_cmd_out(cmd_out) 97 dnid = processed_output.get('payload') 98 else: 99 LOG.error("Error %s in getting datanode hypervisor id", 100 cmd_err) 101 raise exception.UnableToExecuteHyperScaleCmd( 102 message=cmdarg_json) 103 except exception.UnableToExecuteHyperScaleCmd: 104 with excutils.save_and_reraise_exception(): 105 LOG.debug("Unable to execute get_datanode_id", exc_info=True) 106 107 except exception.UnableToProcessHyperScaleCmdOutput: 108 with excutils.save_and_reraise_exception(): 109 LOG.debug("Unable to process get_datanode_id output", 110 exc_info=True) 111 return dnid 112 113 114def episodic_snap(meta): 115 116 cmd_out = None 117 cmd_err = None 118 out_meta = None 119 try: 120 cmd_arg = {} 121 cmd_arg['operation'] = 'episodic_snap' 122 cmd_arg['metadata'] = meta 123 # create a json for cmd argument 124 cmdarg_json = json.dumps(cmd_arg) 125 126 # call hscli for episodic_snap 127 (cmd_out, cmd_err) = hsexecute(cmdarg_json) 128 129 # cmd_err should be None in case of successful execution of cmd 130 if not cmd_err: 131 processed_output = process_cmd_out(cmd_out) 132 out_meta = processed_output.get('payload') 133 else: 134 LOG.error("Error %s in processing episodic_snap", 135 cmd_err) 136 raise exception.UnableToExecuteHyperScaleCmd( 137 message=cmdarg_json) 138 except exception.UnableToExecuteHyperScaleCmd: 139 with excutils.save_and_reraise_exception(): 140 LOG.debug("Unable to execute episodic_snap", exc_info=True) 141 142 except exception.UnableToProcessHyperScaleCmdOutput: 143 with excutils.save_and_reraise_exception(): 144 LOG.debug("Unable to process episodic_snap output", 145 exc_info=True) 146 return out_meta 147 148 149def get_image_path(image_id, op_type='image'): 150 151 cmd_out = None 152 cmd_err = None 153 image_path = None 154 try: 155 cmd_arg = {} 156 if op_type == 'image': 157 cmd_arg['operation'] = 'get_image_path' 158 elif op_type == 'volume': 159 cmd_arg['operation'] = 'get_volume_path' 160 cmd_arg['image_id'] = image_id 161 # create a json for cmd argument 162 cmdarg_json = json.dumps(cmd_arg) 163 164 # call hscli for get_image_path 165 (cmd_out, cmd_err) = hsexecute(cmdarg_json) 166 167 # cmd_err should be None in case of successful execution of cmd 168 if not cmd_err: 169 processed_output = process_cmd_out(cmd_out) 170 image_path = processed_output.get('payload') 171 else: 172 LOG.error("Error %s in processing get_image_path", 173 cmd_err) 174 raise exception.UnableToExecuteHyperScaleCmd( 175 message=cmdarg_json) 176 except exception.UnableToExecuteHyperScaleCmd: 177 with excutils.save_and_reraise_exception(): 178 LOG.debug("Unable to execute get_image_path", exc_info=True) 179 180 except exception.UnableToProcessHyperScaleCmdOutput: 181 with excutils.save_and_reraise_exception(): 182 LOG.debug("Unable to process get_image_path output", 183 exc_info=True) 184 return image_path 185 186 187def update_image(image_path, volume_id, hs_img_id): 188 cmd_out = None 189 cmd_err = None 190 output = None 191 try: 192 cmd_arg = {} 193 cmd_arg['operation'] = 'update_image' 194 cmd_arg['image_path'] = image_path 195 cmd_arg['volume_id'] = volume_id 196 cmd_arg['hs_image_id'] = hs_img_id 197 # create a json for cmd argument 198 cmdarg_json = json.dumps(cmd_arg) 199 200 (cmd_out, cmd_err) = hsexecute(cmdarg_json) 201 202 # cmd_err should be None in case of successful execution of cmd 203 if not cmd_err: 204 output = process_cmd_out(cmd_out) 205 else: 206 LOG.error("Error %s in execution of update_image", 207 cmd_err) 208 raise exception.UnableToExecuteHyperScaleCmd( 209 message=cmdarg_json) 210 except exception.UnableToExecuteHyperScaleCmd: 211 with excutils.save_and_reraise_exception(): 212 LOG.debug("Unable to execute update_image", exc_info=True) 213 214 except exception.UnableToProcessHyperScaleCmdOutput: 215 with excutils.save_and_reraise_exception(): 216 LOG.debug("Unable to process update_image output", 217 exc_info=True) 218 return output 219 220 221def hsexecute(cmdarg_json): 222 223 cmd_out = None 224 cmd_err = None 225 try: 226 # call hyperscale cli 227 (cmd_out, cmd_err) = utils.execute("hscli", 228 cmdarg_json, 229 run_as_root=True) 230 except (putils.UnknownArgumentError, putils.ProcessExecutionError, 231 exception.ErrorInParsingArguments, OSError): 232 LOG.error("Exception in running the command for %s", 233 cmdarg_json, 234 exc_info=True) 235 raise exception.UnableToExecuteHyperScaleCmd(message=cmdarg_json) 236 237 except Exception: 238 LOG.error("Internal exception in cmd for %s", cmdarg_json, 239 exc_info=True) 240 raise exception.UnableToExecuteHyperScaleCmd(message=cmdarg_json) 241 242 return (cmd_out, cmd_err) 243 244 245def process_cmd_out(cmd_out): 246 """Process the cmd output.""" 247 248 output = None 249 250 try: 251 # get the python object from the cmd_out 252 output = json.loads(cmd_out) 253 error_code = output.get('err_code') 254 if error_code: 255 error_message = output.get('err_msg') 256 operation = output.get('token') 257 LOG.error("Failed to perform %(operation)s with error code" 258 " %(err_code)s, error message is %(err_msg)s", 259 {"operation": operation, 260 "err_code": error_code, 261 "err_msg": error_message}) 262 except ValueError: 263 raise exception.UnableToProcessHyperScaleCmdOutput(cmd_out=cmd_out) 264 265 return output 266 267 268def check_for_setup_error(): 269 return True 270 271 272def get_configuration(persona): 273 """Get required configuration from controller.""" 274 275 msg_body = {'persona': persona} 276 configuration = None 277 try: 278 cmd_out, cmd_error = message_controller( 279 constants.HS_CONTROLLER_EXCH, 280 'hyperscale.controller.get.configuration', 281 **msg_body) 282 LOG.debug("Response Message from Controller: %s", cmd_out) 283 payload = cmd_out.get('payload') 284 configuration = payload.get('config_data') 285 286 except (exception.ErrorInSendingMsg, 287 exception.UnableToExecuteHyperScaleCmd, 288 exception.UnableToProcessHyperScaleCmdOutput): 289 LOG.exception("Failed to get configuration from controller") 290 raise exception.ErrorInFetchingConfiguration(persona=persona) 291 292 return configuration 293 294 295def _send_message(exchange, routing_key, message_token, **kwargs): 296 """Send message to specified node.""" 297 298 cmd_out = None 299 cmd_err = None 300 processed_output = None 301 msg = None 302 try: 303 LOG.debug("Sending message: %s", message_token) 304 305 # Build message from kwargs 306 message_body = _populate_message_body(kwargs) 307 cmd_arg = {} 308 cmd_arg["operation"] = "message" 309 cmd_arg["msg_body"] = message_body 310 cmd_arg["msg_token"] = message_token 311 # exchange name 312 cmd_arg["exchange_name"] = exchange 313 # routing key 314 cmd_arg["routing_key"] = routing_key 315 # create a json for cmd argument 316 cmdarg_json = json.dumps(cmd_arg) 317 318 (cmd_out, cmd_err) = hsexecute(cmdarg_json) 319 320 # cmd_err should be none in case of successful execution of cmd 321 if cmd_err: 322 LOG.debug("Sending message failed. Error %s", cmd_err) 323 raise exception.ErrorInSendingMsg(cmd_err=cmd_err) 324 else: 325 processed_output = process_cmd_out(cmd_out) 326 327 except exception.UnableToExecuteHyperScaleCmd: 328 with excutils.save_and_reraise_exception(): 329 msg = ("Unable to execute HyperScale command for %(cmd)s" 330 " to exchange %(exch)s with key %(rt_key)s") 331 LOG.debug(msg, {"cmd": message_token, 332 "exch": exchange, 333 "rt_key": routing_key}, 334 exc_info=True) 335 336 except exception.UnableToProcessHyperScaleCmdOutput: 337 with excutils.save_and_reraise_exception(): 338 msg = ("Unable to process msg %(message)s" 339 " to exchange %(exch)s with key %(rt_key)s") 340 LOG.debug(msg, {"message": message_token, 341 "exch": exchange, 342 "rt_key": routing_key}) 343 344 return (processed_output, cmd_err) 345 346 347def message_compute_plane(routing_key, message_token, **kwargs): 348 """Send message to compute plane.""" 349 350 LOG.debug("Sending message to compute plane") 351 352 return _send_message(constants.HS_COMPUTE_EXCH, 353 routing_key, 354 message_token, 355 **kwargs) 356 357 358def message_data_plane(routing_key, message_token, **kwargs): 359 """Send message to data node.""" 360 361 LOG.debug("Sending message to data plane") 362 363 return _send_message(constants.HS_DATANODE_EXCH, 364 routing_key, 365 message_token, 366 **kwargs) 367 368 369def message_controller(routing_key, message_token, **kwargs): 370 """Send message to controller.""" 371 372 LOG.debug("Sending message to controller") 373 374 return _send_message(constants.HS_CONTROLLER_EXCH, 375 routing_key, 376 message_token, 377 **kwargs) 378