Package entropy :: Package client :: Package services :: Package system :: Module interfaces

Source Code for Module entropy.client.services.system.interfaces

  1  # -*- coding: utf-8 -*- 
  2  """ 
  3   
  4      @author: Fabio Erculiani <lxnay@sabayonlinux.org> 
  5      @contact: lxnay@sabayonlinux.org 
  6      @copyright: Fabio Erculiani 
  7      @license: GPL-2 
  8   
  9      B{Entropy Client Services Base Interface}. 
 10   
 11  """ 
 12  import time 
 13  from entropy.const import const_isstring, etpUi 
 14  from entropy.exceptions import * 
 15  from entropy.i18n import _ 
 16  from entropy.misc import TimeScheduled 
 17  from entropy.i18n import _ 
 18   
19 -class Client:
20 21 ssl_connection = True
22 - def __init__(self, OutputInterface, MethodsInterface = None, 23 ClientCommandsInterface = None, quiet = True, show_progress = False, 24 do_cache_connection = False, do_cache_session = False, 25 socket_timeout = 120.0):
26 27 if not hasattr(OutputInterface, 'updateProgress'): 28 mytxt = _("OutputInterface does not have an updateProgress method") 29 raise IncorrectParameter("IncorrectParameter: %s, (! %s !)" % ( 30 OutputInterface, mytxt,)) 31 elif not hasattr(OutputInterface.updateProgress, '__call__'): 32 mytxt = _("OutputInterface does not have an updateProgress method") 33 raise IncorrectParameter("IncorrectParameter: %s, (! %s !)" % ( 34 OutputInterface, mytxt,)) 35 36 from entropy.client.services.system.commands import Client as ClientCommands 37 if not issubclass(ClientCommandsInterface, ClientCommands): 38 mytxt = _("A valid entropy.client.services.system.commands.Client class/subclass is needed") 39 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,)) 40 41 from entropy.client.services.system.methods import BaseMixin 42 if not issubclass(MethodsInterface, BaseMixin): 43 mytxt = _("A valid entropy.client.services.system.methods.BaseMixin class/subclass is needed") 44 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,)) 45 46 self.ClientCommandsInterface = ClientCommandsInterface 47 import socket, struct 48 import entropy.tools as entropyTools 49 self.socket, self.struct, self.entropyTools = socket, struct, entropyTools 50 from datetime import datetime 51 self.datetime = datetime 52 import threading 53 self.threading = threading 54 self.Output = OutputInterface 55 self.hostname = None 56 self.hostport = None 57 self.username = None 58 self.password = None 59 if quiet and etpUi['debug']: # enforce quiet value if debug is enabled 60 quiet = False 61 self.quiet = quiet 62 self.socket_timeout = socket_timeout 63 self.do_cache_connection = do_cache_connection 64 self.show_progress = show_progress 65 self.ClientCommandsInterface = ClientCommandsInterface 66 self.Methods = MethodsInterface(self) 67 self.session_cache = {} 68 self.SessionCacheLock = self.threading.Lock() 69 self.connection_cache = {} 70 self.CacheLock = self.threading.Lock() 71 self.shutdown = False 72 self.connection_killer = None 73 74 # XXX actually session cache doesn't work when the connection is closed and re-opened 75 # when the server is spawning requests under a child process (fork_requests = True) 76 # this should be fixed by pushing the cache to disk but triggers a possible security issue 77 # since sessions and their password are stored in memory and kept alive there until those 78 # expires 79 self.do_cache_session = do_cache_session 80 if self.do_cache_connection: 81 self.connection_killer = TimeScheduled(2, 82 self.connection_killer_handler) 83 self.connection_killer.start()
84
85 - def __del__(self):
86 if hasattr(self, 'shutdown'): 87 self.shutdown = True 88 if hasattr(self, 'connection_killer'): 89 if self.connection_killer != None: 90 self.connection_killer.kill()
91
92 - def _validate_credentials(self):
93 if not const_isstring(self.hostname): 94 raise IncorrectParameter("IncorrectParameter: hostname: %s. %s" % ( 95 _('not a string'), _('Please use setup_connection() properly'),)) 96 if not const_isstring(self.username): 97 raise IncorrectParameter("IncorrectParameter: username: %s. %s" % ( 98 _('not a string'), _('Please use setup_connection() properly'),)) 99 if not const_isstring(self.password): 100 raise IncorrectParameter("IncorrectParameter: password: %s. %s" % ( 101 _('not a string'), _('Please use setup_connection() properly'),)) 102 if not isinstance(self.hostport, int): 103 raise IncorrectParameter("IncorrectParameter: port: %s. %s" % ( 104 _('not an int'), _('Please use setup_connection() properly'),)) 105 if not isinstance(self.ssl_connection, bool): 106 raise IncorrectParameter("IncorrectParameter: ssl_connection: %s. %s" % ( 107 _('not a bool'), _('Please use setup_connection() properly'),))
108
109 - def get_session_cache(self, cmd_tuple):
110 if self.do_cache_session: 111 with self.SessionCacheLock: 112 return self.session_cache.get(cmd_tuple)
113
114 - def set_session_cache(self, cmd_tuple, session_id):
115 if self.do_cache_session: 116 with self.SessionCacheLock: 117 self.session_cache[cmd_tuple] = session_id
118
119 - def remove_session_cache(self, cmd_tuple):
120 if self.do_cache_session: 121 with self.SessionCacheLock: 122 del self.session_cache[cmd_tuple]
123
124 - def get_connection_cache_key(self):
125 return hash((self.hostname, self.hostport, self.username, self.password, self.ssl_connection,))
126
127 - def get_connection_cache(self):
128 if self.do_cache_connection: 129 key = self.get_connection_cache_key() 130 srv = self.connection_cache.get(key) 131 # FIXME: if you enable cache connection, you should also 132 # consider to clear the socket buffer 133 # srv.sock_conn 134 # srv.real_sock_conn 135 return srv
136
137 - def cache_connection(self, srv):
138 if self.do_cache_connection: 139 key = self.get_connection_cache_key() 140 self.connection_cache[key] = { 141 'conn': srv, 142 'ts': self.get_ts(), 143 }
144
145 - def update_connection_ts(self):
146 if self.do_cache_connection: 147 key = self.get_connection_cache_key() 148 if key not in self.connection_cache: 149 return 150 self.connection_cache[key]['ts'] = self.get_ts()
151
152 - def kill_all_connections(self):
153 if self.do_cache_connection: 154 self.CacheLock.acquire() 155 try: 156 keys = list(self.connection_cache.keys()) 157 for key in keys: 158 data = self.connection_cache.pop(key) 159 data['conn'].disconnect() 160 finally: 161 self.CacheLock.release()
162
164 165 if not self.do_cache_connection: 166 return 167 if self.shutdown: 168 return 169 if not self.connection_cache: 170 return 171 172 keys = list(self.connection_cache.keys()) 173 for key in keys: 174 curr_ts = self.get_ts() 175 ts = self.connection_cache[key]['ts'] 176 delta = curr_ts - ts 177 if delta.seconds < 60: 178 continue 179 self.CacheLock.acquire() 180 try: 181 data = self.connection_cache.pop(key) 182 finally: 183 self.CacheLock.release() 184 srv = data['conn'] 185 srv.disconnect()
186
187 - def get_ts(self):
188 return self.datetime.fromtimestamp(time.time())
189
190 - def setup_connection(self, hostname, port, username, password, ssl):
191 self.hostname = hostname 192 self.hostport = port 193 self.username = username 194 self.password = password 195 self.ssl_connection = ssl 196 self._validate_credentials()
197
198 - def connect_to_service(self, timeout = None):
199 self._validate_credentials() 200 args = [self.Output, self.ClientCommandsInterface] 201 kwargs = { 202 'ssl': self.ssl_connection, 203 'quiet': self.quiet, 204 'show_progress': self.show_progress 205 } 206 if timeout is None: 207 kwargs['socket_timeout'] = self.socket_timeout 208 else: 209 kwargs['socket_timeout'] = timeout 210 from entropy.services.ugc.interfaces import Client 211 srv = Client(*args,**kwargs) 212 srv.connect(self.hostname, self.hostport) 213 return srv
214
215 - def get_service_connection(self, timeout = None):
216 try: 217 srv = self.connect_to_service(timeout = timeout) 218 except (ConnectionError, self.socket.error, self.struct.error,): 219 return None 220 return srv
221
222 - def logout(self, srv, session_id):
223 self._validate_credentials() 224 return srv.CmdInterface.service_logout(self.username, session_id)
225
226 - def login(self, srv, session_id):
227 self._validate_credentials() 228 return srv.CmdInterface.service_login(self.username, self.password, session_id)
229 230 # eval(func) must have session as first param
231 - def do_cmd(self, login_required, func, args, kwargs):
232 233 with self.CacheLock: 234 235 srv = self.get_connection_cache() 236 if srv == None: 237 srv = self.get_service_connection(timeout = self.socket_timeout) 238 if srv != None: 239 self.cache_connection(srv) 240 else: 241 srv = srv['conn'] 242 243 if srv == None: 244 return False, 'no connection' 245 246 cmd_tuple = (login_required, func,) 247 new_session = False 248 session = self.get_session_cache(cmd_tuple) 249 if session == None: 250 new_session = True 251 session = srv.open_session() 252 if session == None: 253 return False, 'no session' 254 else: 255 if not srv.is_session_alive(session): 256 new_session = True 257 session = srv.open_session() 258 if session == None: 259 return False, 'no session' 260 self.set_session_cache(cmd_tuple, session) 261 262 self.update_connection_ts() 263 args.insert(0, session) 264 265 if login_required and new_session: 266 logged, error = self.login(srv, session) 267 if not logged: 268 srv.close_session(session) 269 self.remove_session_cache(cmd_tuple) 270 if not self.do_cache_connection: 271 srv.disconnect() 272 return False, error 273 274 cmd_func = getattr(srv.CmdInterface, func) 275 rslt = cmd_func(*args, **kwargs) 276 if not self.do_cache_session: 277 if login_required: 278 self.logout(srv, session) 279 srv.close_session(session) 280 if not self.do_cache_connection: 281 srv.disconnect() 282 return rslt
283
285 return self.Methods.available_commands.copy()
286