pystorcli.storcli
StorCLI python class
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 python class 8''' 9 10import os 11import re 12import json 13import shutil 14import threading 15import subprocess 16 17from . import common 18from . import exc 19from . import cmdRunner 20 21_SINGLETON_STORCLI_MODULE_LOCK = threading.Lock() 22_SINGLETON_STORCLI_MODULE_ENABLE = False 23 24 25class StorCLI(object): 26 """StorCLI command line wrapper 27 28 Instance of this class is storcli command line wrapper 29 30 Args: 31 binary (str): storcli binary or full path to the binary 32 33 Properties: 34 cache_enable (boolean): enable disable resposne cache (also setter) 35 cache (dict): get / set raw cache content 36 37 Methods: 38 run (dict): output data from command line 39 check_response_status (): check ouput command line status from storcli 40 clear_cache (): purge cache 41 42 TODO: 43 * implement TTL for cache 44 45 """ 46 __singleton_instance = None 47 __cache_lock = threading.Lock() 48 __cache_enabled = False 49 __response_cache = {} 50 __cmdrunner = None 51 52 def __new__(cls, *args, **kwargs): 53 """Thread safe singleton 54 """ 55 global _SINGLETON_STORCLI_MODULE_LOCK 56 with _SINGLETON_STORCLI_MODULE_LOCK: 57 if _SINGLETON_STORCLI_MODULE_ENABLE: 58 if StorCLI.__singleton_instance is None: 59 StorCLI.__singleton_instance = super( 60 StorCLI, cls).__new__(cls) 61 return StorCLI.__singleton_instance 62 else: 63 return super(StorCLI, cls).__new__(cls) 64 65 def __init__(self, binary='storcli64', cmdrunner=None): 66 """Constructor - create StorCLI object wrapper 67 68 Args: 69 binary (str): storcli binary or full path to the binary 70 """ 71 72 if cmdrunner is None: 73 if self.__cmdrunner is None: 74 self.__cmdrunner = cmdRunner.CMDRunner() 75 else: 76 self.__cmdrunner = cmdrunner 77 78 if _SINGLETON_STORCLI_MODULE_ENABLE: 79 if not hasattr(self, '_storcli'): 80 # do not override _storcli in singleton if already exist 81 self._storcli = self.__cmdrunner.binaryCheck(binary) 82 83 if not _SINGLETON_STORCLI_MODULE_ENABLE: 84 # dont share singleton lock and binary 85 self._storcli = self.__cmdrunner.binaryCheck(binary) 86 self.__cache_lock = threading.Lock() 87 88 @property 89 def cache_enable(self): 90 """Enable/Disable resposne cache (atomic) 91 92 Returns: 93 bool: true/false 94 """ 95 96 return self.__cache_enabled 97 98 @cache_enable.setter 99 def cache_enable(self, value): 100 with self.__cache_lock: 101 self.__cache_enabled = value 102 103 def clear_cache(self): 104 """Clear cache (atomic) 105 """ 106 with self.__cache_lock: 107 self.__response_cache = {} 108 109 @property 110 def cache(self): 111 """Get/Set raw cache 112 113 Args: 114 (dict): raw cache 115 116 Returns: 117 (dict): cache 118 """ 119 return self.__response_cache 120 121 @cache.setter 122 def cache(self, value): 123 with self.__cache_lock: 124 self.__response_cache = value 125 126 @staticmethod 127 def check_response_status(cmd, out): 128 """Check ouput command line status from storcli. 129 130 Args: 131 cmd (list of str): full command line 132 out (dict): output from command line 133 134 Raises: 135 StorCliCmdError 136 """ 137 cmd_status = common.response_cmd(out) 138 if cmd_status['Status'] == 'Failure': 139 if 'Detailed Status' in cmd_status: 140 raise exc.StorCliCmdError( 141 cmd, "{0}".format(cmd_status['Detailed Status'])) 142 else: 143 raise exc.StorCliCmdError(cmd, "{0}".format(cmd_status)) 144 145 def run(self, args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs): 146 """Execute storcli command line with arguments. 147 148 Run command line and check output for errors. 149 150 Args: 151 args (list of str): cmd line arguments (without binary) 152 stdout (fd): controll subprocess stdout fd 153 stderr (fd): controll subporcess stderr fd 154 **kwargs: arguments to subprocess run 155 156 Returns: 157 dict: output data from command line 158 159 Raises: 160 exc.StorCliCmdError 161 exc.StorCliRunTimeError 162 exc.StorCliRunTimeout 163 """ 164 cmd = [self._storcli] 165 cmd.extend(args) 166 # output in JSON format 167 cmd.append('J') 168 cmd_cache_key = ''.join(cmd) 169 170 if self.cache_enable: 171 if cmd_cache_key in self.__response_cache: 172 return self.__response_cache[cmd_cache_key] 173 174 with self.__cache_lock: 175 try: 176 ret = self.__cmdrunner.run( 177 args=cmd, stdout=stdout, stderr=stderr, universal_newlines=True, **kwargs) 178 try: 179 ret_json = json.loads(ret.stdout) 180 self.check_response_status(cmd, ret_json) 181 ret.check_returncode() 182 if self.cache_enable: 183 self.__response_cache[cmd_cache_key] = ret_json 184 return ret_json 185 except json.JSONDecodeError: 186 # :/ 187 err = re.search('(^.*)Storage.*Command.*$', 188 ret.stdout, re.MULTILINE | re.DOTALL).group(1) 189 raise exc.StorCliCmdError(cmd, err) 190 except subprocess.TimeoutExpired as err: 191 raise exc.StorCliRunTimeout(err) 192 except subprocess.SubprocessError as err: 193 raise exc.StorCliRunTimeError(err) 194 195 # Singleton stuff 196 @staticmethod 197 def __set_singleton(value): 198 global _SINGLETON_STORCLI_MODULE_ENABLE 199 global _SINGLETON_STORCLI_MODULE_LOCK 200 with _SINGLETON_STORCLI_MODULE_LOCK: 201 _SINGLETON_STORCLI_MODULE_ENABLE = value 202 203 @staticmethod 204 def enable_singleton(): 205 """Enable StorCLI to be singleton on module level 206 207 Use StorCLI singleton across all objects. All pystorcli 208 class instances use their own StorCLI object. With enabled cache 209 we can speedup for example metric lookups. 210 211 """ 212 StorCLI.__set_singleton(True) 213 214 @staticmethod 215 def disable_singleton(): 216 """Disable StoreCLI class as signleton 217 """ 218 StorCLI.__set_singleton(False) 219 220 @staticmethod 221 def is_singleton() -> bool: 222 """Check if singleton is enabled 223 """ 224 return _SINGLETON_STORCLI_MODULE_ENABLE
26class StorCLI(object): 27 """StorCLI command line wrapper 28 29 Instance of this class is storcli command line wrapper 30 31 Args: 32 binary (str): storcli binary or full path to the binary 33 34 Properties: 35 cache_enable (boolean): enable disable resposne cache (also setter) 36 cache (dict): get / set raw cache content 37 38 Methods: 39 run (dict): output data from command line 40 check_response_status (): check ouput command line status from storcli 41 clear_cache (): purge cache 42 43 TODO: 44 * implement TTL for cache 45 46 """ 47 __singleton_instance = None 48 __cache_lock = threading.Lock() 49 __cache_enabled = False 50 __response_cache = {} 51 __cmdrunner = None 52 53 def __new__(cls, *args, **kwargs): 54 """Thread safe singleton 55 """ 56 global _SINGLETON_STORCLI_MODULE_LOCK 57 with _SINGLETON_STORCLI_MODULE_LOCK: 58 if _SINGLETON_STORCLI_MODULE_ENABLE: 59 if StorCLI.__singleton_instance is None: 60 StorCLI.__singleton_instance = super( 61 StorCLI, cls).__new__(cls) 62 return StorCLI.__singleton_instance 63 else: 64 return super(StorCLI, cls).__new__(cls) 65 66 def __init__(self, binary='storcli64', cmdrunner=None): 67 """Constructor - create StorCLI object wrapper 68 69 Args: 70 binary (str): storcli binary or full path to the binary 71 """ 72 73 if cmdrunner is None: 74 if self.__cmdrunner is None: 75 self.__cmdrunner = cmdRunner.CMDRunner() 76 else: 77 self.__cmdrunner = cmdrunner 78 79 if _SINGLETON_STORCLI_MODULE_ENABLE: 80 if not hasattr(self, '_storcli'): 81 # do not override _storcli in singleton if already exist 82 self._storcli = self.__cmdrunner.binaryCheck(binary) 83 84 if not _SINGLETON_STORCLI_MODULE_ENABLE: 85 # dont share singleton lock and binary 86 self._storcli = self.__cmdrunner.binaryCheck(binary) 87 self.__cache_lock = threading.Lock() 88 89 @property 90 def cache_enable(self): 91 """Enable/Disable resposne cache (atomic) 92 93 Returns: 94 bool: true/false 95 """ 96 97 return self.__cache_enabled 98 99 @cache_enable.setter 100 def cache_enable(self, value): 101 with self.__cache_lock: 102 self.__cache_enabled = value 103 104 def clear_cache(self): 105 """Clear cache (atomic) 106 """ 107 with self.__cache_lock: 108 self.__response_cache = {} 109 110 @property 111 def cache(self): 112 """Get/Set raw cache 113 114 Args: 115 (dict): raw cache 116 117 Returns: 118 (dict): cache 119 """ 120 return self.__response_cache 121 122 @cache.setter 123 def cache(self, value): 124 with self.__cache_lock: 125 self.__response_cache = value 126 127 @staticmethod 128 def check_response_status(cmd, out): 129 """Check ouput command line status from storcli. 130 131 Args: 132 cmd (list of str): full command line 133 out (dict): output from command line 134 135 Raises: 136 StorCliCmdError 137 """ 138 cmd_status = common.response_cmd(out) 139 if cmd_status['Status'] == 'Failure': 140 if 'Detailed Status' in cmd_status: 141 raise exc.StorCliCmdError( 142 cmd, "{0}".format(cmd_status['Detailed Status'])) 143 else: 144 raise exc.StorCliCmdError(cmd, "{0}".format(cmd_status)) 145 146 def run(self, args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs): 147 """Execute storcli command line with arguments. 148 149 Run command line and check output for errors. 150 151 Args: 152 args (list of str): cmd line arguments (without binary) 153 stdout (fd): controll subprocess stdout fd 154 stderr (fd): controll subporcess stderr fd 155 **kwargs: arguments to subprocess run 156 157 Returns: 158 dict: output data from command line 159 160 Raises: 161 exc.StorCliCmdError 162 exc.StorCliRunTimeError 163 exc.StorCliRunTimeout 164 """ 165 cmd = [self._storcli] 166 cmd.extend(args) 167 # output in JSON format 168 cmd.append('J') 169 cmd_cache_key = ''.join(cmd) 170 171 if self.cache_enable: 172 if cmd_cache_key in self.__response_cache: 173 return self.__response_cache[cmd_cache_key] 174 175 with self.__cache_lock: 176 try: 177 ret = self.__cmdrunner.run( 178 args=cmd, stdout=stdout, stderr=stderr, universal_newlines=True, **kwargs) 179 try: 180 ret_json = json.loads(ret.stdout) 181 self.check_response_status(cmd, ret_json) 182 ret.check_returncode() 183 if self.cache_enable: 184 self.__response_cache[cmd_cache_key] = ret_json 185 return ret_json 186 except json.JSONDecodeError: 187 # :/ 188 err = re.search('(^.*)Storage.*Command.*$', 189 ret.stdout, re.MULTILINE | re.DOTALL).group(1) 190 raise exc.StorCliCmdError(cmd, err) 191 except subprocess.TimeoutExpired as err: 192 raise exc.StorCliRunTimeout(err) 193 except subprocess.SubprocessError as err: 194 raise exc.StorCliRunTimeError(err) 195 196 # Singleton stuff 197 @staticmethod 198 def __set_singleton(value): 199 global _SINGLETON_STORCLI_MODULE_ENABLE 200 global _SINGLETON_STORCLI_MODULE_LOCK 201 with _SINGLETON_STORCLI_MODULE_LOCK: 202 _SINGLETON_STORCLI_MODULE_ENABLE = value 203 204 @staticmethod 205 def enable_singleton(): 206 """Enable StorCLI to be singleton on module level 207 208 Use StorCLI singleton across all objects. All pystorcli 209 class instances use their own StorCLI object. With enabled cache 210 we can speedup for example metric lookups. 211 212 """ 213 StorCLI.__set_singleton(True) 214 215 @staticmethod 216 def disable_singleton(): 217 """Disable StoreCLI class as signleton 218 """ 219 StorCLI.__set_singleton(False) 220 221 @staticmethod 222 def is_singleton() -> bool: 223 """Check if singleton is enabled 224 """ 225 return _SINGLETON_STORCLI_MODULE_ENABLE
StorCLI command line wrapper
Instance of this class is storcli command line wrapper
Args: binary (str): storcli binary or full path to the binary
Properties: cache_enable (boolean): enable disable resposne cache (also setter) cache (dict): get / set raw cache content
Methods: run (dict): output data from command line check_response_status (): check ouput command line status from storcli clear_cache (): purge cache
TODO: * implement TTL for cache
66 def __init__(self, binary='storcli64', cmdrunner=None): 67 """Constructor - create StorCLI object wrapper 68 69 Args: 70 binary (str): storcli binary or full path to the binary 71 """ 72 73 if cmdrunner is None: 74 if self.__cmdrunner is None: 75 self.__cmdrunner = cmdRunner.CMDRunner() 76 else: 77 self.__cmdrunner = cmdrunner 78 79 if _SINGLETON_STORCLI_MODULE_ENABLE: 80 if not hasattr(self, '_storcli'): 81 # do not override _storcli in singleton if already exist 82 self._storcli = self.__cmdrunner.binaryCheck(binary) 83 84 if not _SINGLETON_STORCLI_MODULE_ENABLE: 85 # dont share singleton lock and binary 86 self._storcli = self.__cmdrunner.binaryCheck(binary) 87 self.__cache_lock = threading.Lock()
Constructor - create StorCLI object wrapper
Args: binary (str): storcli binary or full path to the binary
104 def clear_cache(self): 105 """Clear cache (atomic) 106 """ 107 with self.__cache_lock: 108 self.__response_cache = {}
Clear cache (atomic)
127 @staticmethod 128 def check_response_status(cmd, out): 129 """Check ouput command line status from storcli. 130 131 Args: 132 cmd (list of str): full command line 133 out (dict): output from command line 134 135 Raises: 136 StorCliCmdError 137 """ 138 cmd_status = common.response_cmd(out) 139 if cmd_status['Status'] == 'Failure': 140 if 'Detailed Status' in cmd_status: 141 raise exc.StorCliCmdError( 142 cmd, "{0}".format(cmd_status['Detailed Status'])) 143 else: 144 raise exc.StorCliCmdError(cmd, "{0}".format(cmd_status))
Check ouput command line status from storcli.
Args: cmd (list of str): full command line out (dict): output from command line
Raises: StorCliCmdError
146 def run(self, args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs): 147 """Execute storcli command line with arguments. 148 149 Run command line and check output for errors. 150 151 Args: 152 args (list of str): cmd line arguments (without binary) 153 stdout (fd): controll subprocess stdout fd 154 stderr (fd): controll subporcess stderr fd 155 **kwargs: arguments to subprocess run 156 157 Returns: 158 dict: output data from command line 159 160 Raises: 161 exc.StorCliCmdError 162 exc.StorCliRunTimeError 163 exc.StorCliRunTimeout 164 """ 165 cmd = [self._storcli] 166 cmd.extend(args) 167 # output in JSON format 168 cmd.append('J') 169 cmd_cache_key = ''.join(cmd) 170 171 if self.cache_enable: 172 if cmd_cache_key in self.__response_cache: 173 return self.__response_cache[cmd_cache_key] 174 175 with self.__cache_lock: 176 try: 177 ret = self.__cmdrunner.run( 178 args=cmd, stdout=stdout, stderr=stderr, universal_newlines=True, **kwargs) 179 try: 180 ret_json = json.loads(ret.stdout) 181 self.check_response_status(cmd, ret_json) 182 ret.check_returncode() 183 if self.cache_enable: 184 self.__response_cache[cmd_cache_key] = ret_json 185 return ret_json 186 except json.JSONDecodeError: 187 # :/ 188 err = re.search('(^.*)Storage.*Command.*$', 189 ret.stdout, re.MULTILINE | re.DOTALL).group(1) 190 raise exc.StorCliCmdError(cmd, err) 191 except subprocess.TimeoutExpired as err: 192 raise exc.StorCliRunTimeout(err) 193 except subprocess.SubprocessError as err: 194 raise exc.StorCliRunTimeError(err)
Execute storcli command line with arguments.
Run command line and check output for errors.
Args: args (list of str): cmd line arguments (without binary) stdout (fd): controll subprocess stdout fd stderr (fd): controll subporcess stderr fd **kwargs: arguments to subprocess run
Returns: dict: output data from command line
Raises: exc.StorCliCmdError exc.StorCliRunTimeError exc.StorCliRunTimeout
204 @staticmethod 205 def enable_singleton(): 206 """Enable StorCLI to be singleton on module level 207 208 Use StorCLI singleton across all objects. All pystorcli 209 class instances use their own StorCLI object. With enabled cache 210 we can speedup for example metric lookups. 211 212 """ 213 StorCLI.__set_singleton(True)
Enable StorCLI to be singleton on module level
Use StorCLI singleton across all objects. All pystorcli class instances use their own StorCLI object. With enabled cache we can speedup for example metric lookups.