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
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
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
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
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
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)
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
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
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