diff -r 7685cec9fd3c -r f2ddfa555b0f doc/api/python/ccm-pysrc.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/api/python/ccm-pysrc.html Fri Sep 11 11:54:49 2009 +0100 @@ -0,0 +1,10533 @@ + + + + + ccm + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + 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 +
1087 - def is_recursive_predecessor_of_fast(self, o): +
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 +
+
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + +