pystorcli.controller

StorCLI controller 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 controller python module
  8'''
  9
 10from . import StorCLI
 11from . import common
 12from . import enclosure
 13from . import virtualdrive
 14from . import exc
 15
 16from typing import List
 17
 18
 19class ControllerMetrics(object):
 20    """StorCLI Controller Metrics
 21
 22    Instance of this class represents controller metrics
 23    """
 24
 25    def __init__(self, ctl):
 26        """Constructor - create StorCLI ControllerMetrics object
 27
 28        Args:
 29            ctl (:obj:Controller): controller object
 30
 31        Properties:
 32            state (str): controller status
 33            memory_correctable_error (str): number of controllers memory correctable errors
 34            memory_uncorrectable_error (str): number of controllers memory uncorrectable errors
 35            drive_groups (str): number of drive groups on controller
 36            virtual_drives (str): number of virtual drives on controller
 37            virtual_drives_non_optimal (dict): number of virtual drives with in non optimal state
 38            physical_drives (str): number of physical drives on controller
 39            physical_drives_non_optimal (dict): number of physical drives in non optimal state
 40            roc_temperature (str): RAID-on-Chip temperature
 41            ctl_temperature (str): controller temperature
 42            all (dict): all metrics
 43        """
 44        self._ctl = ctl
 45
 46    @property
 47    def _show_all(self):
 48        args = [
 49            'show',
 50            'all',
 51        ]
 52        return common.response_data(self._ctl._run(args))
 53
 54    @property
 55    def _status(self):
 56        return self._show_all['Status']
 57
 58    @property
 59    def _hwcfg(self):
 60        return self._show_all['HwCfg']
 61
 62    @property
 63    @common.stringify
 64    @common.lower
 65    def state(self):
 66        """(str): controller status (Needs Attention | Optimal | Failed | Unknown)
 67        """
 68        return self._status["Controller Status"]
 69
 70    @property
 71    @common.stringify
 72    def memory_correctable_error(self):
 73        """(str): number of controllers memory correctable errors
 74        """
 75        return self._status["Memory Correctable Errors"]
 76
 77    @property
 78    @common.stringify
 79    def memory_uncorrectable_error(self):
 80        """(str): number of controllers memory uncorrectable errors
 81        """
 82        return self._status["Memory Uncorrectable Errors"]
 83
 84    @property
 85    @common.stringify
 86    def drive_groups(self):
 87        """(str): number of drive groups on controller
 88        """
 89        data = self._show_all
 90        if 'Drive Groups' in data:
 91            return data["Drive Groups"]
 92        return 0
 93
 94    @property
 95    @common.stringify
 96    def virtual_drives(self):
 97        """(str): number of virtual drives on controller
 98        """
 99        data = self._show_all
100        if 'Virtual Drives' in data:
101            return data["Virtual Drives"]
102        return 0
103
104    @property
105    def virtual_drives_non_optimal(self):
106        """(dict): number of virtual drives with in non optimal state
107        """
108        vds = {}
109
110        if not self._ctl.vds.has_vds:
111            return vds
112
113        for vd in self._ctl.vds:
114            if not vd.state == 'optimal':
115                if vd.state in vds:
116                    vds[vd.state] += 1
117                else:
118                    vds[vd.state] = 1
119
120        # convert counter to string
121        return {k: str(v) for k, v in vds.items()}
122
123    @property
124    @common.stringify
125    def physical_drives(self):
126        """(str): number of physical drives on controller
127        """
128        data = self._show_all
129
130        if 'Physical Drives' in data:
131            return data["Physical Drives"]
132        return 0
133
134    @property
135    def physical_drives_non_optimal(self):
136        """(dict): number of physical drives in non optimal state (UBad | Offln)
137        """
138        drives = {}
139
140        for encl in self._ctl.encls:
141            if not encl.has_drives:
142                continue
143            for drive in encl.drives:
144                if not drive.state in ('good', 'online', 'ghs', 'dhs'):
145                    if drive.state in drives:
146                        drives[drive.state] += 1
147                    else:
148                        drives[drive.state] = 1
149
150        # convert counter to string
151        return {k: str(v) for k, v in drives.items()}
152
153    @property
154    @common.stringify
155    def roc_temperature(self):
156        """(str): RAID-on-Chip temperature or unknown if absent
157        """
158        hwcfg = self._hwcfg
159        if hwcfg['Temperature Sensor for ROC'] == 'Present':
160            return hwcfg['ROC temperature(Degree Celsius)']
161        return 'unknown'
162
163    @property
164    @common.stringify
165    def ctl_temperature(self):
166        """(str): Controller temperature or unknown if absent
167        """
168        hwcfg = self._hwcfg
169
170        if hwcfg['Temperature Sensor for Controller'] == 'Present':
171            return hwcfg['Controller temperature(Degree Celsius)']
172        return 'unknown'
173
174    @property
175    def all(self):
176        """(dict): all metrics
177        """
178        metrics = {}
179
180        for attribute in dir(self):
181            if not attribute.startswith('_') and not attribute == 'all':
182                metrics[attribute] = self.__getattribute__(attribute)
183        return metrics
184
185
186class Controller(object):
187    """StorCLI Controller
188
189    Instance of this class represents controller in StorCLI hierarchy
190
191    Args:
192        ctl_id (str): controller id
193        binary (str): storcli binary or full path to the binary
194
195    Properties:
196        id (str): controller id
197        name (str): controller cmd name
198        facts (dict): raw controller facts
199        metrics (:obj:ControllerMetrics): controller metrics
200        vds (list of :obj:virtualdrive.VirtualDrives): controller virtual drives
201        encls (:obj:enclosure.Enclosures): controller enclosures
202
203    Methods:
204        create_vd (:obj:VirtualDrive): create virtual drive
205
206    """
207
208    def __init__(self, ctl_id, binary='storcli64'):
209        """Constructor - create StorCLI Controller object
210
211        Args:
212            ctl_id (str): controller id
213            binary (str): storcli binary or full path to the binary
214        """
215        self._ctl_id = ctl_id
216        self._binary = binary
217        self._storcli = StorCLI(binary)
218        self._name = '/c{0}'.format(self._ctl_id)
219
220        self._exist()
221
222    def __str__(self):
223        return '{0}'.format(common.response_data(self._run(['show'])))
224
225    def _run(self, args, **kwargs):
226        args = args[:]
227        args.insert(0, self._name)
228        return self._storcli.run(args, **kwargs)
229
230    def _exist(self):
231        try:
232            self._run(['show'])
233        except exc.StorCliCmdError:
234            raise exc.StorCliMissingError(
235                self.__class__.__name__, self._name) from None
236
237    @property
238    def id(self):
239        """ (str): controller id
240        """
241        return self._ctl_id
242
243    @property
244    def name(self):
245        """ (str): controller cmd name
246        """
247        return self._name
248
249    @property
250    def facts(self):
251        """ (dict): raw controller facts
252        """
253        args = [
254            'show',
255            'all'
256        ]
257        return common.response_data(self._run(args))
258
259    @property
260    def metrics(self):
261        """(:obj:ControllerMetrics): controller metrics
262        """
263        return ControllerMetrics(ctl=self)
264
265    @property
266    def vds(self):
267        """(:obj:virtualdrive.VirtualDrives): controllers virtual drives
268        """
269        return virtualdrive.VirtualDrives(ctl_id=self._ctl_id, binary=self._binary)
270
271    @property
272    def encls(self):
273        """(:obj:enclosure.Enclosures): controller enclosures
274        """
275        return enclosure.Enclosures(ctl_id=self._ctl_id, binary=self._binary)
276
277    def create_vd(self, name, raid, drives, strip='64'):
278        """Create virtual drive (raid) managed by current controller
279
280        Args:
281            name (str): virtual drive name
282            raid (str): virtual drive raid level (raid0, raid1, ...)
283            drives (str): storcli drives expression (e:s|e:s-x|e:s-x,y;e:s-x,y,z)
284            strip (str, optional): virtual drive raid strip size
285
286        Returns:
287            (None): no virtual drive created with name
288            (:obj:virtualdrive.VirtualDrive)
289        """
290        args = [
291            'add',
292            'vd',
293            'type={0}'.format(raid),
294            'name={0}'.format(name),
295            'drives={0}'.format(drives),
296            'strip={0}'.format(strip)
297        ]
298
299        self._run(args)
300        for vd in self.vds:
301            if name == vd.name:
302                return vd
303        return None
304
305
306class Controllers(object):
307    """StorCLI Controllers
308
309    Instance of this class is iterable with :obj:Controller as item
310
311    Args:
312        binary (str): storcli binary or full path to the binary
313
314    Properties:
315        ids (list of str): list of controllers id
316
317    Methods:
318        get_clt (:obj:Controller): return controller object by id
319    """
320
321    def __init__(self, binary='storcli64'):
322        """Constructor - create StorCLI Controllers object
323
324        Args:
325            binary (str): storcli binary or full path to the binary
326        """
327        self._binary = binary
328        self._storcli = StorCLI(binary)
329
330    @property
331    def _ctl_ids(self) -> List[str]:
332        out = self._storcli.run(['show'])
333        response = common.response_data(out)
334
335        if "Number of Controllers" in response and response["Number of Controllers"] == 0:
336            return []
337        else:
338            return [ctl['Ctl'] for ctl in common.response_data(out)['System Overview']]
339
340    @property
341    def _ctls(self):
342        for ctl_id in self._ctl_ids:
343            yield Controller(ctl_id=ctl_id, binary=self._binary)
344
345    def __iter__(self):
346        return self._ctls
347
348    @property
349    def ids(self):
350        """(list of str): controllers id
351        """
352        return self._ctl_ids
353
354    def get_ctl(self, ctl_id):
355        """Get controller object by id
356
357        Args:
358            ctl_id (str): controller id
359
360        Returns:
361            (None): no controller with id
362            (:obj:Controller): controller object
363        """
364        for ctl in self:
365            if ctl.id == ctl_id:
366                return ctl
367        return None
class ControllerMetrics:
 20class ControllerMetrics(object):
 21    """StorCLI Controller Metrics
 22
 23    Instance of this class represents controller metrics
 24    """
 25
 26    def __init__(self, ctl):
 27        """Constructor - create StorCLI ControllerMetrics object
 28
 29        Args:
 30            ctl (:obj:Controller): controller object
 31
 32        Properties:
 33            state (str): controller status
 34            memory_correctable_error (str): number of controllers memory correctable errors
 35            memory_uncorrectable_error (str): number of controllers memory uncorrectable errors
 36            drive_groups (str): number of drive groups on controller
 37            virtual_drives (str): number of virtual drives on controller
 38            virtual_drives_non_optimal (dict): number of virtual drives with in non optimal state
 39            physical_drives (str): number of physical drives on controller
 40            physical_drives_non_optimal (dict): number of physical drives in non optimal state
 41            roc_temperature (str): RAID-on-Chip temperature
 42            ctl_temperature (str): controller temperature
 43            all (dict): all metrics
 44        """
 45        self._ctl = ctl
 46
 47    @property
 48    def _show_all(self):
 49        args = [
 50            'show',
 51            'all',
 52        ]
 53        return common.response_data(self._ctl._run(args))
 54
 55    @property
 56    def _status(self):
 57        return self._show_all['Status']
 58
 59    @property
 60    def _hwcfg(self):
 61        return self._show_all['HwCfg']
 62
 63    @property
 64    @common.stringify
 65    @common.lower
 66    def state(self):
 67        """(str): controller status (Needs Attention | Optimal | Failed | Unknown)
 68        """
 69        return self._status["Controller Status"]
 70
 71    @property
 72    @common.stringify
 73    def memory_correctable_error(self):
 74        """(str): number of controllers memory correctable errors
 75        """
 76        return self._status["Memory Correctable Errors"]
 77
 78    @property
 79    @common.stringify
 80    def memory_uncorrectable_error(self):
 81        """(str): number of controllers memory uncorrectable errors
 82        """
 83        return self._status["Memory Uncorrectable Errors"]
 84
 85    @property
 86    @common.stringify
 87    def drive_groups(self):
 88        """(str): number of drive groups on controller
 89        """
 90        data = self._show_all
 91        if 'Drive Groups' in data:
 92            return data["Drive Groups"]
 93        return 0
 94
 95    @property
 96    @common.stringify
 97    def virtual_drives(self):
 98        """(str): number of virtual drives on controller
 99        """
100        data = self._show_all
101        if 'Virtual Drives' in data:
102            return data["Virtual Drives"]
103        return 0
104
105    @property
106    def virtual_drives_non_optimal(self):
107        """(dict): number of virtual drives with in non optimal state
108        """
109        vds = {}
110
111        if not self._ctl.vds.has_vds:
112            return vds
113
114        for vd in self._ctl.vds:
115            if not vd.state == 'optimal':
116                if vd.state in vds:
117                    vds[vd.state] += 1
118                else:
119                    vds[vd.state] = 1
120
121        # convert counter to string
122        return {k: str(v) for k, v in vds.items()}
123
124    @property
125    @common.stringify
126    def physical_drives(self):
127        """(str): number of physical drives on controller
128        """
129        data = self._show_all
130
131        if 'Physical Drives' in data:
132            return data["Physical Drives"]
133        return 0
134
135    @property
136    def physical_drives_non_optimal(self):
137        """(dict): number of physical drives in non optimal state (UBad | Offln)
138        """
139        drives = {}
140
141        for encl in self._ctl.encls:
142            if not encl.has_drives:
143                continue
144            for drive in encl.drives:
145                if not drive.state in ('good', 'online', 'ghs', 'dhs'):
146                    if drive.state in drives:
147                        drives[drive.state] += 1
148                    else:
149                        drives[drive.state] = 1
150
151        # convert counter to string
152        return {k: str(v) for k, v in drives.items()}
153
154    @property
155    @common.stringify
156    def roc_temperature(self):
157        """(str): RAID-on-Chip temperature or unknown if absent
158        """
159        hwcfg = self._hwcfg
160        if hwcfg['Temperature Sensor for ROC'] == 'Present':
161            return hwcfg['ROC temperature(Degree Celsius)']
162        return 'unknown'
163
164    @property
165    @common.stringify
166    def ctl_temperature(self):
167        """(str): Controller temperature or unknown if absent
168        """
169        hwcfg = self._hwcfg
170
171        if hwcfg['Temperature Sensor for Controller'] == 'Present':
172            return hwcfg['Controller temperature(Degree Celsius)']
173        return 'unknown'
174
175    @property
176    def all(self):
177        """(dict): all metrics
178        """
179        metrics = {}
180
181        for attribute in dir(self):
182            if not attribute.startswith('_') and not attribute == 'all':
183                metrics[attribute] = self.__getattribute__(attribute)
184        return metrics

StorCLI Controller Metrics

Instance of this class represents controller metrics

ControllerMetrics(ctl)
26    def __init__(self, ctl):
27        """Constructor - create StorCLI ControllerMetrics object
28
29        Args:
30            ctl (:obj:Controller): controller object
31
32        Properties:
33            state (str): controller status
34            memory_correctable_error (str): number of controllers memory correctable errors
35            memory_uncorrectable_error (str): number of controllers memory uncorrectable errors
36            drive_groups (str): number of drive groups on controller
37            virtual_drives (str): number of virtual drives on controller
38            virtual_drives_non_optimal (dict): number of virtual drives with in non optimal state
39            physical_drives (str): number of physical drives on controller
40            physical_drives_non_optimal (dict): number of physical drives in non optimal state
41            roc_temperature (str): RAID-on-Chip temperature
42            ctl_temperature (str): controller temperature
43            all (dict): all metrics
44        """
45        self._ctl = ctl

Constructor - create StorCLI ControllerMetrics object

Args: ctl (Controller): controller object

Properties: state (str): controller status memory_correctable_error (str): number of controllers memory correctable errors memory_uncorrectable_error (str): number of controllers memory uncorrectable errors drive_groups (str): number of drive groups on controller virtual_drives (str): number of virtual drives on controller virtual_drives_non_optimal (dict): number of virtual drives with in non optimal state physical_drives (str): number of physical drives on controller physical_drives_non_optimal (dict): number of physical drives in non optimal state roc_temperature (str): RAID-on-Chip temperature ctl_temperature (str): controller temperature all (dict): all metrics

state

func effective wrapper

memory_correctable_error

func effective wrapper

memory_uncorrectable_error

func effective wrapper

drive_groups

func effective wrapper

virtual_drives

func effective wrapper

virtual_drives_non_optimal

(dict): number of virtual drives with in non optimal state

physical_drives

func effective wrapper

physical_drives_non_optimal

(dict): number of physical drives in non optimal state (UBad | Offln)

roc_temperature

func effective wrapper

ctl_temperature

func effective wrapper

all

(dict): all metrics

class Controller:
187class Controller(object):
188    """StorCLI Controller
189
190    Instance of this class represents controller in StorCLI hierarchy
191
192    Args:
193        ctl_id (str): controller id
194        binary (str): storcli binary or full path to the binary
195
196    Properties:
197        id (str): controller id
198        name (str): controller cmd name
199        facts (dict): raw controller facts
200        metrics (:obj:ControllerMetrics): controller metrics
201        vds (list of :obj:virtualdrive.VirtualDrives): controller virtual drives
202        encls (:obj:enclosure.Enclosures): controller enclosures
203
204    Methods:
205        create_vd (:obj:VirtualDrive): create virtual drive
206
207    """
208
209    def __init__(self, ctl_id, binary='storcli64'):
210        """Constructor - create StorCLI Controller object
211
212        Args:
213            ctl_id (str): controller id
214            binary (str): storcli binary or full path to the binary
215        """
216        self._ctl_id = ctl_id
217        self._binary = binary
218        self._storcli = StorCLI(binary)
219        self._name = '/c{0}'.format(self._ctl_id)
220
221        self._exist()
222
223    def __str__(self):
224        return '{0}'.format(common.response_data(self._run(['show'])))
225
226    def _run(self, args, **kwargs):
227        args = args[:]
228        args.insert(0, self._name)
229        return self._storcli.run(args, **kwargs)
230
231    def _exist(self):
232        try:
233            self._run(['show'])
234        except exc.StorCliCmdError:
235            raise exc.StorCliMissingError(
236                self.__class__.__name__, self._name) from None
237
238    @property
239    def id(self):
240        """ (str): controller id
241        """
242        return self._ctl_id
243
244    @property
245    def name(self):
246        """ (str): controller cmd name
247        """
248        return self._name
249
250    @property
251    def facts(self):
252        """ (dict): raw controller facts
253        """
254        args = [
255            'show',
256            'all'
257        ]
258        return common.response_data(self._run(args))
259
260    @property
261    def metrics(self):
262        """(:obj:ControllerMetrics): controller metrics
263        """
264        return ControllerMetrics(ctl=self)
265
266    @property
267    def vds(self):
268        """(:obj:virtualdrive.VirtualDrives): controllers virtual drives
269        """
270        return virtualdrive.VirtualDrives(ctl_id=self._ctl_id, binary=self._binary)
271
272    @property
273    def encls(self):
274        """(:obj:enclosure.Enclosures): controller enclosures
275        """
276        return enclosure.Enclosures(ctl_id=self._ctl_id, binary=self._binary)
277
278    def create_vd(self, name, raid, drives, strip='64'):
279        """Create virtual drive (raid) managed by current controller
280
281        Args:
282            name (str): virtual drive name
283            raid (str): virtual drive raid level (raid0, raid1, ...)
284            drives (str): storcli drives expression (e:s|e:s-x|e:s-x,y;e:s-x,y,z)
285            strip (str, optional): virtual drive raid strip size
286
287        Returns:
288            (None): no virtual drive created with name
289            (:obj:virtualdrive.VirtualDrive)
290        """
291        args = [
292            'add',
293            'vd',
294            'type={0}'.format(raid),
295            'name={0}'.format(name),
296            'drives={0}'.format(drives),
297            'strip={0}'.format(strip)
298        ]
299
300        self._run(args)
301        for vd in self.vds:
302            if name == vd.name:
303                return vd
304        return None

StorCLI Controller

Instance of this class represents controller in StorCLI hierarchy

Args: ctl_id (str): controller id binary (str): storcli binary or full path to the binary

Properties: id (str): controller id name (str): controller cmd name facts (dict): raw controller facts metrics (ControllerMetrics): controller metrics vds (list of virtualdrive.VirtualDrives): controller virtual drives encls (enclosure.Enclosures): controller enclosures

Methods: create_vd (VirtualDrive): create virtual drive

Controller(ctl_id, binary='storcli64')
209    def __init__(self, ctl_id, binary='storcli64'):
210        """Constructor - create StorCLI Controller object
211
212        Args:
213            ctl_id (str): controller id
214            binary (str): storcli binary or full path to the binary
215        """
216        self._ctl_id = ctl_id
217        self._binary = binary
218        self._storcli = StorCLI(binary)
219        self._name = '/c{0}'.format(self._ctl_id)
220
221        self._exist()

Constructor - create StorCLI Controller object

Args: ctl_id (str): controller id binary (str): storcli binary or full path to the binary

id

(str): controller id

name

(str): controller cmd name

facts

(dict): raw controller facts

metrics

(ControllerMetrics): controller metrics

vds

(virtualdrive.VirtualDrives): controllers virtual drives

encls

(enclosure.Enclosures): controller enclosures

def create_vd(self, name, raid, drives, strip='64')
278    def create_vd(self, name, raid, drives, strip='64'):
279        """Create virtual drive (raid) managed by current controller
280
281        Args:
282            name (str): virtual drive name
283            raid (str): virtual drive raid level (raid0, raid1, ...)
284            drives (str): storcli drives expression (e:s|e:s-x|e:s-x,y;e:s-x,y,z)
285            strip (str, optional): virtual drive raid strip size
286
287        Returns:
288            (None): no virtual drive created with name
289            (:obj:virtualdrive.VirtualDrive)
290        """
291        args = [
292            'add',
293            'vd',
294            'type={0}'.format(raid),
295            'name={0}'.format(name),
296            'drives={0}'.format(drives),
297            'strip={0}'.format(strip)
298        ]
299
300        self._run(args)
301        for vd in self.vds:
302            if name == vd.name:
303                return vd
304        return None

Create virtual drive (raid) managed by current controller

Args: name (str): virtual drive name raid (str): virtual drive raid level (raid0, raid1, ...) drives (str): storcli drives expression (e:s|e:s-x|e:s-x,y;e:s-x,y,z) strip (str, optional): virtual drive raid strip size

Returns: (None): no virtual drive created with name (virtualdrive.VirtualDrive)

class Controllers:
307class Controllers(object):
308    """StorCLI Controllers
309
310    Instance of this class is iterable with :obj:Controller as item
311
312    Args:
313        binary (str): storcli binary or full path to the binary
314
315    Properties:
316        ids (list of str): list of controllers id
317
318    Methods:
319        get_clt (:obj:Controller): return controller object by id
320    """
321
322    def __init__(self, binary='storcli64'):
323        """Constructor - create StorCLI Controllers object
324
325        Args:
326            binary (str): storcli binary or full path to the binary
327        """
328        self._binary = binary
329        self._storcli = StorCLI(binary)
330
331    @property
332    def _ctl_ids(self) -> List[str]:
333        out = self._storcli.run(['show'])
334        response = common.response_data(out)
335
336        if "Number of Controllers" in response and response["Number of Controllers"] == 0:
337            return []
338        else:
339            return [ctl['Ctl'] for ctl in common.response_data(out)['System Overview']]
340
341    @property
342    def _ctls(self):
343        for ctl_id in self._ctl_ids:
344            yield Controller(ctl_id=ctl_id, binary=self._binary)
345
346    def __iter__(self):
347        return self._ctls
348
349    @property
350    def ids(self):
351        """(list of str): controllers id
352        """
353        return self._ctl_ids
354
355    def get_ctl(self, ctl_id):
356        """Get controller object by id
357
358        Args:
359            ctl_id (str): controller id
360
361        Returns:
362            (None): no controller with id
363            (:obj:Controller): controller object
364        """
365        for ctl in self:
366            if ctl.id == ctl_id:
367                return ctl
368        return None

StorCLI Controllers

Instance of this class is iterable with Controller as item

Args: binary (str): storcli binary or full path to the binary

Properties: ids (list of str): list of controllers id

Methods: get_clt (Controller): return controller object by id

Controllers(binary='storcli64')
322    def __init__(self, binary='storcli64'):
323        """Constructor - create StorCLI Controllers object
324
325        Args:
326            binary (str): storcli binary or full path to the binary
327        """
328        self._binary = binary
329        self._storcli = StorCLI(binary)

Constructor - create StorCLI Controllers object

Args: binary (str): storcli binary or full path to the binary

ids

(list of str): controllers id

def get_ctl(self, ctl_id)
355    def get_ctl(self, ctl_id):
356        """Get controller object by id
357
358        Args:
359            ctl_id (str): controller id
360
361        Returns:
362            (None): no controller with id
363            (:obj:Controller): controller object
364        """
365        for ctl in self:
366            if ctl.id == ctl_id:
367                return ctl
368        return None

Get controller object by id

Args: ctl_id (str): controller id

Returns: (None): no controller with id (Controller): controller object