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