Package flumotion :: Package admin :: Module connections
[hide private]

Source Code for Module flumotion.admin.connections

  1  # -*- Mode: Python; fill-column: 80 -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3   
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  """recent connections""" 
 19   
 20  import datetime 
 21  import fnmatch 
 22  import os 
 23  from xml.dom import minidom, Node 
 24   
 25  from flumotion.common import log, common, xdg 
 26  from flumotion.common.connection import PBConnectionInfo, parsePBConnectionInfo 
 27  from flumotion.common.errors import OptionError 
 28  from flumotion.configure import configure 
 29  from flumotion.twisted.pb import Authenticator 
 30   
 31  __version__ = "$Rev$" 
 32   
 33   
34 -class ConnectionInfo(object):
35 """ 36 I wrap the information contained in a single connection file entry. 37 38 I can be used to construct L{PBConnectionInfo} object, but because some of 39 my variables can be shell globs, they are all strings. 40 """ 41
42 - def __init__(self, host, port, use_insecure, user, passwd, manager):
43 self.host = host 44 self.port = port 45 self.use_insecure = use_insecure 46 self.user = user 47 self.passwd = passwd 48 self.manager = manager
49
50 - def asPBConnectionInfo(self):
51 """ 52 Return a L{PBConnectionInfo} object constructed from my state. If my 53 state contains shell globs, I might throw a ValueError. 54 """ 55 if ('*' in self.host) or (self.use_insecure not in ('0', '1')): 56 raise ValueError("Shell glob in connection info") 57 return PBConnectionInfo(self.host, int(self.port), 58 self.use_insecure == '0', 59 Authenticator(username=self.user, 60 password=self.passwd))
61
62 - def __str__(self):
63 return '%s@%s:%s' % (self.user, self.host, self.port)
64 65
66 -class RecentConnection(object):
67 """ 68 I am an object representing a recent connection. 69 You can access some of my state and update the timestamp 70 (eg, when I was last connected to) by calling L{updateTimestamp}. 71 72 @ivar name: name of the recent connection usually host:port 73 @type name: string 74 @ivar host: hostname 75 @type host: string 76 @ivar filename: filename of the connection 77 @type filename: string 78 @ivar info: connection info 79 @type info: L{PBConnectionInfo} 80 @ivar timestamp: timestamp 81 @type timestamp: datetime.datetime 82 """ 83
84 - def __init__(self, host, filename, info):
85 self.name = str(info) 86 self.host = host 87 self.filename = filename 88 self.info = info.asPBConnectionInfo() 89 self.manager = info.manager 90 self.timestamp = datetime.datetime.fromtimestamp( 91 os.stat(filename).st_ctime)
92
93 - def updateTimestamp(self):
94 os.utime(self.filename, None)
95
96 - def asConnectionInfo(self):
97 """ 98 Return a L{ConnectionInfo} object constructed from my state. 99 """ 100 info = self.info 101 return ConnectionInfo(info.host, str(info.port), 102 info.use_ssl and '0' or '1', 103 info.authenticator.username, 104 info.authenticator.password, '')
105 106
107 -def _getRecentFilenames():
108 # DSU, or as perl folks call it, a Schwartz Transform 109 common.ensureDir(configure.registrydir, "registry dir") 110 111 for filename in os.listdir(configure.registrydir): 112 filename = os.path.join(configure.registrydir, filename) 113 if filename.endswith('.connection'): 114 yield filename
115 116
117 -def hasRecentConnections():
118 """ 119 Returns if we have at least one recent connection 120 @returns: if we have a recent connection 121 @rtype: bool 122 """ 123 gen = _getRecentFilenames() 124 try: 125 gen.next() 126 except StopIteration: 127 return False 128 129 return True
130 131
132 -def _parseConnection(element):
133 state = {} 134 for childNode in element.childNodes: 135 if (childNode.nodeType != Node.TEXT_NODE and 136 childNode.nodeType != Node.COMMENT_NODE): 137 state[childNode.nodeName] = childNode.childNodes[0].wholeText 138 return ConnectionInfo(state['host'], state['port'], state['use_insecure'], 139 state['user'], state['passwd'], state['manager'])
140 141
142 -def _parseSingleConnectionFile(filename):
143 tree = minidom.parse(filename) 144 return _parseConnection(tree.documentElement)
145 146
147 -def _parseMultipleConnectionsFile(filename):
148 tree = minidom.parse(filename) 149 return map(_parseConnection, tree.getElementsByTagName('connection'))
150 151
152 -def getRecentConnections():
153 """ 154 Fetches a list of recently used connections 155 @returns: recently used connections 156 @rtype: list of L{RecentConnection} 157 """ 158 159 recentFilenames = _getRecentFilenames() 160 recentConnections = [] 161 for filename in sorted(recentFilenames, reverse=True): 162 try: 163 state = _parseSingleConnectionFile(filename) 164 recentConnections.append( 165 RecentConnection(str(state), 166 filename=filename, 167 info=state)) 168 except Exception, e: 169 log.warning('connections', 'Error parsing %s: %r', filename, e) 170 return recentConnections
171 172
173 -def getDefaultConnections():
174 """ 175 Fetches a list of default connections. 176 177 @returns: default connections 178 @rtype: list of L{ConnectionInfo} 179 """ 180 181 filename = xdg.config_read_path('connections') 182 if not filename: 183 return [] 184 185 try: 186 return _parseMultipleConnectionsFile(filename) 187 except Exception, e: 188 log.warning('connections', 'Error parsing %s: %r', filename, e) 189 return []
190 191
192 -def updateFromConnectionList(info, connections, match_glob=False):
193 """ 194 Updates the info object with the username and password taken from the list 195 of connections. 196 197 @param info: connection info 198 @type info: L{PBConnectionInfo} 199 @param connections: recent or default connections 200 @type: a list of L{ConnectionInfo} 201 @param match_glob: if values of host, port, etc. to be matched between 202 info and the recent or default connections should be 203 treated as shell globs 204 @type: boolean 205 @returns: None 206 """ 207 208 def match(v1, v2): 209 if match_glob: 210 # v2 is the candidate, which might be a shell glob 211 return fnmatch.fnmatch(v1, v2) 212 else: 213 return v1 == v2
214 215 def compatible(info, c_info): 216 if not match(info.host, c_info.host): 217 return False 218 port = str(info.port) 219 if not match(port, c_info.port): 220 return False 221 use_insecure = info.use_ssl and '0' or '1' 222 if not match(use_insecure, c_info.use_insecure): 223 return False 224 auth = info.authenticator 225 if auth.username and not match(auth.username, c_info.user): 226 return False 227 # doesn't make sense to match the password, if everything before that 228 # matched, we won't fill in anything 229 return True 230 231 for candidate in connections: 232 if compatible(info, candidate): 233 # it's compatible, fill in the variables 234 if not info.authenticator.username: 235 info.authenticator.username = candidate.user 236 if not info.authenticator.password: 237 info.authenticator.password = candidate.passwd 238 break 239 return info 240 241
242 -def parsePBConnectionInfoRecent(managerString, use_ssl=True, 243 defaultPort=configure.defaultSSLManagerPort):
244 """The same as L{flumotion.common.connection.parsePBConnectionInfo}, 245 but fills in missing information from the recent connections cache or 246 from the default user and password definitions file if possible. 247 @param managerString: manager string we should connect to 248 @type managerString: string 249 @param use_ssl: True if we should use ssl 250 @type use_ssl: bool 251 @param defaultPort: default port to use 252 @type defaultPort: int 253 @returns: connection info 254 @rtype: a L{PBConnectionInfo} 255 """ 256 recent = getRecentConnections() 257 if not managerString: 258 if recent: 259 return recent[0].info 260 else: 261 raise OptionError('No string given and no recent ' 262 'connections to use') 263 264 info = parsePBConnectionInfo(managerString, username=None, 265 password=None, 266 port=defaultPort, 267 use_ssl=use_ssl) 268 269 if not (info.authenticator.username and info.authenticator.password): 270 recent_infos = [r.asConnectionInfo() for r in recent] 271 updateFromConnectionList(info, recent_infos, match_glob=False) 272 if not (info.authenticator.username and info.authenticator.password): 273 defaults = getDefaultConnections() 274 updateFromConnectionList(info, defaults, match_glob=True) 275 if not (info.authenticator.username and info.authenticator.password): 276 raise OptionError('You are connecting to %s for the ' 277 'first time; please specify a user and ' 278 'password (e.g. user:test@%s).' 279 % (managerString, managerString)) 280 else: 281 return info
282