pystorcli.drive
StorCLI drive python module
1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2018, Martin Dojcak <martin@dojcak.sk> 4# Copyright (c) 2022, Rafael Leira & Naudit HPCN S.L. <rafael.leira@naudit.es> 5# See LICENSE for details. 6 7'''StorCLI drive python module 8''' 9 10from . import StorCLI 11from . import common 12from . import controller 13from . import enclosure 14from . import exc 15 16 17class DriveMetrics(object): 18 """StorCLI DriveMerics 19 20 Instance of this class represents drive metrics 21 22 Args: 23 drive (:obj:Drive): drive object 24 25 Properties: 26 state (str): drive state 27 shield_errors (str): number of shield errors on drive 28 media_errors (str): number of media errors on drive 29 predictive_failure (str): predictive failure on drive 30 temperature (str): temperature of drive in celsius 31 smart_alert (str): S.M.A.R.T alert flag on drive 32 init_progress (str): % progress of initialization on a drive 33 rebuild_progress (str): % progress of rebuild on a drive 34 erase_progress (str): % progress of erase on a drive 35 all (dict): all metrics 36 """ 37 38 def __init__(self, drive): 39 """Constructor - create StorCLI DriveMetrics object 40 41 Args: 42 cv (:obj:CacheVault): cachevault object 43 """ 44 self._drive = drive 45 46 @property 47 def _show_all(self): 48 args = [ 49 'show', 50 'all' 51 ] 52 return common.response_data(self._drive._run(args)) 53 54 @property 55 def _resposne_state(self): 56 key_prefix = 'Drive /c{0}/e{1}/s{2}'.format( 57 self._drive.ctl_id, 58 self._drive.encl_id, 59 self._drive.id 60 ) 61 detailed_info = self._show_all['{0} - Detailed Information'.format( 62 key_prefix)] 63 detailed_state = detailed_info['{0} State'.format(key_prefix)] 64 return detailed_state 65 66 @property 67 def state(self): 68 """drive state 69 70 Returns 71 (str): 72 dhs - dedicated hotspare to some virtual drive 73 ghs - global hotspare 74 bad - bad drive 75 good - unconfigured good 76 online - already in virtual drive with good state 77 offline - already in virtual drive with bad state 78 """ 79 return self._drive.state 80 81 @property 82 @common.stringify 83 def shield_errors(self): 84 """(str): number of shield errors on drive 85 """ 86 return self._resposne_state['Shield Counter'] 87 88 @property 89 @common.stringify 90 def media_errors(self): 91 """(str): number of media errors on drive 92 """ 93 return self._resposne_state['Media Error Count'] 94 95 @property 96 @common.stringify 97 def other_errors(self): 98 """(str): number of other errors on drive 99 """ 100 return self._resposne_state['Other Error Count'] 101 102 @property 103 @common.stringify 104 def predictive_failure(self): 105 """predictive failure on drive 106 """ 107 return self._resposne_state['Predictive Failure Count'] 108 109 @property 110 def temperature(self): 111 """temperature of drive in celsius 112 """ 113 return self._resposne_state['Drive Temperature'].split('C')[0].lstrip() 114 115 @property 116 @common.lower 117 def smart_alert(self): 118 """(str): S.M.A.R.T alert flag on drive 119 """ 120 return self._resposne_state['S.M.A.R.T alert flagged by drive'] 121 122 @property 123 @common.stringify 124 def init_progress(self): 125 """Show initialization progress in percentage 126 127 Returns: 128 (str): progress in percentage 129 """ 130 args = [ 131 'show', 132 'initialization' 133 ] 134 135 progress = common.response_data(self._drive._run(args))[0]['Progress%'] 136 if progress == '-': 137 return "100" 138 return progress 139 140 @property 141 @common.stringify 142 def rebuild_progress(self): 143 """Show rebuild progress in percentage 144 145 Returns: 146 (str): rebuild in percentage 147 """ 148 args = [ 149 'show', 150 'rebuild' 151 ] 152 153 progress = common.response_data(self._drive._run(args))[0]['Progress%'] 154 if progress == '-': 155 return "100" 156 return progress 157 158 @property 159 @common.stringify 160 def erase_progress(self): 161 """Show drive erase progress in percentage 162 163 Returns: 164 (str): progress in percentage 165 """ 166 args = [ 167 'show', 168 'erase' 169 ] 170 171 progress = common.response_data(self._drive._run(args))[0]['Progress%'] 172 if progress == '-': 173 return "100" 174 return progress 175 176 @property 177 def all(self): 178 """(:obj:DriveMetrics): all metrics 179 """ 180 metrics = {} 181 182 for attribute in dir(self): 183 if not attribute.startswith('_') and not attribute == 'all': 184 metrics[attribute] = self.__getattribute__(attribute) 185 return metrics 186 187 188class Drive(object): 189 """StorCLI Drive 190 191 Instance of this class represents drive in StorCLI hierarchy 192 193 Args: 194 ctl_id (str): controller id 195 encl_id (str): enclosure id 196 slot_id (str): slot id 197 binary (str): storcli binary or full path to the binary 198 199 Properties: 200 id (str): drive id 201 name (str): drive cmd name 202 facts (dict): raw drive facts 203 metrics (dict): drive metrics for monitoring 204 size (str): drive size 205 interface (str): SATA / SAS 206 medium (str): SSD / HDD 207 model (str): drive model informations 208 serial (str): drive serial number 209 wwn (str): drive wwn 210 firmware (str): drive firmware version 211 device_speed (str): drive speed 212 linke_speed (str): drive connection link speed 213 ctl_id (str): drive controller id 214 ctl (:obj:controller.Controller): drive controller 215 encl_id (str): drive enclosure 216 encl (:obj:enclosure.Enclosure): drive enclosure 217 phyerrorcounters (dict): drive error counters (also setter) 218 state (str): drive state (also setter) 219 spin (str): drive spin state (also setter) 220 221 222 Methods: 223 init_start (dict): starts the initialization process on a drive 224 init_stop (dict): stops an initialization process running on a drive 225 init_running (bool): check if initialization is running on a drive 226 erase_start (dict): securely erases non-SED drive 227 erase_stop (dict): stops an erase process running on a drive 228 erase_running (bool): check if erase is running on a drive 229 hotparedrive_create (dict): add drive to hotspares 230 hotparedrive_delete (dict): delete drive from hotspare 231 232 TODO: 233 Implement missing methods: 234 * start rebuild 235 * stop rebuild 236 * pause rebuild 237 * resume rebuild 238 * rebuild running 239 """ 240 241 def __init__(self, ctl_id, encl_id, slot_id, binary='storcli64'): 242 """Constructor - create StorCLI Drive object 243 244 Args: 245 ctl_id (str): controller id 246 encl_id (str): enclosure id 247 slot_id (str): slot id 248 binary (str): storcli binary or full path to the binary 249 """ 250 self._ctl_id = ctl_id 251 self._encl_id = encl_id 252 self._slot_id = slot_id 253 self._binary = binary 254 self._storcli = StorCLI(binary) 255 self._name = '/c{0}/e{1}/s{2}'.format(self._ctl_id, 256 self._encl_id, self._slot_id) 257 258 self._exist() 259 260 @staticmethod 261 def _response_properties(out): 262 return common.response_data(out)['Drive Information'][0] 263 264 def _response_attributes(self, out): 265 detailed_info = ('Drive /c{0}/e{1}/s{2}' 266 ' - Detailed Information'.format(self._ctl_id, self._encl_id, self._slot_id)) 267 attr = 'Drive /c{0}/e{1}/s{2} Device attributes'.format( 268 self._ctl_id, self._encl_id, self._slot_id) 269 return common.response_data(out)[detailed_info][attr] 270 271 def _run(self, args, **kwargs): 272 args = args[:] 273 args.insert(0, self._name) 274 return self._storcli.run(args, **kwargs) 275 276 def _exist(self): 277 try: 278 self._run(['show']) 279 except exc.StorCliCmdError: 280 raise exc.StorCliMissingError( 281 self.__class__.__name__, self._name) from None 282 283 @property 284 def id(self): 285 """(str): drive id 286 """ 287 return self._slot_id 288 289 @property 290 def name(self): 291 """(str): drive cmd name 292 """ 293 return self._name 294 295 @property 296 def facts(self): 297 """(dict): raw drive facts 298 """ 299 args = [ 300 'show', 301 'all' 302 ] 303 return common.response_data(self._run(args)) 304 305 @property 306 def metrics(self): 307 """(dict): drive metrics 308 """ 309 return DriveMetrics(self) 310 311 @property 312 def size(self): 313 """(str): drive size 314 """ 315 args = [ 316 'show' 317 ] 318 return self._response_properties(self._run(args))['Size'] 319 320 @property 321 @common.lower 322 def interface(self): 323 """(str): SATA / SAS 324 """ 325 args = [ 326 'show' 327 ] 328 return self._response_properties(self._run(args))['Intf'] 329 330 @property 331 @common.lower 332 def medium(self): 333 """(str): SSD / HDD 334 """ 335 args = [ 336 'show' 337 ] 338 return self._response_properties(self._run(args))['Med'] 339 340 @property 341 @common.lower 342 def model(self): 343 """(str): drive model informations 344 """ 345 args = [ 346 'show' 347 ] 348 return self._response_properties(self._run(args))['Model'] 349 350 @property 351 def serial(self): 352 """(str): drive serial number 353 """ 354 args = [ 355 'show', 356 'all' 357 ] 358 return self._response_attributes(self._run(args))['SN'] 359 360 @property 361 @common.lower 362 def wwn(self): 363 """(str): drive wwn 364 """ 365 args = [ 366 'show', 367 'all' 368 ] 369 return self._response_attributes(self._run(args))['WWN'] 370 371 @property 372 @common.lower 373 def firmware(self): 374 """(str): drive firmware version 375 """ 376 args = [ 377 'show', 378 'all' 379 ] 380 return self._response_attributes(self._run(args))['Firmware Revision'] 381 382 @property 383 def device_speed(self): 384 """(str): drive speed 385 """ 386 args = [ 387 'show', 388 'all' 389 ] 390 return self._response_attributes(self._run(args))['Device Speed'] 391 392 @property 393 def link_speed(self): 394 """(str): drive connection link speed 395 """ 396 args = [ 397 'show', 398 'all' 399 ] 400 return self._response_attributes(self._run(args))['Link Speed'] 401 402 @property 403 def ctl_id(self): 404 """(str): drive controller id 405 """ 406 return self._ctl_id 407 408 @property 409 def ctl(self): 410 """(:obj:controller.Controller): drive controller 411 """ 412 return controller.Controller(ctl_id=self._ctl_id, binary=self._binary) 413 414 @property 415 def encl_id(self): 416 """(str): dirve enclosure id 417 """ 418 return self._encl_id 419 420 @property 421 def encl(self): 422 """(:obj:enclosure.Enclosure): drive enclosure 423 """ 424 return enclosure.Enclosure(ctl_id=self._ctl_id, encl_id=self._encl_id, binary=self._binary) 425 426 def init_start(self): 427 """Start initialization of a drive 428 429 Returns: 430 (dict): resposne cmd data 431 """ 432 args = [ 433 'start', 434 'initialization' 435 ] 436 return common.response_cmd(self._run(args)) 437 438 def init_stop(self): 439 """Stop initialization on a drive 440 441 A stopped initialization process cannot be resumed. 442 443 Returns: 444 (dict): resposne cmd data 445 """ 446 args = [ 447 'stop', 448 'initialization' 449 ] 450 return common.response_cmd(self._run(args)) 451 452 @property 453 def init_running(self): 454 """Check if initialization process is running on a drive 455 456 Returns: 457 (bool): true / false 458 """ 459 args = [ 460 'show', 461 'initialization' 462 ] 463 464 status = common.response_data(self._run(args))[0]['Status'] 465 return bool(status == 'In progress') 466 467 def erase_start(self, mode='simple'): 468 """Securely erases non-SED drives with specified erase pattern 469 470 Args: 471 mode (str): 472 simple - Single pass, single pattern write 473 normal - Three pass, three pattern write 474 thorough - Nine pass, repeats the normal write 3 times 475 standard - Applicable only for DFF's 476 threepass - Three pass, pass1 random pattern write, pass2,3 write zero, verify 477 crypto - Applicable only for ISE capable drives 478 PatternA|PatternB - an 8-Bit binary pattern to overwrite the data. 479 480 Returns: 481 (dict): resposne cmd data 482 """ 483 args = [ 484 'start', 485 'erase', 486 '{0}'.format(mode) 487 ] 488 return common.response_cmd(self._run(args)) 489 490 def erase_stop(self): 491 """Stops the erase operation of a drive 492 493 Returns: 494 (dict): resposne cmd data 495 """ 496 args = [ 497 'stop', 498 'erase' 499 ] 500 return common.response_cmd(self._run(args)) 501 502 @property 503 def erase_running(self): 504 """Check if erase process is running on a drive 505 506 Returns: 507 (bool): true / false 508 """ 509 args = [ 510 'show', 511 'erase' 512 ] 513 514 status = common.response_data(self._run(args))[0]['Status'] 515 return bool(status == 'In progress') 516 517 @property 518 def phyerrorcounters(self): 519 """Get/Reset the drive phyerrorcounters 520 521 Reset drive error counters with (str) 0 522 """ 523 args = [ 524 'show', 525 'phyerrorcounters' 526 ] 527 return common.response_data(self._run(args))[self._name] 528 529 @phyerrorcounters.setter 530 def phyerrorcounters_reset(self): 531 """ 532 """ 533 args = [ 534 'reset', 535 'phyerrorcounters' 536 ] 537 return common.response_cmd(self._run(args)) 538 539 @property 540 def state(self): 541 """Get/Set drive state 542 543 One of the following states can be set (str): 544 online - changes the drive state to online 545 offline - changes the drive state to offline 546 missing - marks a drive as missing 547 good - changes the drive state to unconfigured good 548 jbod - sets the drive state to JBOD 549 550 Returns: 551 (str): 552 dhs - dedicated hotspare to some virtual drive 553 ghs - global hotspare 554 bad - bad drive 555 good - unconfigured good 556 online - already in virtual drive with good state 557 offline - already in virtual drive with bad state 558 """ 559 args = [ 560 'show' 561 ] 562 563 state = self._response_properties(self._run(args))['State'] 564 if state == 'DHS': 565 return 'dhs' 566 elif state == 'UBad': 567 return 'bad' 568 elif state == 'Onln': 569 return 'online' 570 elif state == 'Offln': 571 return 'offline' 572 elif state == 'GHS': 573 return 'ghs' 574 return 'good' 575 576 @state.setter 577 def state(self, value): 578 """ 579 """ 580 # online | offline | missing | good 581 args = [ 582 'set', 583 '{0}'.format(value) 584 ] 585 return common.response_setter(self._run(args)) 586 587 @property 588 def spin(self): 589 """Get/Set drive spin status 590 591 One of the following states can be set (str): 592 up - spins up and set to unconfigured good 593 down - spins down an unconfigured drive and prepares it for removal 594 595 Returns: 596 (str): up / down 597 """ 598 args = [ 599 'show' 600 ] 601 602 spin = self._response_properties(self._run(args))['Sp'] 603 if spin == 'U': 604 return 'up' 605 return 'down' 606 607 @spin.setter 608 def spin(self, value): 609 """ 610 """ 611 if value == 'up': 612 spin = 'spinup' 613 elif value == 'down': 614 spin = 'spindown' 615 else: 616 spin = value 617 618 args = [ 619 '{0}'.format(spin) 620 ] 621 return common.response_cmd(self._run(args)) 622 623 def hotparedrive_create(self, dgs=None, enclaffinity=False, nonrevertible=False): 624 """Creates a hotspare drive 625 626 Args: 627 dgs (str): specifies the drive group to which the hotspare drive is dedicated (N|0,1,2...) 628 enclaffinity (bool): Specifies the enclosure to which the hotspare is associated with. 629 If this option is specified, affinity is set; if it is not specified, 630 there is no affinity.NOTE Affinity cannot be removed once it is set 631 for a hotspare drive. 632 nonrevertible (bool): sets the drive as a nonrevertible hotspare 633 634 Returns: 635 (dict): resposne cmd data 636 """ 637 args = [ 638 'add', 639 'hotsparedrive' 640 ] 641 642 if dgs: 643 args.append("dgs={0}".format(dgs)) 644 if enclaffinity: 645 args.append('enclaffinity') 646 if nonrevertible: 647 args.append('nonrevertible') 648 return common.response_cmd(self._run(args)) 649 650 def hotparedrive_delete(self): 651 """Deletes drive from hotspares 652 653 Returns: 654 (dict): resposne cmd data 655 """ 656 args = [ 657 'delete', 658 'hotsparedrive' 659 ] 660 return common.response_cmd(self._run(args))
18class DriveMetrics(object): 19 """StorCLI DriveMerics 20 21 Instance of this class represents drive metrics 22 23 Args: 24 drive (:obj:Drive): drive object 25 26 Properties: 27 state (str): drive state 28 shield_errors (str): number of shield errors on drive 29 media_errors (str): number of media errors on drive 30 predictive_failure (str): predictive failure on drive 31 temperature (str): temperature of drive in celsius 32 smart_alert (str): S.M.A.R.T alert flag on drive 33 init_progress (str): % progress of initialization on a drive 34 rebuild_progress (str): % progress of rebuild on a drive 35 erase_progress (str): % progress of erase on a drive 36 all (dict): all metrics 37 """ 38 39 def __init__(self, drive): 40 """Constructor - create StorCLI DriveMetrics object 41 42 Args: 43 cv (:obj:CacheVault): cachevault object 44 """ 45 self._drive = drive 46 47 @property 48 def _show_all(self): 49 args = [ 50 'show', 51 'all' 52 ] 53 return common.response_data(self._drive._run(args)) 54 55 @property 56 def _resposne_state(self): 57 key_prefix = 'Drive /c{0}/e{1}/s{2}'.format( 58 self._drive.ctl_id, 59 self._drive.encl_id, 60 self._drive.id 61 ) 62 detailed_info = self._show_all['{0} - Detailed Information'.format( 63 key_prefix)] 64 detailed_state = detailed_info['{0} State'.format(key_prefix)] 65 return detailed_state 66 67 @property 68 def state(self): 69 """drive state 70 71 Returns 72 (str): 73 dhs - dedicated hotspare to some virtual drive 74 ghs - global hotspare 75 bad - bad drive 76 good - unconfigured good 77 online - already in virtual drive with good state 78 offline - already in virtual drive with bad state 79 """ 80 return self._drive.state 81 82 @property 83 @common.stringify 84 def shield_errors(self): 85 """(str): number of shield errors on drive 86 """ 87 return self._resposne_state['Shield Counter'] 88 89 @property 90 @common.stringify 91 def media_errors(self): 92 """(str): number of media errors on drive 93 """ 94 return self._resposne_state['Media Error Count'] 95 96 @property 97 @common.stringify 98 def other_errors(self): 99 """(str): number of other errors on drive 100 """ 101 return self._resposne_state['Other Error Count'] 102 103 @property 104 @common.stringify 105 def predictive_failure(self): 106 """predictive failure on drive 107 """ 108 return self._resposne_state['Predictive Failure Count'] 109 110 @property 111 def temperature(self): 112 """temperature of drive in celsius 113 """ 114 return self._resposne_state['Drive Temperature'].split('C')[0].lstrip() 115 116 @property 117 @common.lower 118 def smart_alert(self): 119 """(str): S.M.A.R.T alert flag on drive 120 """ 121 return self._resposne_state['S.M.A.R.T alert flagged by drive'] 122 123 @property 124 @common.stringify 125 def init_progress(self): 126 """Show initialization progress in percentage 127 128 Returns: 129 (str): progress in percentage 130 """ 131 args = [ 132 'show', 133 'initialization' 134 ] 135 136 progress = common.response_data(self._drive._run(args))[0]['Progress%'] 137 if progress == '-': 138 return "100" 139 return progress 140 141 @property 142 @common.stringify 143 def rebuild_progress(self): 144 """Show rebuild progress in percentage 145 146 Returns: 147 (str): rebuild in percentage 148 """ 149 args = [ 150 'show', 151 'rebuild' 152 ] 153 154 progress = common.response_data(self._drive._run(args))[0]['Progress%'] 155 if progress == '-': 156 return "100" 157 return progress 158 159 @property 160 @common.stringify 161 def erase_progress(self): 162 """Show drive erase progress in percentage 163 164 Returns: 165 (str): progress in percentage 166 """ 167 args = [ 168 'show', 169 'erase' 170 ] 171 172 progress = common.response_data(self._drive._run(args))[0]['Progress%'] 173 if progress == '-': 174 return "100" 175 return progress 176 177 @property 178 def all(self): 179 """(:obj:DriveMetrics): all metrics 180 """ 181 metrics = {} 182 183 for attribute in dir(self): 184 if not attribute.startswith('_') and not attribute == 'all': 185 metrics[attribute] = self.__getattribute__(attribute) 186 return metrics
StorCLI DriveMerics
Instance of this class represents drive metrics
Args: drive (Drive): drive object
Properties: state (str): drive state shield_errors (str): number of shield errors on drive media_errors (str): number of media errors on drive predictive_failure (str): predictive failure on drive temperature (str): temperature of drive in celsius smart_alert (str): S.M.A.R.T alert flag on drive init_progress (str): % progress of initialization on a drive rebuild_progress (str): % progress of rebuild on a drive erase_progress (str): % progress of erase on a drive all (dict): all metrics
39 def __init__(self, drive): 40 """Constructor - create StorCLI DriveMetrics object 41 42 Args: 43 cv (:obj:CacheVault): cachevault object 44 """ 45 self._drive = drive
Constructor - create StorCLI DriveMetrics object
Args: cv (CacheVault): cachevault object
189class Drive(object): 190 """StorCLI Drive 191 192 Instance of this class represents drive in StorCLI hierarchy 193 194 Args: 195 ctl_id (str): controller id 196 encl_id (str): enclosure id 197 slot_id (str): slot id 198 binary (str): storcli binary or full path to the binary 199 200 Properties: 201 id (str): drive id 202 name (str): drive cmd name 203 facts (dict): raw drive facts 204 metrics (dict): drive metrics for monitoring 205 size (str): drive size 206 interface (str): SATA / SAS 207 medium (str): SSD / HDD 208 model (str): drive model informations 209 serial (str): drive serial number 210 wwn (str): drive wwn 211 firmware (str): drive firmware version 212 device_speed (str): drive speed 213 linke_speed (str): drive connection link speed 214 ctl_id (str): drive controller id 215 ctl (:obj:controller.Controller): drive controller 216 encl_id (str): drive enclosure 217 encl (:obj:enclosure.Enclosure): drive enclosure 218 phyerrorcounters (dict): drive error counters (also setter) 219 state (str): drive state (also setter) 220 spin (str): drive spin state (also setter) 221 222 223 Methods: 224 init_start (dict): starts the initialization process on a drive 225 init_stop (dict): stops an initialization process running on a drive 226 init_running (bool): check if initialization is running on a drive 227 erase_start (dict): securely erases non-SED drive 228 erase_stop (dict): stops an erase process running on a drive 229 erase_running (bool): check if erase is running on a drive 230 hotparedrive_create (dict): add drive to hotspares 231 hotparedrive_delete (dict): delete drive from hotspare 232 233 TODO: 234 Implement missing methods: 235 * start rebuild 236 * stop rebuild 237 * pause rebuild 238 * resume rebuild 239 * rebuild running 240 """ 241 242 def __init__(self, ctl_id, encl_id, slot_id, binary='storcli64'): 243 """Constructor - create StorCLI Drive object 244 245 Args: 246 ctl_id (str): controller id 247 encl_id (str): enclosure id 248 slot_id (str): slot id 249 binary (str): storcli binary or full path to the binary 250 """ 251 self._ctl_id = ctl_id 252 self._encl_id = encl_id 253 self._slot_id = slot_id 254 self._binary = binary 255 self._storcli = StorCLI(binary) 256 self._name = '/c{0}/e{1}/s{2}'.format(self._ctl_id, 257 self._encl_id, self._slot_id) 258 259 self._exist() 260 261 @staticmethod 262 def _response_properties(out): 263 return common.response_data(out)['Drive Information'][0] 264 265 def _response_attributes(self, out): 266 detailed_info = ('Drive /c{0}/e{1}/s{2}' 267 ' - Detailed Information'.format(self._ctl_id, self._encl_id, self._slot_id)) 268 attr = 'Drive /c{0}/e{1}/s{2} Device attributes'.format( 269 self._ctl_id, self._encl_id, self._slot_id) 270 return common.response_data(out)[detailed_info][attr] 271 272 def _run(self, args, **kwargs): 273 args = args[:] 274 args.insert(0, self._name) 275 return self._storcli.run(args, **kwargs) 276 277 def _exist(self): 278 try: 279 self._run(['show']) 280 except exc.StorCliCmdError: 281 raise exc.StorCliMissingError( 282 self.__class__.__name__, self._name) from None 283 284 @property 285 def id(self): 286 """(str): drive id 287 """ 288 return self._slot_id 289 290 @property 291 def name(self): 292 """(str): drive cmd name 293 """ 294 return self._name 295 296 @property 297 def facts(self): 298 """(dict): raw drive facts 299 """ 300 args = [ 301 'show', 302 'all' 303 ] 304 return common.response_data(self._run(args)) 305 306 @property 307 def metrics(self): 308 """(dict): drive metrics 309 """ 310 return DriveMetrics(self) 311 312 @property 313 def size(self): 314 """(str): drive size 315 """ 316 args = [ 317 'show' 318 ] 319 return self._response_properties(self._run(args))['Size'] 320 321 @property 322 @common.lower 323 def interface(self): 324 """(str): SATA / SAS 325 """ 326 args = [ 327 'show' 328 ] 329 return self._response_properties(self._run(args))['Intf'] 330 331 @property 332 @common.lower 333 def medium(self): 334 """(str): SSD / HDD 335 """ 336 args = [ 337 'show' 338 ] 339 return self._response_properties(self._run(args))['Med'] 340 341 @property 342 @common.lower 343 def model(self): 344 """(str): drive model informations 345 """ 346 args = [ 347 'show' 348 ] 349 return self._response_properties(self._run(args))['Model'] 350 351 @property 352 def serial(self): 353 """(str): drive serial number 354 """ 355 args = [ 356 'show', 357 'all' 358 ] 359 return self._response_attributes(self._run(args))['SN'] 360 361 @property 362 @common.lower 363 def wwn(self): 364 """(str): drive wwn 365 """ 366 args = [ 367 'show', 368 'all' 369 ] 370 return self._response_attributes(self._run(args))['WWN'] 371 372 @property 373 @common.lower 374 def firmware(self): 375 """(str): drive firmware version 376 """ 377 args = [ 378 'show', 379 'all' 380 ] 381 return self._response_attributes(self._run(args))['Firmware Revision'] 382 383 @property 384 def device_speed(self): 385 """(str): drive speed 386 """ 387 args = [ 388 'show', 389 'all' 390 ] 391 return self._response_attributes(self._run(args))['Device Speed'] 392 393 @property 394 def link_speed(self): 395 """(str): drive connection link speed 396 """ 397 args = [ 398 'show', 399 'all' 400 ] 401 return self._response_attributes(self._run(args))['Link Speed'] 402 403 @property 404 def ctl_id(self): 405 """(str): drive controller id 406 """ 407 return self._ctl_id 408 409 @property 410 def ctl(self): 411 """(:obj:controller.Controller): drive controller 412 """ 413 return controller.Controller(ctl_id=self._ctl_id, binary=self._binary) 414 415 @property 416 def encl_id(self): 417 """(str): dirve enclosure id 418 """ 419 return self._encl_id 420 421 @property 422 def encl(self): 423 """(:obj:enclosure.Enclosure): drive enclosure 424 """ 425 return enclosure.Enclosure(ctl_id=self._ctl_id, encl_id=self._encl_id, binary=self._binary) 426 427 def init_start(self): 428 """Start initialization of a drive 429 430 Returns: 431 (dict): resposne cmd data 432 """ 433 args = [ 434 'start', 435 'initialization' 436 ] 437 return common.response_cmd(self._run(args)) 438 439 def init_stop(self): 440 """Stop initialization on a drive 441 442 A stopped initialization process cannot be resumed. 443 444 Returns: 445 (dict): resposne cmd data 446 """ 447 args = [ 448 'stop', 449 'initialization' 450 ] 451 return common.response_cmd(self._run(args)) 452 453 @property 454 def init_running(self): 455 """Check if initialization process is running on a drive 456 457 Returns: 458 (bool): true / false 459 """ 460 args = [ 461 'show', 462 'initialization' 463 ] 464 465 status = common.response_data(self._run(args))[0]['Status'] 466 return bool(status == 'In progress') 467 468 def erase_start(self, mode='simple'): 469 """Securely erases non-SED drives with specified erase pattern 470 471 Args: 472 mode (str): 473 simple - Single pass, single pattern write 474 normal - Three pass, three pattern write 475 thorough - Nine pass, repeats the normal write 3 times 476 standard - Applicable only for DFF's 477 threepass - Three pass, pass1 random pattern write, pass2,3 write zero, verify 478 crypto - Applicable only for ISE capable drives 479 PatternA|PatternB - an 8-Bit binary pattern to overwrite the data. 480 481 Returns: 482 (dict): resposne cmd data 483 """ 484 args = [ 485 'start', 486 'erase', 487 '{0}'.format(mode) 488 ] 489 return common.response_cmd(self._run(args)) 490 491 def erase_stop(self): 492 """Stops the erase operation of a drive 493 494 Returns: 495 (dict): resposne cmd data 496 """ 497 args = [ 498 'stop', 499 'erase' 500 ] 501 return common.response_cmd(self._run(args)) 502 503 @property 504 def erase_running(self): 505 """Check if erase process is running on a drive 506 507 Returns: 508 (bool): true / false 509 """ 510 args = [ 511 'show', 512 'erase' 513 ] 514 515 status = common.response_data(self._run(args))[0]['Status'] 516 return bool(status == 'In progress') 517 518 @property 519 def phyerrorcounters(self): 520 """Get/Reset the drive phyerrorcounters 521 522 Reset drive error counters with (str) 0 523 """ 524 args = [ 525 'show', 526 'phyerrorcounters' 527 ] 528 return common.response_data(self._run(args))[self._name] 529 530 @phyerrorcounters.setter 531 def phyerrorcounters_reset(self): 532 """ 533 """ 534 args = [ 535 'reset', 536 'phyerrorcounters' 537 ] 538 return common.response_cmd(self._run(args)) 539 540 @property 541 def state(self): 542 """Get/Set drive state 543 544 One of the following states can be set (str): 545 online - changes the drive state to online 546 offline - changes the drive state to offline 547 missing - marks a drive as missing 548 good - changes the drive state to unconfigured good 549 jbod - sets the drive state to JBOD 550 551 Returns: 552 (str): 553 dhs - dedicated hotspare to some virtual drive 554 ghs - global hotspare 555 bad - bad drive 556 good - unconfigured good 557 online - already in virtual drive with good state 558 offline - already in virtual drive with bad state 559 """ 560 args = [ 561 'show' 562 ] 563 564 state = self._response_properties(self._run(args))['State'] 565 if state == 'DHS': 566 return 'dhs' 567 elif state == 'UBad': 568 return 'bad' 569 elif state == 'Onln': 570 return 'online' 571 elif state == 'Offln': 572 return 'offline' 573 elif state == 'GHS': 574 return 'ghs' 575 return 'good' 576 577 @state.setter 578 def state(self, value): 579 """ 580 """ 581 # online | offline | missing | good 582 args = [ 583 'set', 584 '{0}'.format(value) 585 ] 586 return common.response_setter(self._run(args)) 587 588 @property 589 def spin(self): 590 """Get/Set drive spin status 591 592 One of the following states can be set (str): 593 up - spins up and set to unconfigured good 594 down - spins down an unconfigured drive and prepares it for removal 595 596 Returns: 597 (str): up / down 598 """ 599 args = [ 600 'show' 601 ] 602 603 spin = self._response_properties(self._run(args))['Sp'] 604 if spin == 'U': 605 return 'up' 606 return 'down' 607 608 @spin.setter 609 def spin(self, value): 610 """ 611 """ 612 if value == 'up': 613 spin = 'spinup' 614 elif value == 'down': 615 spin = 'spindown' 616 else: 617 spin = value 618 619 args = [ 620 '{0}'.format(spin) 621 ] 622 return common.response_cmd(self._run(args)) 623 624 def hotparedrive_create(self, dgs=None, enclaffinity=False, nonrevertible=False): 625 """Creates a hotspare drive 626 627 Args: 628 dgs (str): specifies the drive group to which the hotspare drive is dedicated (N|0,1,2...) 629 enclaffinity (bool): Specifies the enclosure to which the hotspare is associated with. 630 If this option is specified, affinity is set; if it is not specified, 631 there is no affinity.NOTE Affinity cannot be removed once it is set 632 for a hotspare drive. 633 nonrevertible (bool): sets the drive as a nonrevertible hotspare 634 635 Returns: 636 (dict): resposne cmd data 637 """ 638 args = [ 639 'add', 640 'hotsparedrive' 641 ] 642 643 if dgs: 644 args.append("dgs={0}".format(dgs)) 645 if enclaffinity: 646 args.append('enclaffinity') 647 if nonrevertible: 648 args.append('nonrevertible') 649 return common.response_cmd(self._run(args)) 650 651 def hotparedrive_delete(self): 652 """Deletes drive from hotspares 653 654 Returns: 655 (dict): resposne cmd data 656 """ 657 args = [ 658 'delete', 659 'hotsparedrive' 660 ] 661 return common.response_cmd(self._run(args))
StorCLI Drive
Instance of this class represents drive in StorCLI hierarchy
Args: ctl_id (str): controller id encl_id (str): enclosure id slot_id (str): slot id binary (str): storcli binary or full path to the binary
Properties: id (str): drive id name (str): drive cmd name facts (dict): raw drive facts metrics (dict): drive metrics for monitoring size (str): drive size interface (str): SATA / SAS medium (str): SSD / HDD model (str): drive model informations serial (str): drive serial number wwn (str): drive wwn firmware (str): drive firmware version device_speed (str): drive speed linke_speed (str): drive connection link speed ctl_id (str): drive controller id ctl (controller.Controller): drive controller encl_id (str): drive enclosure encl (enclosure.Enclosure): drive enclosure phyerrorcounters (dict): drive error counters (also setter) state (str): drive state (also setter) spin (str): drive spin state (also setter)
Methods: init_start (dict): starts the initialization process on a drive init_stop (dict): stops an initialization process running on a drive init_running (bool): check if initialization is running on a drive erase_start (dict): securely erases non-SED drive erase_stop (dict): stops an erase process running on a drive erase_running (bool): check if erase is running on a drive hotparedrive_create (dict): add drive to hotspares hotparedrive_delete (dict): delete drive from hotspare
TODO: Implement missing methods: * start rebuild * stop rebuild * pause rebuild * resume rebuild * rebuild running
242 def __init__(self, ctl_id, encl_id, slot_id, binary='storcli64'): 243 """Constructor - create StorCLI Drive object 244 245 Args: 246 ctl_id (str): controller id 247 encl_id (str): enclosure id 248 slot_id (str): slot id 249 binary (str): storcli binary or full path to the binary 250 """ 251 self._ctl_id = ctl_id 252 self._encl_id = encl_id 253 self._slot_id = slot_id 254 self._binary = binary 255 self._storcli = StorCLI(binary) 256 self._name = '/c{0}/e{1}/s{2}'.format(self._ctl_id, 257 self._encl_id, self._slot_id) 258 259 self._exist()
Constructor - create StorCLI Drive object
Args: ctl_id (str): controller id encl_id (str): enclosure id slot_id (str): slot id binary (str): storcli binary or full path to the binary
427 def init_start(self): 428 """Start initialization of a drive 429 430 Returns: 431 (dict): resposne cmd data 432 """ 433 args = [ 434 'start', 435 'initialization' 436 ] 437 return common.response_cmd(self._run(args))
Start initialization of a drive
Returns: (dict): resposne cmd data
439 def init_stop(self): 440 """Stop initialization on a drive 441 442 A stopped initialization process cannot be resumed. 443 444 Returns: 445 (dict): resposne cmd data 446 """ 447 args = [ 448 'stop', 449 'initialization' 450 ] 451 return common.response_cmd(self._run(args))
Stop initialization on a drive
A stopped initialization process cannot be resumed.
Returns: (dict): resposne cmd data
468 def erase_start(self, mode='simple'): 469 """Securely erases non-SED drives with specified erase pattern 470 471 Args: 472 mode (str): 473 simple - Single pass, single pattern write 474 normal - Three pass, three pattern write 475 thorough - Nine pass, repeats the normal write 3 times 476 standard - Applicable only for DFF's 477 threepass - Three pass, pass1 random pattern write, pass2,3 write zero, verify 478 crypto - Applicable only for ISE capable drives 479 PatternA|PatternB - an 8-Bit binary pattern to overwrite the data. 480 481 Returns: 482 (dict): resposne cmd data 483 """ 484 args = [ 485 'start', 486 'erase', 487 '{0}'.format(mode) 488 ] 489 return common.response_cmd(self._run(args))
Securely erases non-SED drives with specified erase pattern
Args: mode (str): simple - Single pass, single pattern write normal - Three pass, three pattern write thorough - Nine pass, repeats the normal write 3 times standard - Applicable only for DFF's threepass - Three pass, pass1 random pattern write, pass2,3 write zero, verify crypto - Applicable only for ISE capable drives PatternA|PatternB - an 8-Bit binary pattern to overwrite the data.
Returns: (dict): resposne cmd data
491 def erase_stop(self): 492 """Stops the erase operation of a drive 493 494 Returns: 495 (dict): resposne cmd data 496 """ 497 args = [ 498 'stop', 499 'erase' 500 ] 501 return common.response_cmd(self._run(args))
Stops the erase operation of a drive
Returns: (dict): resposne cmd data
Get/Set drive state
One of the following states can be set (str): online - changes the drive state to online offline - changes the drive state to offline missing - marks a drive as missing good - changes the drive state to unconfigured good jbod - sets the drive state to JBOD
Returns: (str): dhs - dedicated hotspare to some virtual drive ghs - global hotspare bad - bad drive good - unconfigured good online - already in virtual drive with good state offline - already in virtual drive with bad state
Get/Set drive spin status
One of the following states can be set (str): up - spins up and set to unconfigured good down - spins down an unconfigured drive and prepares it for removal
Returns: (str): up / down
624 def hotparedrive_create(self, dgs=None, enclaffinity=False, nonrevertible=False): 625 """Creates a hotspare drive 626 627 Args: 628 dgs (str): specifies the drive group to which the hotspare drive is dedicated (N|0,1,2...) 629 enclaffinity (bool): Specifies the enclosure to which the hotspare is associated with. 630 If this option is specified, affinity is set; if it is not specified, 631 there is no affinity.NOTE Affinity cannot be removed once it is set 632 for a hotspare drive. 633 nonrevertible (bool): sets the drive as a nonrevertible hotspare 634 635 Returns: 636 (dict): resposne cmd data 637 """ 638 args = [ 639 'add', 640 'hotsparedrive' 641 ] 642 643 if dgs: 644 args.append("dgs={0}".format(dgs)) 645 if enclaffinity: 646 args.append('enclaffinity') 647 if nonrevertible: 648 args.append('nonrevertible') 649 return common.response_cmd(self._run(args))
Creates a hotspare drive
Args: dgs (str): specifies the drive group to which the hotspare drive is dedicated (N|0,1,2...) enclaffinity (bool): Specifies the enclosure to which the hotspare is associated with. If this option is specified, affinity is set; if it is not specified, there is no affinity.NOTE Affinity cannot be removed once it is set for a hotspare drive. nonrevertible (bool): sets the drive as a nonrevertible hotspare
Returns: (dict): resposne cmd data
651 def hotparedrive_delete(self): 652 """Deletes drive from hotspares 653 654 Returns: 655 (dict): resposne cmd data 656 """ 657 args = [ 658 'delete', 659 'hotsparedrive' 660 ] 661 return common.response_cmd(self._run(args))
Deletes drive from hotspares
Returns: (dict): resposne cmd data