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))
class DriveMetrics:
 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

DriveMetrics(drive)
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

state

drive state

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

shield_errors

func effective wrapper

media_errors

func effective wrapper

other_errors

func effective wrapper

predictive_failure

func effective wrapper

temperature

temperature of drive in celsius

smart_alert

func effective wrapper

init_progress

func effective wrapper

rebuild_progress

func effective wrapper

erase_progress

func effective wrapper

all

(DriveMetrics): all metrics

class Drive:
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

Drive(ctl_id, encl_id, slot_id, binary='storcli64')
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

id

(str): drive id

name

(str): drive cmd name

facts

(dict): raw drive facts

metrics

(dict): drive metrics

size

(str): drive size

interface

func effective wrapper

medium

func effective wrapper

model

func effective wrapper

serial

(str): drive serial number

wwn

func effective wrapper

firmware

func effective wrapper

device_speed

(str): drive speed

ctl_id

(str): drive controller id

ctl

(controller.Controller): drive controller

encl_id

(str): dirve enclosure id

encl

(enclosure.Enclosure): drive enclosure

def init_start(self)
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

def init_stop(self)
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

init_running

Check if initialization process is running on a drive

Returns: (bool): true / false

def erase_start(self, mode='simple')
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

def erase_stop(self)
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

erase_running

Check if erase process is running on a drive

Returns: (bool): true / false

phyerrorcounters

Get/Reset the drive phyerrorcounters

Reset drive error counters with (str) 0

phyerrorcounters_reset

Get/Reset the drive phyerrorcounters

Reset drive error counters with (str) 0

state

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

spin

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

def hotparedrive_create(self, dgs=None, enclaffinity=False, nonrevertible=False)
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

def hotparedrive_delete(self)
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