Package ccm
[hide private]
[frames] | no frames]

Source Code for Package ccm

   1  #============================================================================  
   2  #Name        : __init__.py  
   3  #Part of     : Helium  
   4   
   5  #Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
   6  #All rights reserved. 
   7  #This component and the accompanying materials are made available 
   8  #under the terms of the License "Eclipse Public License v1.0" 
   9  #which accompanies this distribution, and is available 
  10  #at the URL "http://www.eclipse.org/legal/epl-v10.html". 
  11  # 
  12  #Initial Contributors: 
  13  #Nokia Corporation - initial contribution. 
  14  # 
  15  #Contributors: 
  16  # 
  17  #Description: 
  18  #=============================================================================== 
  19   
  20  """ CM/Synergy Python toolkit. 
  21   
  22  """ 
  23   
  24  import logging 
  25  import netrc 
  26  import os 
  27  import re 
  28  import subprocess 
  29  import sys 
  30  import threading 
  31   
  32  import fileutils 
  33  import nokia.gscm 
  34   
  35   
  36  # Uncomment this line to enable logging in this module, or configure logging elsewhere 
  37  _logger = logging.getLogger("ccm") 
  38  #logging.basicConfig(level=logging.DEBUG) 
  39   
  40   
  41  VALID_OBJECT_STATES = ('working', 'checkpoint', 'public', 'prep', 'integrate', 'sqa', 'test','released') 
  42  STATIC_OBJECT_STATES = ('integrate', 'sqa', 'test','released') 
  43  CCM_SESSION_LOCK = os.path.join(os.environ['TEMP'], "ccm_session.lock") 
  44   
45 -def _execute(command, timeout=None):
46 """ Runs a command and returns the result data. """ 47 launcher = os.path.join(os.environ['HELIUM_HOME'], 'tools', 'common', 'python', 'scripts', 'timeout_launcher.py') 48 targ = "" 49 if timeout is not None: 50 targ = "--timeout=%s" % timeout 51 process = subprocess.Popen("python %s %s -- %s" % (launcher, targ, command), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) 52 stdout = process.communicate()[0] 53 process.wait() 54 _logger.debug(stdout) 55 _logger.debug("Return code: %s" % process.returncode) 56 return (stdout, process.returncode)
57 58
59 -class CCMException(Exception):
60 """ Base exception that should be raised by methods of this framework. """
61 - def __init__(self, reason, result = None):
62 Exception.__init__(self, reason) 63 self.result = result
64 65
66 -class Result(object):
67 """Class that abstracts ccm call result handling. 68 69 Subclass it to implement a new generic output parser. 70 """
71 - def __init__(self, session):
72 self._session = session 73 self.status = None 74 self._output = None 75 self._output_str = None
76
77 - def _setoutput(self, output):
78 self._output = output
79
80 - def __setoutput(self, output):
81 """ Internal function to allow overloading, you must override _setoutput. 82 """ 83 # the output is automatically converted to ascii before any treatment 84 if isinstance(output, unicode): 85 self._output_str = output.encode('ascii', 'replace') 86 else: 87 self._output_str = output.decode('ascii', 'ignore') 88 _logger.debug("output ---->") 89 for line in self._output_str.splitlines(): 90 _logger.debug(line) 91 _logger.debug("<----") 92 self._setoutput(self._output_str)
93
94 - def _getoutput(self):
95 """ Returns the content of _output. """ 96 return self._output
97
98 - def __str__(self):
99 """ Synergy output log. """ 100 return self._output_str.encode('ascii', 'replace')
101 102 output = property(_getoutput, __setoutput)
103 104
105 -class ProjectCheckoutResult(Result):
106 """ Project checkout output parser. 107 Sets project to the created project or None if failed. 108 """
109 - def __init__(self, session, project):
110 Result.__init__(self, session) 111 self.__project = project 112 self.__result_project = None
113
114 - def _setoutput(self, output):
115 """ Parsing the output of the checkout command. """ 116 self._output = output 117 for line in output.splitlines(): 118 mresult = re.match(r"Saved work area options for project: '(.+)'", line, re.I) 119 #(?P<name>.+)-(?P<version>.+?)(:(?P<type>\S+):(?P<instance>\S+))? 120 if mresult != None: 121 #self.__project.name + "-" + mo.groupdict()['version'] + ":" + self.__project.type + ":" + self.__project.instance 122 self.__result_project = self._session.create(mresult.group(1)) 123 _logger.debug("ProjectCheckoutResult: project: '%s'" % self.__result_project) 124 return
125
126 - def __get_result_project(self):
127 """ return the checked out project. """ 128 return self.__result_project
129 130 project = property(__get_result_project)
131 132
133 -class ProjectPurposeResult(Result):
134 """ Parses purpose query output. """
135 - def __init__(self, session):
136 Result.__init__(self, session)
137
138 - def _setoutput(self, output):
139 self._output = {} 140 for line in output.splitlines(): 141 mresult = re.match(r"(?P<purpose>.+?)\s+(?P<member_status>\w+)\s+(?P<status>\w+)$", line) 142 if mresult != None: 143 data = mresult.groupdict() 144 if re.match(r'^\s+Purpose\s+Member$', data['purpose'], re.I) == None: 145 self._output[data['purpose'].strip()] = {'member_status' : data['member_status'].strip(), 146 'status' : data['status'].strip() 147 }
148
149 -class ConflictsResult(Result):
150 """ Parses purpose query output. """
151 - def __init__(self, session):
152 Result.__init__(self, session)
153
154 - def _setoutput(self, output):
155 self._output = {} 156 project = None 157 158 for line in output.splitlines(): 159 mresult = re.match(r"Project:\s*(.+)\s*$", line) 160 if mresult != None: 161 project = self._session.create(mresult.group(1)) 162 self._output[project] = [] 163 mresult = re.match(r"^(.*)\s+(\w+#\d+)\s+(.+)$", line) 164 if mresult != None and project != None: 165 self._output[project].append({'object': self._session.create(mresult.group(1)), 166 'task': self._session.create("Task %s" % mresult.group(2)), 167 'comment': mresult.group(3)}) 168 mresult = re.match(r"^(\w+#\d+)\s+(.+)$", line) 169 if mresult != None and project != None: 170 self._output[project].append({'task': self._session.create("Task %s" % mresult.group(1)), 171 'comment': mresult.group(2)})
172 173
174 -class FinduseResult(Result):
175 """ Parses finduse query output. """
176 - def __init__(self, ccm_object):
177 Result.__init__(self, ccm_object.session) 178 self.__object = ccm_object
179
180 - def _setoutput(self, output):
181 self._output = [] 182 for line in output.splitlines(): 183 _logger.debug("FinduseResult: ---->%s<----" % line) 184 _logger.debug("FinduseResult: ---->%s-%s<----" % (self.__object.name, self.__object.version)) 185 186 # MCNaviscroll\NaviAnim-wbernard7@MCNaviscroll-wbernard6 187 mresult = re.match(r"^\s*(?P<path>.+)[\\/]%s-%s@(?P<project>.+)" % (self.__object.name, self.__object.version), line, re.I) 188 if mresult != None: 189 data = mresult.groupdict() 190 _logger.debug("FinduseResult: %s" % data) 191 project = self._session.create(data['project']) 192 self._output.append({'path' : data['path'], 'project' : project})
193 194
195 -class UpdateTemplateInformation(Result):
196 """ Parse update template information output. """
197 - def __init__(self, session):
198 Result.__init__(self, session)
199
200 - def _setoutput(self, output):
201 """ 202 Baseline Selection Mode: Latest Baseline Projects 203 Prep Allowed: No 204 Versions Matching: *abs.50* 205 Release Purposes: 206 Use by Default: Yes 207 Modifiable in Database: tr1s60 208 In Use For Release: Yes 209 Folder Templates and Folders: 210 - Template assigned or completed tasks for %owner for release %release 211 - Template all completed tasks for release %release 212 - Folder tr1s60#4844: All completed Xuikon/Xuikon_rel_X tasks 213 - Folder tr1s60#4930: All tasks for release AppBaseDo_50 214 """ 215 self._output = {} 216 for line in output.splitlines(): 217 rmo = re.match(r"^\s*(.+):\s*(.*)\s*", line) 218 if rmo != None: 219 if rmo.group(1) == "Baseline Selection Mode": 220 self._output['baseline_selection_mode'] = rmo.group(2) 221 elif rmo.group(1) == "Prep Allowed": 222 self._output['prep_allowed'] = (rmo.group(2) != "No") 223 elif rmo.group(1) == "Versions Matching": 224 self._output['version_matching'] = rmo.group(2) 225 elif rmo.group(1) == "Release Purposes": 226 self._output['release_purpose'] = rmo.group(2) 227 elif rmo.group(1) == "Use by Default": 228 self._output['default'] = (rmo.group(2) != "No") 229 elif rmo.group(1) == "Modifiable in Database": 230 self._output['modifiable_in_database'] = rmo.group(2).strip() 231 elif rmo.group(1) == "In Use For Release": 232 self._output['in_use_for_release'] = (rmo.group(2) != "No")
233 234
235 -class UpdatePropertiesRefreshResult(Result):
236 """ Parse update template refresh output. """
237 - def __init__(self, session):
238 Result.__init__(self, session)
239
240 - def _setoutput(self, output):
241 self._output = {'added': [], 'removed': []} 242 match_added = re.compile(r"^Added the following tasks") 243 match_removed = re.compile(r"^Removed the following tasks") 244 match_task_new = re.compile(r"^\s+(Task \S+#\d+)") 245 section = None 246 247 for line in output.splitlines(): 248 res = match_added.match(line) 249 if res != None: 250 section = 'added' 251 continue 252 res = match_removed.match(line) 253 if res != None: 254 section = 'removed' 255 continue 256 if section is not None: 257 res = match_task_new.match(line) 258 if res != None: 259 self._output[section].append(self._session.create(res.group(1))) 260 continue
261 262
263 -class UpdateResultSimple(Result):
264 """ Parse update output. """
265 - def __init__(self, session):
266 Result.__init__(self, session) 267 self._success = True
268
269 - def _setoutput(self, output):
270 self._output = output 271 match_failed = re.compile(r"(Update failed)") 272 for line in output.splitlines(): 273 res = match_failed.match(line) 274 if res != None: 275 self._success = False
276 277 @property
278 - def successful(self):
279 return self._success
280
281 -class UpdateResult(UpdateResultSimple):
282 """ Parse update output. """
283 - def __init__(self, session):
285
286 - def _setoutput(self, output):
287 self._output = {"tasks":[], "modifications": [], "errors": [], "warnings": []} 288 match_object_update = re.compile(r"^\s+'(.*)'\s+replaces\s+'(.*)'\s+under\s+'(.*)'\.") 289 match_object_new = re.compile(r"^\s+(?:Subproject\s+)?'(.*)'\s+is now bound under\s+'(.*)'\.") 290 match_task_new = re.compile(r"^\s+(Task \S+#\d+)") 291 match_no_candidate = re.compile(r"^\s+(.+) in project (.+) had no candidates") 292 match_update_failure = re.compile(r"^\s+Failed to use selected object\s+(.+)\s+under directory\s+(.+)\s+in project\s+(.+)\s+:\s+(.+)") 293 match_warning = re.compile(r"^Warning:(.*)") 294 match_failed = re.compile(r"(Update failed)") 295 296 # TODO: cleanup the parsing to do that in a more efficient way. 297 for line in output.splitlines(): 298 _logger.info(line) 299 res = match_object_update.match(line) 300 if res != None: 301 self._output['modifications'].append({ "new": self._session.create(res.group(1)), 302 "old": self._session.create(res.group(2)), 303 "project": self._session.create(res.group(3)) 304 }) 305 continue 306 res = match_object_new.match(line) 307 if res != None: 308 self._output['modifications'].append({ "new": self._session.create(res.group(1)), 309 "old": None, 310 "project": self._session.create(res.group(2)) 311 }) 312 continue 313 res = match_task_new.match(line) 314 if res != None: 315 self._output['tasks'].append(self._session.create(res.group(1))) 316 continue 317 res = match_no_candidate.match(line) 318 if res != None: 319 self._output['errors'].append({'family': res.group(1), 320 'project': self._session.create(res.group(2)), 321 'comment': "had no candidates", 322 'line': line, 323 }) 324 continue 325 res = match_update_failure.match(line) 326 if res != None: 327 self._output['errors'].append({'family': res.group(1), 328 'dir': self._session.create(res.group(2)), 329 'project': self._session.create(res.group(3)), 330 'comment': res.group(4), 331 'line': line, 332 }) 333 continue 334 res = match_warning.match(line) 335 if res != None: 336 self._output['warnings'].append({'family': None, 337 'project': None, 338 'comment': res.group(1), 339 'line': line, 340 }) 341 continue 342 res = match_failed.match(line) 343 if res != None: 344 self._success = False 345 self._output['errors'].append({'Serious': res.group(1), 346 }) 347 continue
348 349 350
351 -class WorkAreaInfoResult(Result):
352 """ Parse work area info output. """
353 - def __init__(self, session):
354 Result.__init__(self, session)
355
356 - def _setoutput(self, output):
357 """ Returns a dict with the following fields: 358 * project: a ccm.Project instance 359 * maintain: a boolean 360 * copies: a boolean 361 * relative: a boolean 362 * time: a boolean 363 * translate: a boolean 364 * modify: a boolean 365 * path: a string representing the project wa path 366 """ 367 self._output = None 368 for line in output.splitlines(): 369 mresult = re.match(r"(?P<project>.*)\s+(?P<maintain>TRUE|FALSE)\s+(?P<copies>TRUE|FALSE)\s+(?P<relative>TRUE|FALSE)\s+(?P<time>TRUE|FALSE)\s+(?P<translate>TRUE|FALSE)\s+(?P<modify>TRUE|FALSE)\s+'(?P<path>.*)'", line) 370 if mresult != None: 371 data = mresult.groupdict() 372 self._output = {'project': self._session.create(data['project']), 373 'maintain' : data['maintain'] == "TRUE", 374 'copies' : data['copies'] == "TRUE", 375 'relative' : data['relative'] == "TRUE", 376 'time' : data['time'] == "TRUE", 377 'translate' : data['translate'] == "TRUE", 378 'modify' : data['modify'] == "TRUE", 379 'path' : data['path'] 380 } 381 return
382 383
384 -class CreateNewTaskResult(Result):
385
386 - def __init__(self, session):
387 Result.__init__(self, session)
388
389 - def _setoutput(self, output):
390 self._output = None 391 for line in output.splitlines(): 392 mresult = re.match(r"Task\s+(?P<task>\S+\#\d+)\s+created\.", line) 393 if mresult != None: 394 self._output = self._session.create("Task " + mresult.groupdict()['task']) 395 return
396 397
398 -class AttributeNameListResult(Result):
399 """ Class that abstract ccm call result handling. 400 Subclass it to implement a new generic output parser. 401 """
402 - def __init__(self, session):
403 Result.__init__(self, session)
404
405 - def _setoutput(self, obj):
406 def _create(arg): 407 mresult = re.match(r"^\s*(?P<name>\w+)", arg.strip()) 408 if mresult != None: 409 return mresult.groupdict()['name'] 410 return None
411 self._output = [_create(line) for line in obj.strip().splitlines()]
412 413
414 -class ObjectListResult(Result):
415 """ Parses an object list Synergy output. """
416 - def __init__(self, session):
417 Result.__init__(self, session)
418
419 - def _setoutput(self, obj):
420 self._output = [] 421 if re.match(r"^None|^No tasks|^Warning", obj, re.M) != None: 422 return 423 def _create(arg): 424 arg = arg.strip() 425 if arg != "": 426 return self._session.create(arg) 427 return None
428 result = [_create(line) for line in obj.strip().splitlines()] 429 for result_line in result: 430 if result_line != None: 431 self._output.append(result_line) 432
433 -class DataMapperListResult(Result):
434 """ Parses an object list Synergy output. """ 435 436 dataconv = {'ccmobject': lambda x, y: x.create(y), 437 'string': lambda x, y: y, 438 'int': lambda x, y: int(y), 439 'boolean': lambda x, y: (y.lower() == "true")} 440
441 - def __init__(self, session, separator, keywords, datamodel):
442 self._separator = separator 443 self._keywords = keywords 444 self._datamodel = datamodel 445 Result.__init__(self, session)
446
447 - def format(self):
448 formatted_keywords = ["%s%s%s%%%s" % (self._separator, x, self._separator, x) for x in self._keywords] 449 return "".join(formatted_keywords) + self._separator
450
451 - def regex(self):
452 regex_keywords = [r'%s%s%s(.*?)' % (self._separator, x, self._separator) for x in self._keywords] 453 regex = r''.join(regex_keywords) 454 regex = r"%s%s\s*\n" % (regex, self._separator) 455 return re.compile(regex, re.MULTILINE | re.I | re.DOTALL | re.VERBOSE | re.U)
456
457 - def _setoutput(self, obj):
458 self._output = [] 459 regex = self.regex() 460 _logger.debug("Regex %s" % (regex.pattern)) 461 for match in regex.finditer(obj): 462 _logger.debug("Found: %s" % (match)) 463 if match != None: 464 output_line = {} 465 for i in range(len(self._datamodel)): 466 _logger.debug("Found %d: %s" % (i, match.group(i + 1))) 467 model = self._datamodel[i] 468 output_line[self._keywords[i]] = self.dataconv[model](self._session, match.group(i + 1)) 469 i += 1 470 self._output.append(output_line)
471 472
473 -class FolderCopyResult(Result):
474 """ Parses a folder copy result """
475 - def __init__(self, session):
476 Result.__init__(self, session)
477
478 - def _setoutput(self, output):
479 self._output = None 480 for line in output.splitlines(): 481 mo = re.match(r"appended to", line) 482 if mo != None: 483 self._output = self._session.create(line) 484 return
485 486 CHECKOUT_LOG_RULES = [[r'^Derive failed for', logging.ERROR], 487 [r'^Serious:', logging.ERROR], 488 [r'^Warning: .* failed.', logging.ERROR], 489 [r'^Warning:', logging.WARNING],] 490 491 UPDATE_LOG_RULES = [[r'^Update failed.', logging.ERROR], 492 [r'^Serious:', logging.ERROR], 493 [r'^\s+Failed to', logging.ERROR], 494 [r'^\d+ failures to', logging.ERROR], 495 [r"^Warning: This work area '.+' cannot be reused", logging.ERROR], 496 [r'^Rebind of .* failed', logging.ERROR], 497 [r'^Warning: .* failed.', logging.ERROR], 498 [r'^Skipping \'.*\'\. You do not have permission to modify this project.', logging.ERROR], 499 [r'^Work area conflict exists for file', logging.ERROR], 500 [r'^Warning: No candidates found for directory entry', logging.ERROR], 501 [r'^Warning:', logging.WARNING],] 502 503 CONFLICTS_LOG_RULES = [[r'^\w+#\d+\s+Implicit', logging.WARNING], 504 [r'^(.*)\s+(\w+#\d+)\s+(.+)', logging.ERROR],] 505 506 SYNC_LOG_RULES = [[r'^\s+0\s+Conflict\(s\) for project', logging.INFO], 507 [r'^\s+\d+\s+Conflict\(s\) for project', logging.ERROR], 508 [r'^Project \'.*\' does not maintain a workarea.', logging.ERROR], 509 [r'^Work area conflict exists for file', logging.ERROR], 510 [r'^Warning: Conflicts detected during synchronization. Check your logs.', logging.ERROR], 511 [r'^Warning:', logging.WARNING],] 512
513 -def log_result(result, rules, logger=None):
514 """ Rules it a list of tuple defining a regular expression and an log level. """ 515 if logger is None: 516 logger = _logger 517 crules = [] 518 if rules is not None: 519 for rule in rules: 520 crules.append([re.compile(rule[0]), rule[1]]) 521 522 for line in str(result).splitlines(): 523 for rule in crules: 524 if rule[0].match(line) != None: 525 logger.log(rule[1], line) 526 break 527 else: 528 logger.info(line)
529
530 -class AbstractSession(object):
531 """An abstract Synergy session. 532 533 Must be overridden to implement either a single session or 534 multiple session handling. 535 """
536 - def __init__(self, username, engine, dbpath, ccm_addr):
537 self.username = username 538 self.engine = engine 539 self.dbpath = dbpath 540 self._session_addr = ccm_addr 541 # internal object list 542 self.__ccm_objects = {}
543
544 - def addr(self):
545 """ Returns the Synergy session id.""" 546 return self._session_addr
547
548 - def database(self):
549 _logger.debug("AbstractSession: database") 550 self.__find_dbpath() 551 _logger.debug("AbstractSession: database: %s" % self.dbpath) 552 return os.path.basename(self.dbpath)
553
554 - def __find_dbpath(self):
555 """ retrieve the database path from current session status. """ 556 _logger.debug("AbstractSession: __find_dbpath") 557 if (self.dbpath != None): 558 return 559 result = self.execute("status") 560 for match in re.finditer(r'(?:(?:Graphical)|(?:Command)) Interface\s+@\s+(?P<ccmaddr>\w+:\d+(?:\:\d+\.\d+\.\d+\.\d+)+)(?P<current_session>\s+\(current\s+session\))?\s*\nDatabase:\s*(?P<dbpath>\S+)', result.output, re.M | re.I): 561 d = match.groupdict() 562 if (d['current_session'] != None): 563 _logger.debug("AbstractSession: __find_dbpath: Found dbpath: %s" % d['dbpath']) 564 self.dbpath = d['dbpath'] 565 assert self.dbpath != None
566
567 - def execute(self, _, result=None):
568 """ Abstract function that should implement the execution of ccm command 569 line call. 570 """ 571 return result
572
573 - def create(self, fpn):
574 """ Object factory, this is the toolkit entry point to create objects from 575 four part names. Objects are stored into a dictionary, so you have 576 only one wrapper per synergy object. 577 """ 578 result = re.search(r"^(?P<project>.+)-(?P<version>[^:]+?)$", fpn) 579 if result != None: 580 matches = result.groupdict() 581 fpn = "%s-%s:project:%s#1" % (matches['project'], matches['version'], self.database()) 582 _logger.debug("session.create('%s')" % fpn) 583 ofpn = FourPartName(fpn) 584 if not self.__ccm_objects.has_key(str(fpn)): 585 obj = None 586 if ofpn.type == 'project': 587 obj = Project(self, fpn) 588 elif ofpn.type == 'dir': 589 obj = Dir(self, fpn) 590 elif ofpn.type == 'task': 591 obj = Task(self, fpn) 592 elif ofpn.type == 'folder': 593 obj = Folder(self, fpn) 594 elif ofpn.type == 'releasedef': 595 obj = Releasedef(self, fpn) 596 else: 597 obj = File(self, fpn) 598 self.__ccm_objects[str(fpn)] = obj 599 return self.__ccm_objects[str(fpn)]
600
601 - def get_workarea_info(self, dir_):
602 """ Return a dictionary containing workarea info from directory dir. 603 """ 604 if (not os.path.exists(dir_)): 605 raise CCMException("Error retrieving work_area info for the directory '%s' (doesn't exists)" % dir_) 606 path = os.path.abspath(os.path.curdir) 607 path_ccmwaid = os.path.join(dir_,"_ccmwaid.inf"); 608 if(not os.path.exists(path_ccmwaid)): 609 raise CCMException("No work area in '%s'" % dir_) 610 os.chdir(dir_) 611 result = self.execute("wa -show", WorkAreaInfoResult(self)) 612 os.chdir(path) 613 if result.output == None: 614 raise CCMException("Error retrieving work_area info for the directory '%s'" % dir_) 615 return result.output
616
617 - def _get_role(self):
618 result = self.execute("set role") 619 return result.output.strip()
620
621 - def _set_role_internal(self, role):
622 """ method to be override by child class else property accession is not working properly. """ 623 if len(role) == 0 or role == None: 624 raise CCMException("You must provide a role.") 625 result = self.execute("set role %s" % role) 626 if re.match(r'^Warning:', result.output, re.M) != None: 627 raise CCMException("Error switching to role %s: %s" %(role, result.output.strip()))
628
629 - def _set_role(self, role):
631 632 role = property(fget=_get_role, fset=_set_role) 633
634 - def _get_home(self):
635 result = self.execute("set Home") 636 return result.output.strip()
637
638 - def _set_home(self, home):
639 if len(home) == 0 or home == None: 640 raise CCMException("You must provide a home.") 641 result = self.execute("set Home %s" % home) 642 if re.match(r'^Warning:', result.output, re.M) != None: 643 raise CCMException("Error switching to Home %s: %s" %(home, result.output.strip()))
644 645 home = property(_get_home, _set_home) 646
647 - def close(self):
648 pass
649
650 - def __str__(self):
651 self.__find_dbpath() 652 return self._session_addr + ':' + self.dbpath
653
654 - def __repr__(self):
655 return self.__str__()
656
657 - def __del__(self):
658 self.close()
659
660 - def purposes(self, role=None):
661 """ Returns available purposes. """ 662 args = "" 663 if role != None: 664 args = "-role \"%s\"" % role 665 result = self.execute("project_purpose -show %s" % args, ProjectPurposeResult(self)) 666 return result.output
667
668 -class Session(AbstractSession):
669 """A Synergy session. 670 """
671 - def __init__(self, username, engine, dbpath, ccm_addr, close_on_exit=True):
672 AbstractSession.__init__(self, username, engine, dbpath, ccm_addr) 673 self._execute_lock = threading.Lock() 674 self.close_on_exit = close_on_exit
675 676 @staticmethod
677 - def start(username, password, engine, dbpath, timeout=300):
678 if username == None: 679 raise CCMException('username is not valid') 680 if password == None: 681 raise CCMException('password is not valid') 682 if CCM_BIN == None: 683 raise CCMException("Could not find CM/Synergy executable in the path.") 684 command = "%s start -m -q -nogui -n %s -pw %s -h %s -d %s" % \ 685 (CCM_BIN, username, password, engine, dbpath) 686 _logger.debug('Starting new session:' + command.replace(password, "***")) 687 (result, status) = _execute(command, timeout=timeout) 688 if status != 0: 689 raise Exception("Error creating a session: result:\n%s" % result) 690 session_addr = result.strip() 691 _logger.debug(session_addr) 692 if not re.match(r'\w+:\d+:\d+.\d+.\d+.\d+(:\d+.\d+.\d+.\d+)?', session_addr): 693 raise Exception("Error creating a session: result:\n%s" % result) 694 return Session(username, engine, dbpath, session_addr)
695
696 - def execute(self, cmdline, result=None):
697 """ Executes a Synergy CLI operation. """ 698 if self._session_addr == None: 699 raise CCMException("No Synergy session running") 700 if CCM_BIN == None: 701 raise CCMException("Could not find CM/Synergy executable in the path.") 702 self._execute_lock.acquire() 703 704 try: 705 if result == None: 706 result = Result(self) 707 if sys.platform == "win32": 708 command = "set CCM_ADDR=" + self._session_addr + " && " + CCM_BIN + " %s" % cmdline 709 else: 710 command = "export CCM_ADDR=" + self._session_addr + " && " + CCM_BIN + " %s" % cmdline 711 _logger.debug('Execute > ' + command) 712 713 process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 714 output = process.stdout.read() 715 result.status = process.returncode 716 finally: 717 self._execute_lock.release() 718 result.output = output.strip() 719 return result
720
721 - def close(self):
722 """ Closes this Synergy session if it was not previously running anyway. """ 723 _logger.debug("Closing session %s" % self._session_addr) 724 if self._session_addr != None and self.close_on_exit: 725 _logger.debug("Closing session %s" % self._session_addr) 726 self._execute_lock.acquire() 727 if sys.platform == "win32": 728 command = "set CCM_ADDR=" + self._session_addr + " && " + CCM_BIN + " stop" 729 else: 730 command = "export CCM_ADDR=" + self._session_addr + " && " + CCM_BIN + " stop" 731 _logger.debug('Execute > ' + command) 732 pipe = os.popen(command) 733 pipe.close() 734 self._session_addr = None 735 self._execute_lock.release() 736 elif self._session_addr != None and not self.close_on_exit: 737 _logger.debug("Keeping session %s alive." % self._session_addr)
738 739
740 -class SessionPool(AbstractSession):
741 """ Session that transparently handled several subsession, to easily enable 742 multithreaded application. 743 """
744 - def __init__(self, username, password, engine, dbpath, database=None, size=4, opener=None):
745 AbstractSession.__init__(self, username, engine, dbpath, None) 746 self._opener = opener 747 if self._opener is None: 748 self._opener = open_session 749 self._free_sessions = [] 750 self._used_sessions = [] 751 self._thread_sessions = {} 752 self._pool_lock = threading.Condition() 753 self._lock_pool = False 754 self.__password = password 755 self.__database = database 756 self.size = size
757
758 - def _set_size(self, size):
759 """ Set the pool size """ 760 self._pool_lock.acquire() 761 poolsize = len(self._free_sessions) + len(self._used_sessions) 762 if poolsize > size: 763 to_be_remove = poolsize - size 764 self._lock_pool = True 765 while len(self._free_sessions) < to_be_remove: 766 self._pool_lock.wait() 767 for _ in range(to_be_remove): 768 self._free_sessions.pop().close() 769 self._lock_pool = False 770 else: 771 for _ in range(size - poolsize): 772 self._free_sessions.append(self._opener(self.username, self.__password, self.engine, self.dbpath, self.__database, False)) 773 self._pool_lock.release()
774
775 - def _get_size(self):
776 self._pool_lock.acquire() 777 poolsize = len(self._free_sessions) + len(self._used_sessions) 778 self._pool_lock.release() 779 return poolsize
780 781 size = property (_get_size, _set_size) 782
783 - def execute(self, cmdline, result=None):
784 """ Executing a ccm command on a free session. """ 785 _logger.debug("SessionPool:execute: %s %s" % (cmdline, type(result))) 786 787 # waiting for a free session 788 self._pool_lock.acquire() 789 790 # check for recursion, in that case reallocate the same session, 791 if threading.currentThread() in self._thread_sessions: 792 _logger.debug("Same thread, reusing allocation session.") 793 # release the pool and reuse associated session 794 self._pool_lock.release() 795 return self._thread_sessions[threading.currentThread()].execute(cmdline, result) 796 797 while len(self._free_sessions)==0 or self._lock_pool: 798 self._pool_lock.wait() 799 session = self._free_sessions.pop(0) 800 self._used_sessions.append(session) 801 self._thread_sessions[threading.currentThread()] = session 802 self._pool_lock.release() 803 804 # running command 805 try: 806 result = session.execute(cmdline, result) 807 finally: 808 # we can now release the session - anyway 809 self._pool_lock.acquire() 810 self._thread_sessions.pop(threading.currentThread()) 811 self._used_sessions.remove(session) 812 self._free_sessions.append(session) 813 self._pool_lock.notifyAll() 814 self._pool_lock.release() 815 return result
816
817 - def close(self):
818 """ Closing all subsessions. """ 819 _logger.debug("Closing session pool sub-sessions") 820 self._lock_pool = True 821 self._pool_lock.acquire() 822 while len(self._used_sessions) > 0: 823 _logger.debug("Waiting to free used sessions.") 824 _logger.debug("Waiting to free used sessions. %s %s" % (len(self._used_sessions), len(self._free_sessions))) 825 _logger.debug(self._used_sessions) 826 _logger.debug(self._free_sessions) 827 self._pool_lock.wait() 828 _logger.debug("Closing all free session from the pool.") 829 while len(self._free_sessions) > 0: 830 self._free_sessions.pop().close() 831 self._lock_pool = False 832 self._pool_lock.notifyAll() 833 self._pool_lock.release()
834
835 - def _set_role_internal(self, role):
836 """ Set role on all subsessions. """ 837 self._lock_pool = True 838 self._pool_lock.acquire() 839 while len(self._used_sessions)!=0: 840 self._pool_lock.wait() 841 842 try: 843 for session in self._free_sessions: 844 session.role = role 845 finally: 846 self._lock_pool = False 847 self._pool_lock.notifyAll() 848 self._pool_lock.release()
849 850
851 -class Query(object):
852 """ This object wrap a synergy query, it takes a query as input as well as the 853 attribute you want as output, and get them translated using the model configuration. 854 e.g 855 Query(session, "type='task' and release='test/next'", ['objectname', 'task_synopsis'], ['ccmobject', 'string']) 856 857 This will return a list of hash: [{'objectname': Task(xxx), 'task_synopsis': 'xxx'}, ...] 858 """ 859
860 - def __init__(self, session, query, keywords, model, cmd="query"):
861 """ Initialize a Synergy query.""" 862 self._session = session 863 self._query = query 864 self._keywords = keywords 865 self._model = model 866 self._cmd = cmd
867
868 - def execute(self):
869 """ Executing the query on the database. """ 870 mapper = DataMapperListResult(self._session, '@@@', self._keywords, self._model) 871 query = "%s %s -u -f \"%s\"" % (self._cmd, self._query, mapper.format()) 872 return self._session.execute(query, mapper)
873 874 875
876 -class InvalidFourPartNameException(CCMException):
877 """ Badly formed Synergy four-part name. """
878 - def __init__(self, fpn = ""):
879 CCMException.__init__(self, fpn)
880 881
882 -class FourPartName(object):
883 """ This class handle four part name parsing and validation. 884 """ 885
886 - def __init__(self, ifpn):
887 """ Create a FourPartName object based on a ifpn string. 888 889 The string have to match the following patterns: 890 - name-version:type:instance 891 - name:version:releasedef:instance 892 - Task database#id 893 - Folder database#id 894 895 Anything else is considered as old release string format. 896 897 """ 898 _logger.debug("FourPartName: '%s'", ifpn) 899 fpn = FourPartName.convert(ifpn) 900 result = re.search(r"^(?P<name>.+)-(?P<version>.+?):(?P<type>\S+):(?P<instance>\S+)$", fpn) 901 if result == None: 902 result = re.search(r"^(?P<name>.+):(?P<version>.+?):(?P<type>releasedef):(?P<instance>\S+)$", fpn) 903 if result == None: 904 raise InvalidFourPartNameException(fpn) 905 # set all attributes 906 self._name = result.groupdict()['name'] 907 self._version = result.groupdict()['version'] 908 self._type = result.groupdict()['type'] 909 self._instance = result.groupdict()['instance']
910
911 - def __getname(self):
912 """ Returns the name of the object. """ 913 return self._name
914
915 - def __getversion(self):
916 """ Returns the version of the object. """ 917 return self._version
918
919 - def __gettype(self):
920 """ Returns the type of the object. """ 921 return self._type
922
923 - def __getinstance(self):
924 """ Returns the instance of the object. """ 925 return self._instance
926
927 - def __getobjectname(self):
928 """ Returns the objectname of the object. """ 929 if (self.type == 'releasedef'): 930 return "%s:%s:%s:%s" % (self.name, self.version, self.type, self.instance) 931 return "%s-%s:%s:%s" % (self.name, self.version, self.type, self.instance)
932
933 - def __str__(self):
934 """ Returns the string representation of the object. """ 935 return self.objectname
936
937 - def __repr__(self):
938 """ Returns the string representation of the python object. """ 939 if (self.type == 'releasedef'): 940 return "<%s:%s:%s:%s>" % (self.name, self.version, self.type, self.instance) 941 return "<%s-%s:%s:%s>" % (self.name, self.version, self.type, self.instance)
942
943 - def is_same_family(self, ccmobject):
944 """ Returns True if the ccmobject is part of the same family (=same name, type and version) as self. """ 945 assert isinstance(ccmobject, FourPartName) 946 return (self.name == ccmobject.name and self.type == ccmobject.type and self.instance == ccmobject.instance)
947
948 - def __getfamily(self):
949 return "%s:%s:%s" % (self.name, self.type, self.instance)
950
951 - def __eq__(self, ccmobject):
952 """ Returns True if object four parts name are identical. """ 953 if ccmobject == None: 954 return False 955 assert isinstance(ccmobject, FourPartName) 956 return (self.name == ccmobject.name and self.version == ccmobject.version and self.type == ccmobject.type and self.instance == ccmobject.instance)
957
958 - def __ne__(self, ccmobject):
959 """ Returns True if object four parts name are different. """ 960 if ccmobject == None: 961 return True 962 assert isinstance(ccmobject, FourPartName) 963 return (self.name != ccmobject.name or self.version != ccmobject.version or self.type != ccmobject.type or self.instance != ccmobject.instance)
964 965 @staticmethod
966 - def is_valid(fpn):
967 """ Check if a given string represents a valid four part name. 968 """ 969 return (re.match(r"^(.+)-(.+?):(\S+):(\S+)|(.+):(.+?):releasedef:(\S+)$", fpn) != None)
970 971 @staticmethod
972 - def convert(fpn):
973 """ Update a CCM output string to a valid four part name. This is due to the f$*@ing inconsistent 974 output of CM/Synergy that will probably never get fixed as it would require they hire humains 975 and not apes to core their CLI. 976 """ 977 fpn = fpn.strip() 978 if FourPartName.is_valid(fpn): 979 return fpn 980 result = re.search(r"^(?P<type>Task|Folder)\s+(?P<instance>\w+)#(?P<id>\d+)$", fpn) 981 if result != None: 982 matches = result.groupdict() 983 if matches["type"] == "Task": 984 return "task%s-1:task:%s" % (matches["id"], matches["instance"]) 985 elif matches["type"] == "Folder": 986 return "%s-1:folder:%s" % (matches['id'], matches['instance']) 987 else: 988 result = re.search(r"^(?P<project>\S+)/(?P<version>\S+)$", fpn) 989 if result != None: 990 matches = result.groupdict() 991 return "%s:%s:releasedef:1" % (matches['project'], matches['version']) 992 else: 993 # Check the name doesn't contains any of the following character: " :-" 994 result = re.search(r"^[^\s^:^-]+$", fpn) 995 if result != None: 996 return "none:%s:releasedef:1" % (fpn) 997 raise InvalidFourPartNameException(fpn)
998 999 name = property (__getname) 1000 version = property (__getversion) 1001 type = property (__gettype) 1002 instance = property (__getinstance) 1003 objectname = property (__getobjectname) 1004 family = property(__getfamily)
1005 1006
1007 -class CCMObject(FourPartName):
1008 """ Base class for any Synergy object. """ 1009
1010 - def __init__(self, session, fpn):
1011 FourPartName.__init__(self, fpn) 1012 self._session = session
1013
1014 - def _getsession(self):
1015 return self._session
1016 1017 session = property(_getsession) 1018
1019 - def exists(self):
1020 """ Check if an the object exists in the database. """ 1021 return (len(self._session.execute("query \"name='%s' and version='%s' and type='%s' and instance='%s'\" -u -f \"%%objectname\"" % (self.name, self.version, self.type, self.instance), ObjectListResult(self._session)).output) == 1)
1022
1023 - def __setitem__(self, name, value):
1024 project = "" 1025 if self.type == 'project': 1026 project = "-p" 1027 if value.endswith("\\"): 1028 value += "\\" 1029 result = self._session.execute("attribute -modify \"%s\" -v \"%s\" %s \"%s\"" % (name, value, project, self)) 1030 if result.status != 0 and result.status != None: 1031 raise CCMException("Error modifying '%s' attribute. Result: '%s'" % (name, result.output), result)
1032
1033 - def __getitem__(self, name):
1034 """ Provides access to Synergy object attributes through the dictionary 1035 item interface. 1036 1037 """ 1038 result = self._session.execute("query \"name='%s' and version='%s' and type='%s' and instance='%s'\" -u -f \"%%%s\"" % (self.name, self.version, self.type, self.instance, name)) 1039 if result.status != 0 and result.status != None: 1040 raise CCMException("Error retreiving '%s' attribute. Result: '%s'" % (name, result.output), result) 1041 if result.output.strip() == "<void>": 1042 return None 1043 return result.output.strip()
1044
1045 - def create_attribute(self, name, type_, value=None):
1046 if name in self.keys(): 1047 raise CCMException("Attribute '%s' already exist." % (name)) 1048 args = "" 1049 proj_arg = "" 1050 if value != None: 1051 args += " -value \"%s\"" % value 1052 if self.type == "project": 1053 proj_arg = "-p" 1054 result = self._session.execute("attribute -create \"%s\" -type \"%s\" %s %s \"%s\"" % (name, type_, args, proj_arg, self.objectname)) 1055 if result.status != 0 and result.status != None: 1056 raise CCMException("Error creating '%s' attribute. Result: '%s'" % (name, result.output), result)
1057
1058 - def keys(self):
1059 """ The list of supported Synergy attributes. """ 1060 result = self._session.execute("attribute -la \"%s\"" % self, AttributeNameListResult(self._session)) 1061 return result.output
1062
1063 - def is_predecessor_of(self, o):
1064 result = self._session.execute("query \"is_predecessor_of('%s') and name='%s'and version='%s'and type='%s'and instance='%s'\" -u -f \"%%objectname\"" % (o, self.name, self.version, self.type, self.instance), ObjectListResult(self._session)) 1065 if len(result.output): 1066 return True 1067 return False
1068
1069 - def predecessors(self):
1070 result = self._session.execute("query \"is_predecessor_of('%s')\" -u -f \"%%objectname\"" % self, ObjectListResult(self._session)) 1071 return result.output
1072
1073 - def successors(self):
1074 result = self._session.execute("query \"is_successor_of('%s')\" -u -f \"%%objectname\"" % self, ObjectListResult(self._session)) 1075 return result.output
1076
1077 - def is_recursive_predecessor_of(self, o):
1078 result = self._session.execute("query \"has_predecessor('%s')\" -u -f \"%%objectname\"" % self, ObjectListResult(self._session)) 1079 for s in result.output: 1080 if s == o: 1081 return True 1082 for s in result.output: 1083 if s.is_recursive_predecessor_of(o): 1084 return True 1085 return False
1086
1088 """ Fast implementation of the recursive is_predecessor_of method. """ 1089 input_objects = [self] 1090 while len(input_objects) > 0: 1091 query = " or ".join(["has_predecessor('%s')" % x for x in input_objects]) 1092 result = self._session.execute("query \"query\" -u -f \"%%objectname\"" % query, ObjectListResult(self._session)) 1093 for s in result.output: 1094 if s == o: 1095 return True 1096 return False
1097
1098 - def is_recursive_sucessor_of(self, o):
1099 result = self._session.execute("query \"has_successor('%s')\" -u -f \"%%objectname\"" % self, ObjectListResult(self._session)) 1100 for s in result.output: 1101 if s == o: 1102 return True 1103 for s in result.output: 1104 if s.is_recursive_sucessor_of(o): 1105 return True 1106 return False
1107
1108 - def is_recursive_successor_of_fast(self, o):
1109 """ Fast implementation of the recursive is_successor_of method. """ 1110 input_objects = [self] 1111 while len(input_objects) > 0: 1112 query = " or ".join(["has_successor('%s')" % x for x in input_objects]) 1113 result = self._session.execute("query \"query\" -u -f \"%%objectname\"" % query, ObjectListResult(self._session)) 1114 for s in result.output: 1115 if s == o: 1116 return True 1117 return False
1118
1119 - def relate(self, ccm_object):
1120 result = self._session.execute("relate -name successor -from \"%s\" -to \"%s\"" % self, ccm_object, Result(self._session)) 1121 if result.status != None and result.status != 0: 1122 raise CCMException("Error relating objects %s to %s\n%s" % (self, ccm_object, result.output))
1123
1124 - def finduse(self):
1125 """ Tries to find where an object is used. """ 1126 result = self._session.execute("finduse \"%s\"" % self, FinduseResult(self)) 1127 return result.output
1128 1129
1130 -class File(CCMObject):
1131 """ Wrapper for any Synergy file object """ 1132
1133 - def __init__(self, session, fpn):
1134 CCMObject.__init__(self, session, fpn)
1135
1136 - def content(self):
1137 result = self._session.execute("cat \"%s\"" % self) 1138 return result.output
1139
1140 - def to_file(self, path):
1141 if os.path.exists(path): 1142 _logger.error("Error file %s already exists" % path) 1143 if not os.path.exists(os.path.dirname(path)): 1144 os.makedirs(os.path.dirname(path)) 1145 # Content to file 1146 result = self._session.execute("cat \"%s\" > \"%s\"" % (self, os.path.normpath(path))) 1147 if result.status != 0 and result.status != None: 1148 raise CCMException("Error retrieving content from object %s in %s (error status: %s)\n%s" % (self, path, result.status, result.output), result)
1149
1150 - def merge(self, ccm_object, task):
1151 assert(ccm_object != None, "object must be defined.") 1152 assert(task != None, "task must be defined.") 1153 assert(task.type == "task", "task parameter must be of 'task' type.") 1154 result = self._session.execute("merge -task %s \"%s\" \"%s\"" % (task['displayname'], self, ccm_object)) 1155 1156 validity = 0 1157 for line in result.output.splitlines(): 1158 if re.match(r"Merge Source completed successfully\.", line): 1159 validity = 2 1160 elif re.match(r"Warning: Merge Source warning. \(overlaps during merge\)\.", line): 1161 validity = 1 1162 else: 1163 result = re.match(r"Associated object\s+(?P<object>.+)\s+with task", line) 1164 if result != None: 1165 return (self._session.create(result.groupdict()['object']), validity) 1166 1167 raise CCMException("Error during merge operation.\n" + result.output, result)
1168
1169 - def checkin(self, state, comment=None):
1170 if comment != None: 1171 comment = "-c \"%s\"" % comment 1172 else: 1173 comment = "-nc" 1174 result = self._session.execute("checkin -s \"%s\" %s \"%s\" " % (state, comment, self)) 1175 for line in result.output.splitlines(): 1176 _logger.debug(line) 1177 _logger.debug(r"Checked\s+in\s+'.+'\s+to\s+'%s'" % state) 1178 if re.match(r"Checked\s+in\s+'.+'\s+to\s+'%s'" % state, line) != None: 1179 return 1180 raise CCMException("Error checking in object %s,\n%s" % (self, result.output), result)
1181 1182
1183 -class Project(CCMObject):
1184 """ Wrapper class for Synergy project object. """ 1185
1186 - def __init__(self, session, fpn):
1187 CCMObject.__init__(self, session, fpn) 1188 self._release = None 1189 self._baseline = None
1190
1191 - def _gettasks(self):
1192 result = self._session.execute("rp -show tasks \"%s\" -u -f \"%%objectname\"" % self, ObjectListResult(self._session)) 1193 return result.output
1194
1195 - def add_task(self, task):
1196 """ Add a task to the update properties. """ 1197 result = self._session.execute("up -add -task %s \"%s\"" % (task['displayname'], self.objectname)) 1198 if result.status != None and result.status != 0: 1199 raise CCMException("Error adding task %s to project '%s'\n%s" % (task, self, result.output))
1200
1201 - def remove_task(self, task):
1202 """ Remove a task to the update properties. """ 1203 result = self._session.execute("up -remove -task %s \"%s\"" % (task['displayname'], self.objectname)) 1204 if result.status != None and result.status != 0: 1205 raise CCMException("Error removing task %s from project '%s'\n%s" % (task, self, result.output))
1206
1207 - def add_folder(self, folder):
1208 """ Add a folder to the update properties. """ 1209 result = self._session.execute("up -add -folder %s \"%s\"" % (folder['displayname'], self.objectname)) 1210 if result.status != None and result.status != 0: 1211 raise CCMException("Error adding folder %s to project '%s'\n%s" % (folder, self, result.output))
1212
1213 - def remove_folder(self, folder):
1214 """ Remove a folder to the update properties. """ 1215 result = self._session.execute("up -remove -folder %s \"%s\"" % (folder['displayname'], self.objectname)) 1216 if result.status != None and result.status != 0: 1217 raise CCMException("Error removing folder %s to project '%s'\n%s" % (folder, self, result.output))
1218
1219 - def _getfolders(self):
1220 """ Wrapper method to return the folder list from the update properties - please use the folders attribute to access it. """ 1221 result = self._session.execute("up -show folders \"%s\" -u -f \"%%objectname\"" % self, ObjectListResult(self._session)) 1222 return result.output
1223
1224 - def _getsubprojects(self):
1225 """ Wrapper method to return the subprojects list - please use the subprojects attribute to access it. """ 1226 result = self._session.execute("query -t project \"recursive_is_member_of('%s', none)\" -u -f \"%%objectname\"" % self.objectname, ObjectListResult(self._session)) 1227 return result.output
1228
1229 - def get_members(self, recursive=False, **kargs):
1230 query = "is_member_of('%s')" % self.objectname 1231 if recursive: 1232 query = "recursive_is_member_of('%s', none)" % self.objectname 1233 for k in kargs.keys(): 1234 query += " and %s='%s'" % (k, kargs[k]) 1235 result = self._session.execute("query \"%s\" -u -f \"%%objectname\"" % query, ObjectListResult(self._session)) 1236 return result.output
1237
1238 - def _getrelease(self):
1239 """ Get the release of the current object. Returns a Releasedef object. """ 1240 self._release = Releasedef(self._session, self['release']) 1241 return self._release
1242
1243 - def _setrelease(self, release):
1244 """ Set the release of the current object. """ 1245 self['release'] = release['displayname']
1246
1247 - def refresh(self):
1248 """ Refresh project update properties. """ 1249 result = self._session.execute("up -refresh \"%s\"" % self.objectname, UpdatePropertiesRefreshResult(self._session)) 1250 return result.output
1251
1252 - def _getbaseline(self):
1253 """ Get the baseline of the current project. """ 1254 if self._baseline == None: 1255 result = self._session.execute("up -show baseline_project \"%s\" -f \"%%displayname\" -u" % self.objectname) 1256 if result.output.strip().endswith('does not have a baseline project.'): 1257 return None 1258 self._baseline = self._session.create(result.output) 1259 _logger.debug('baseline: %s' % self._baseline) 1260 return self._baseline
1261
1262 - def set_baseline(self, baseline, recurse=False):
1263 """ Set project baseline. raise a CCMException in case or error. """ 1264 args = "" 1265 if recurse: 1266 args += " -r" 1267 self._baseline = None 1268 result = self._session.execute("up -mb \"%s\" %s \"%s\"" % (baseline, args, self.objectname)) 1269 if result.status != None and result.status != 0: 1270 raise CCMException("Error setting basline of project '%s'\n%s" % (self.objectname, result.output))
1271
1272 - def set_update_method(self, name, recurse = False):
1273 """ Set the update method for the project (and subproject if recurse is True). """ 1274 assert name != None, "name must not be None." 1275 assert len(name) > 0, "name must not be an empty string." 1276 args = "-ru %s" % name 1277 if recurse: 1278 args += " -r" 1279 result = self._session.execute("up %s \"%s\"" % (args, self)) 1280 if result.status != None and result.status != 0: 1281 raise CCMException("Error setting reconfigure properties to %s for project '%s'\nStatus: %s\n%s" % (name, self.objectname, result.status, result.output))
1282
1283 - def apply_update_properties(self, baseline = True, tasks_and_folders = True, recurse=True):
1284 """ Apply update properties to subprojects. """ 1285 args = "" 1286 if not baseline: 1287 args += "-no_baseline" 1288 if not tasks_and_folders: 1289 args += " -no_tasks_and_folders" 1290 if recurse: 1291 args += " -apply_to_subprojs" 1292 result = self._session.execute("rp %s \"%s\"" % (args, self.objectname)) 1293 if result.status != None and result.status != 0: 1294 raise CCMException("Error applying update properties to subprojects for '%s'\n%s" % (self.objectname, result.output))
1295
1296 - def root_dir(self):
1297 """ Return the directory attached to a project. """ 1298 result = self._session.execute("query \"is_child_of('%s','%s')\" -u -f \"%%objectname\"" % (self.objectname, self.objectname), ObjectListResult(self._session)) 1299 return result.output[0]
1300
1301 - def snapshot(self, targetdir, recursive=False):
1302 """ Take a snapshot of the project. """ 1303 assert(targetdir != None, "targetdir must be defined.") 1304 if recursive: 1305 recursive = "-recurse" 1306 else: 1307 recursive = "" 1308 result = self._session.execute("wa_snapshot -path \"%s\" %s \"%s\"" % (os.path.normpath(targetdir), recursive, self.objectname)) 1309 for line in result.output.splitlines(): 1310 if re.match(r"^Creation of snapshot work area complete.|Copying to file system complete\.\s*$", line): 1311 return result.output 1312 raise CCMException("Error creation snapshot of %s,\n%s" % (self.objectname, result.output), result)
1313
1314 - def checkout(self, release, version=None, purpose=None, subprojects=True):
1315 """ Create a checkout of this project. 1316 1317 This will only checkout the project in Synergy. It does not create a work area. 1318 1319 :param release: The Synergy release tag to use. 1320 :param version: The new version to use for the project. This is applied to all subprojects. 1321 :param purpose: The purpose of the checkout. Determines automatically the role from the purpose 1322 and switch it automatically (Could be any role from the DB). 1323 """ 1324 assert release != None, "Release object must be defined." 1325 if not release.exists(): 1326 raise CCMException("Release '%s' must exist in the database." % release) 1327 1328 args = '' 1329 if version != None: 1330 args += '-to "%s"' % version 1331 role = None 1332 if purpose: 1333 self._session.role = get_role_for_purpose(self._session, purpose) 1334 args += " -purpose \"%s\"" % purpose 1335 if subprojects: 1336 args += " -subprojects" 1337 result = self._session.execute("checkout -project \"%s\" -release \"%s\" -no_wa %s" \ 1338 % (self, release['displayname'], args), ProjectCheckoutResult(self._session, self.objectname)) 1339 if role: 1340 self._session.role = role 1341 if result.project == None: 1342 raise CCMException("Error checking out project %s,\n%s" % (self.objectname, result.output), result) 1343 return result
1344
1345 - def work_area(self, maintain, recursive=None, relative=None, path=None, pst=None, wat=False):
1346 """ Configure the work area. This allow to enable it or disable it, set the path, recursion... """ 1347 args = "" 1348 if maintain: 1349 args += "-wa" 1350 else: 1351 args += "-nwa" 1352 # path 1353 if path != None: 1354 args += " -path \"%s\"" % path 1355 # pst 1356 if pst != None: 1357 args += " -pst \"%s\"" % pst 1358 # relative 1359 if relative != None and relative: 1360 args += " -relative" 1361 elif relative != None and not relative: 1362 args += " -not_relative" 1363 # recursive 1364 if recursive != None and recursive: 1365 args += " -recurse" 1366 elif recursive != None and not recursive: 1367 args += " -no_recurse" 1368 #wat 1369 if wat: 1370 args += " -wat" 1371 result = self._session.execute("work_area -project \"%s\" %s" \ 1372 % (self.objectname, args), Result(self._session)) 1373 return result.output
1374
1375 - def update(self, recurse=True, replaceprojects=True, keepgoing=False, result=None):
1376 """ Update the project based on its reconfigure properties. """ 1377 args = "" 1378 if recurse: 1379 args += " -r " 1380 if replaceprojects: 1381 args += " -rs " 1382 else: 1383 args += " -ks " 1384 if result == None: 1385 result = UpdateResult(self._session) 1386 result = self._session.execute("update %s -project %s" % (args, self.objectname), result) 1387 if not result.successful and not keepgoing: 1388 raise CCMException("Error updating %s" % (self.objectname), result) 1389 return result
1390
1391 - def reconcile(self, updatewa=True, recurse=True, consideruncontrolled=True, missingwafile=True, report=True):
1392 """ Reconcile the project to force the work area to match the database. """ 1393 args = "" 1394 if updatewa: 1395 args += " -update_wa " 1396 if recurse: 1397 args += " -recurse " 1398 if consideruncontrolled: 1399 args += " -consider_uncontrolled " 1400 if missingwafile: 1401 args += " -missing_wa_file " 1402 if report: 1403 args += " -report reconcile.txt " 1404 result = self._session.execute("reconcile %s -project %s" % (args, self.objectname), Result(self._session)) 1405 if re.search(r"There are no conflicts in the Work Area", result.output) == None and re.search(r"Reconcile completed", result.output) == None: 1406 raise CCMException("Error reconciling %s,\n%s" % (self.objectname, result.output), result) 1407 return result.output
1408
1409 - def get_latest_baseline(self, filterstring="*", state="released"):
1410 result = self._session.execute("query -n %s -t project -f \"%%displayname\" -s %s -u -ns \"version smatch'%s'\"" % (self.name, state, filterstring)) 1411 lines = result.output.splitlines() 1412 return lines[-1]
1413
1414 - def create_baseline(self, baseline_name, release, baseline_tag, purpose="System Testing", state="published_baseline"):
1415 result = self._session.execute("baseline -create %s -release %s -purpose \"%s\" -vt %s -project \"%s\" -state \"%s\"" % (baseline_name, release, purpose, baseline_tag, self.objectname, state)) 1416 return result.output
1417
1418 - def sync(self, recurse=False, static=False):
1419 """ Synchronize project content. By default it is not been done recusively. (Not unittested)""" 1420 args = "" 1421 if recurse: 1422 args += " -recurse" 1423 if static: 1424 args += " -static" 1425 result = self._session.execute("sync %s -project \"%s\"" % (args, self.objectname)) 1426 if result.status != None and result.status != 0: 1427 raise CCMException("Error during synchronization of %s: %s." % (self.objectname, result.output)) 1428 return result.output
1429
1430 - def conflicts(self, recurse=False, tasks=False):
1431 args = "-noformat " 1432 if recurse: 1433 args += " -r" 1434 if tasks: 1435 args += " -t" 1436 1437 result = self._session.execute("conflicts %s \"%s\"" % (args, self.objectname), ConflictsResult(self._session)) 1438 if result.status != None and result.status != 0: 1439 raise CCMException("Error during conflict detection of %s: %s." % (self.objectname, result)) 1440 return result
1441 1442 tasks = property(_gettasks) 1443 folders = property(_getfolders) 1444 subprojects = property(_getsubprojects) 1445 release = property(_getrelease, _setrelease) 1446 baseline = property(_getbaseline, set_baseline)
1447 1448
1449 -class Dir(CCMObject):
1450 """ Wrapper class for Synergy dir object """ 1451
1452 - def __init__(self, session, fpn):
1453 CCMObject.__init__(self, session, fpn)
1454
1455 - def children(self, project):
1456 assert(project.type == 'project') 1457 result = self._session.execute("query \"is_child_of('%s','%s')\" -u -f \"%%objectname\"" % (self.objectname, project), ObjectListResult(self._session)) 1458 return result.output
1459 1460
1461 -class Releasedef(CCMObject):
1462 """ Wrapper class for Synergy releasedef object """ 1463
1464 - def __init__(self, session, fpn):
1465 CCMObject.__init__(self, session, fpn)
1466
1467 - def _getcomponent(self):
1468 return self.name
1469 1470 component = property(_getcomponent)
1471 1472
1473 -class Folder(CCMObject):
1474 """ Wrapper class for Synergy folder object """ 1475
1476 - def __init__(self, session, fpn):
1477 CCMObject.__init__(self, session, fpn)
1478
1479 - def _gettasks(self):
1480 """ Accessor for 'tasks' property. """ 1481 result = self._session.execute("folder -show tasks \"%s\" -u -f \"%%objectname\"" % self.objectname, ObjectListResult(self._session)) 1482 return result.output
1483
1484 - def _getobjects(self):
1485 result = self._session.execute("folder -show objects \"%s\" -u -f \"%%objectname\"" % self.objectname, ObjectListResult(self._session)) 1486 return result.output
1487
1488 - def _getmode(self):
1489 """ Get the mode used by the folder. """ 1490 result = self._session.execute("folder -show mode \"%s\"" % self.objectname) 1491 return result.output.strip()
1492
1493 - def _getquery(self):
1494 """ Get the query that populate the folder. """ 1495 if self.mode.lower() == "query": 1496 result = self._session.execute("folder -show query \"%s\"" % self.objectname) 1497 return result.output.strip() 1498 else: 1499 raise CCMException("%s is not a query base folder." % (self.objectname))
1500
1501 - def _getdescription(self):
1502 """ Get the description associated with the folder. """ 1503 r = self._session.execute("query -t folder -n %s -i %s -u -f \"%%description\"" % (self.name, self.instance)) 1504 return r.output.strip()
1505
1506 - def remove(self, task):
1507 """ Remove task from this folder. """ 1508 result = self._session.execute("folder -m \"%s\" -remove_task \"%s\"" % (self.objectname, task.objectname)) 1509 if result.status != None and result.status != 0: 1510 raise CCMException("Error removing task %s from %s: %s." % (task.objectname, self.objectname, result.output))
1511
1512 - def update(self):
1513 result = self._session.execute("folder -m -update -f \"%%objectname\"" % self.objectname) 1514 if result.status != None and result.status != 0: 1515 raise CCMException("Error updating the folder content %s: %s." % (self.objectname, result.output))
1516
1517 - def append(self, task):
1518 """ Associate an object to a task """ 1519 class AddTaskException(CCMException): 1520 def __init__(self, reason, task, result): 1521 CCMException.__init__(self, reason, result) 1522 self.task = task
1523 1524 result = self._session.execute("folder -m -at \"%s\" \"%s\"" % (task.objectname, self.objectname)) 1525 if re.search(r"(Added 1 task to)|(is already in folder)", result.output, re.M) is None: 1526 raise AddTaskException(result.output, result, task) 1527
1528 - def copy(self, existing_folder):
1529 """ Copy the contents of existing_folder into this folder. 1530 1531 This appends to the destination folder by default. 1532 1533 :param existing_folder: The destination Folder object. 1534 """ 1535 result = self._session.execute("folder -copy %s -existing %s -append" % (self.objectname, existing_folder), FolderCopyResult(self._session)) 1536 return result.output
1537 1538 objects = property(_getobjects) 1539 tasks = property(_gettasks) 1540 mode = property(_getmode) 1541 query = property(_getquery) 1542 is_query_based = property(lambda x: x.mode.lower() == "query") 1543 description = property(_getdescription) 1544 1545
1546 -class Task(CCMObject):
1547 """ Wrapper class for Synergy task object """ 1548
1549 - def __init__(self, session, fpn):
1550 CCMObject.__init__(self, session, fpn) 1551 self.__unicode_str_text = None
1552
1553 - def _getobjects(self):
1554 result = self._session.execute("task -show objects \"%s\" -u -f \"%%objectname\"" % self.objectname, ObjectListResult(self._session)) 1555 return result.output
1556
1557 - def append(self, ccm_object):
1558 """ Associate an object to a task """ 1559 class AddObjectException(CCMException): 1560 def __init__(self, comment, ccm_object): 1561 CCMException.__init__(self, comment) 1562 self.ccm_object = ccm_object
1563 1564 result = self._session.execute("task -associate \"%s\" -object \"%s\"" % (self.objectname, ccm_object.objectname)) 1565 if not re.match(r"Associated object .+ with task .*\.", result.output, re.M): 1566 raise AddObjectException(result.output) 1567
1568 - def assign(self, username):
1569 result = self._session.execute("task -modify \"%s\" -resolver %s" % (self.objectname, username)) 1570 if not re.match(r"Changed resolver of task", result.output, re.M): 1571 raise CCMException("Error assigning task to user '%s',\n%s" % (username, result.output), result)
1572
1573 - def _getsynopsis(self):
1574 return self['task_synopsis']
1575 1576 @staticmethod
1577 - def create(session, release_tag, synopsis=""):
1578 assert release_tag.type == "releasedef", "release_tag must be a CCM object wrapper of releasedef type" 1579 result = session.execute("task -create -synopsis \"%s\" -release \"%s\"" % (synopsis, release_tag['displayname']), CreateNewTaskResult(session)) 1580 return result.output
1581 1582 objects = property(_getobjects) 1583
1584 - def __unicode__(self):
1585 # TODO: use optimised query that makes only 1 ccm query with suitable format 1586 if self.__unicode_str_text == None: 1587 self.__unicode_str_text = u'%s: %s' % (self['displayname'], self['task_synopsis']) 1588 return self.__unicode_str_text
1589
1590 - def __str__(self):
1591 return self.__unicode__().encode('ascii', 'replace')
1592
1593 - def get_release_tag(self):
1594 """ Get task release. Use release property!""" 1595 result = self._session.execute("attribute -show release \"%s\"" % (self.objectname), Result(self._session)) 1596 return result.output
1597
1598 - def set_release_tag(self, release_tag):
1599 """ Set task release. Use release property!""" 1600 result = self._session.execute("attribute -modify release -value \"%s\" \"%s\"" % (release_tag, self.objectname), Result(self._session)) 1601 return result.output
1602 1603 release = property(get_release_tag, set_release_tag) 1604
1605 -class UpdateTemplate:
1606 """ Allow to access Update Template property using Release and Purpose. """
1607 - def __init__(self, releasedef, purpose):
1608 assert(releasedef != None) 1609 assert(purpose != None) 1610 self._releasedef = releasedef 1611 self._purpose = purpose
1612
1613 - def objectname(self):
1614 """ Return the objectname representing this virtual object. """ 1615 return "%s:%s" % (self._releasedef['displayname'], self._purpose)
1616
1617 - def baseline_projects(self):
1618 """ Query all projects for this UpdateTemplate. """ 1619 result = self._releasedef.session.execute("ut -sh baseline_projects \"%s\"" % self.objectname(), ObjectListResult(self._releasedef.session)) 1620 print result.output 1621 return result.output
1622
1623 - def information(self):
1624 """ Query all projects for this UpdateTemplate. """ 1625 result = self._releasedef.session.execute("ut -sh information \"%s\"" % self.objectname(), UpdateTemplateInformation(self._releasedef.session)) 1626 print result.output 1627 return result.output
1628
1629 - def baseline_selection_mode(self):
1630 """ The current Baseline selection mode """ 1631 result = self._releasedef.session.execute("ut -sh bsm \"%s\"" % self.objectname()) 1632 print result.output.strip() 1633 return result.output.strip()
1634 1635
1636 -def read_ccmwaid_info(filename):
1637 """ Read data from a ccmwaid file. This method is an helper to retreive a project from a physical location. """ 1638 ccmwaid = open(filename, 'r') 1639 # first line: database 1640 dbpath = os.path.dirname(ccmwaid.readline().strip()) 1641 database = os.path.basename(dbpath) 1642 # 2nd line should be a timestamp 1643 ccmwaid.readline().strip() 1644 # 3rd line is the objectname 1645 objectref = ccmwaid.readline().strip() 1646 ccmwaid.close() 1647 return {'dbpath': dbpath, 'database': database, 'objectname': objectref}
1648
1649 -def create_project_from_path(session, path):
1650 """ Uses the (_|.)ccmwaid.inf file to create a Project object. """ 1651 ccmwaid = ".ccmwaid.inf" 1652 if os.name == 'nt': 1653 ccmwaid = "_ccmwaid.inf" 1654 1655 if (not os.path.exists(path + "/" + ccmwaid)): 1656 return None 1657 result = read_ccmwaid_info(path + "/" + ccmwaid) 1658 1659 return session.create(result['objectname'])
1660 1661
1662 -def open_session(username=None, password=None, engine=None, dbpath=None, database=None, reuse=True):
1663 """Provides a Session object. 1664 1665 Attempts to return a Session, based either on existing Synergy 1666 sessions or by creating a new one. 1667 1668 - If a .netrc file can be found on the user's personal drive, 1669 that will be read to obtain Synergy login information if it 1670 is defined there. This will be used to fill in any missing 1671 parameters not passed in the call to open_session(). 1672 1673 The format of the .netrc file entries should be: 1674 1675 machine synergy login USERNAME password foobar account DATABASE_PATH@SERVER 1676 1677 If the details refer to a specific database, the machine can be the database name, 1678 instead of "synergy". 1679 - If an existing session is running that matches the supplied 1680 parameters, it will reuse that. 1681 1682 """ 1683 # See if a .netrc file can be used 1684 if CCM_BIN == None: 1685 raise CCMException("Could not find CM/Synergy executable in the path.") 1686 if password == None or username == None or engine == None or dbpath == None: 1687 if sys.platform == "win32": 1688 os.environ['HOME'] = "H:" + os.sep 1689 _logger.debug('Opening .netrc file') 1690 try: 1691 netrc_file = netrc.netrc() 1692 netrc_info = None 1693 # If settings for a specific database 1694 if database != None: 1695 netrc_info = netrc_file.authenticators(database) 1696 1697 # if not found just try generic one 1698 if netrc_info == None: 1699 netrc_info = netrc_file.authenticators('synergy') 1700 1701 if netrc_info != None: 1702 (n_username, n_account, n_password) = netrc_info 1703 if username == None: 1704 username = n_username 1705 if password == None: 1706 password = n_password 1707 if n_account != None: 1708 (n_dbpath, n_engine) = n_account.split('@') 1709 if dbpath == None and n_dbpath is not None: 1710 _logger.info('Database path set using .netrc (%s)' % n_dbpath) 1711 dbpath = n_dbpath 1712 if engine == None and n_engine is not None: 1713 _logger.info('Database engine set using .netrc (%s)' % n_engine) 1714 engine = n_engine 1715 except IOError: 1716 _logger.debug('Error accessing .netrc file') 1717 1718 # last chance... 1719 if username == None: 1720 username = os.environ['USERNAME'] 1721 1722 # looking for dbpath using GSCM database 1723 if dbpath == None and database != None: 1724 _logger.info('Database path set using the GSCM database.') 1725 dbpath = nokia.gscm.get_db_path(database) 1726 1727 # looking for engine host using GSCM database 1728 if engine == None and database != None: 1729 _logger.info('Database engine set using the GSCM database.') 1730 engine = nokia.gscm.get_engine_host(database) 1731 1732 _sessions = [] 1733 # See if any currently running sessions can be used, only if no password submitted, else use a brand new session! 1734 if password == None and reuse: 1735 _logger.debug('Querying for existing Synergy sessions') 1736 command = "%s status" % (CCM_BIN) 1737 pipe = os.popen(command, 'r') 1738 result = pipe.read() 1739 pipe.close() 1740 _logger.debug('ccm status result: ' + result) 1741 for match in re.finditer(r'(?P<ccmaddr>\w+:\d+:\d+.\d+.\d+.\d+(:\d+.\d+.\d+.\d+)?)(?P<current_session>\s+\(current\s+session\))?\nDatabase:\s*(?P<dbpath>\S+)', result, re.M): 1742 d = match.groupdict() 1743 _logger.debug(d['ccmaddr']) 1744 _logger.debug(os.environ['COMPUTERNAME']) 1745 _logger.debug(d['current_session']) 1746 if d['ccmaddr'].lower().startswith(os.environ['COMPUTERNAME'].lower()): 1747 # These session objects should not close the session on deletion, 1748 # because they did not initially create the session 1749 existing_session = Session(username, engine, d['dbpath'], d['ccmaddr'], close_on_exit=False) 1750 _logger.debug('Existing session found: %s' % existing_session) 1751 _sessions.append(existing_session) 1752 # looking for session using dbpath 1753 for session in _sessions: 1754 if session.dbpath == dbpath: 1755 return session 1756 else: 1757 # looking for router address using GSCM database 1758 router_address = None 1759 if database == None and dbpath != None: 1760 database = os.path.basename(dbpath) 1761 1762 lock = fileutils.Lock(CCM_SESSION_LOCK) 1763 try: 1764 lock.lock(wait=True) 1765 # if we have the database name we can switch to the correct Synergy router 1766 if database != None: 1767 _logger.info('Getting router address.') 1768 router_address = nokia.gscm.get_router_address(database) 1769 if sys.platform == "win32" and router_address != None: 1770 routerfile = open(os.path.join(os.path.dirname(CCM_BIN), "../etc/_router.adr"), 'r') 1771 current_router = routerfile.read().strip() 1772 routerfile.close() 1773 if current_router != router_address.strip(): 1774 _logger.info('Updating %s' % (os.path.normpath(os.path.join(os.path.dirname(CCM_BIN), "../etc/_router.adr")))) 1775 routerfile = open(os.path.join(os.path.dirname(CCM_BIN), "../etc/_router.adr"), "w+") 1776 routerfile.write("%s\n" % router_address) 1777 routerfile.close() 1778 1779 # If no existing sessions were available, start a new one 1780 _logger.info('Opening session.') 1781 new_session = Session.start(username, password, engine, dbpath) 1782 lock.unlock() 1783 return new_session 1784 finally: 1785 lock.unlock() 1786 raise CCMException("Cannot open session for user '%s'" % username)
1787 1788
1789 -def get_role_for_purpose(session, purpose):
1790 """ return role needed to modify project with checkout for purpose. """ 1791 purposes = session.purposes() 1792 if purpose in purposes: 1793 if purposes[purpose]['status'] == 'prep': 1794 return 'build_mgr' 1795 else: 1796 raise CCMException("Could not find purpose '%s' in the database.\n Valid purpose are: %s." % (purpose, ','.join(purposes.keys()))) 1797 return 'developer'
1798
1799 -def get_role_for_status(session, status):
1800 """ return role needed to modify project with a specific status. """ 1801 if status == 'prep': 1802 return 'build_mgr' 1803 elif status == 'working': 1804 return 'developer' 1805 else: 1806 raise CCMException("Unknow status '%s'" % status)
1807
1808 -def running_sessions(database=None):
1809 """ Return the list of synergy session currently available on the local machine. 1810 If database is given then it tries to update the router address. 1811 """ 1812 _logger.debug('Querying for existing Synergy sessions') 1813 if CCM_BIN == None: 1814 raise CCMException("Could not find CM/Synergy executable in the path.") 1815 command = "%s status" % (CCM_BIN) 1816 1817 lock = fileutils.Lock(CCM_SESSION_LOCK) 1818 result = "" 1819 output = [] 1820 try: 1821 # if we have the database name we can switch to the correct Synergy router 1822 if database != None: 1823 lock.lock(wait=True) 1824 _logger.info('Updating router address.') 1825 router_address = nokia.gscm.get_router_address(database) 1826 if sys.platform == "win32" and router_address != None: 1827 routerfile = open(os.path.join(os.path.dirname(CCM_BIN), "../etc/_router.adr"), 'r') 1828 current_router = routerfile.read().strip() 1829 routerfile.close() 1830 if current_router != router_address.strip(): 1831 _logger.info('Updating %s' % (os.path.normpath(os.path.join(os.path.dirname(CCM_BIN), "../etc/_router.adr")))) 1832 routerfile = open(os.path.join(os.path.dirname(CCM_BIN), "../etc/_router.adr"), "w+") 1833 routerfile.write("%s\n" % router_address) 1834 routerfile.close() 1835 1836 _logger.debug('Command: ' + command) 1837 (result, status) = _execute(command) 1838 if database != None: 1839 lock.unlock() 1840 if (status != 0): 1841 raise CCMException("Ccm status execution returned an error.") 1842 _logger.debug('ccm status result: ' + result) 1843 for match in re.finditer(r'Command Interface\s+@\s+(?P<ccmaddr>\w+:\d+:\d+.\d+.\d+.\d+(:\d+.\d+.\d+.\d+)*)(?P<current_session>\s+\(current\s+session\))?\s+Database:\s*(?P<dbpath>\S+)', result, re.M): 1844 data = match.groupdict() 1845 _logger.debug(data['ccmaddr']) 1846 _logger.debug(os.environ['COMPUTERNAME']) 1847 _logger.debug(data['current_session']) 1848 if data['ccmaddr'].lower().startswith(os.environ['COMPUTERNAME'].lower()): 1849 # These session objects should not close the session on deletion, 1850 # because they did not initially create the session 1851 existing_session = Session(None, None, data['dbpath'], data['ccmaddr'], close_on_exit=False) 1852 _logger.debug('Existing session found: %s' % existing_session) 1853 output.append(existing_session) 1854 finally: 1855 if database != None: 1856 lock.unlock() 1857 return output
1858
1859 -def session_exists(sessionid, database=None):
1860 for session in running_sessions(database=database): 1861 _logger.debug(session.addr() + "==" + sessionid + "?") 1862 if session.addr() == sessionid: 1863 return True 1864 return False
1865 1866 # The location of the ccm binary must be located to know where the _router.adr file is, to support 1867 # switching databases. 1868 CCM_BIN = fileutils.which("ccm") 1869 if sys.platform == "win32": 1870 CCM_BIN = fileutils.which("ccm.exe") 1871