00001 """CMS TagCollector Python API 00002 """ 00003 00004 __author__ = "Miguel Ojeda" 00005 __copyright__ = "Copyright 2010-2011, CERN CMS" 00006 __credits__ = ["Miguel Ojeda"] 00007 __license__ = "Unknown" 00008 __maintainer__ = "Miguel Ojeda" 00009 __email__ = "mojedasa@cern.ch" 00010 __status__ = "Staging" 00011 00012 _tagcollector_url = 'https://cmssdt.cern.ch/tc/' 00013 00014 import urllib 00015 import urllib2 00016 import cookielib 00017 try: 00018 import json 00019 except ImportError: 00020 import simplejson as json 00021 import getpass 00022 00023 class TagCollector(object): 00024 """CMS TagCollector Python API""" 00025 00026 def __init__(self): 00027 self._url = _tagcollector_url 00028 self._cj = cookielib.CookieJar() 00029 self._opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._cj)) 00030 self.login = False 00031 00032 def __del__(self): 00033 if self.login: self.signOut() 00034 00035 def _open(self, page, params = None, data = None): 00036 url = self._url + page + '?' 00037 if params: 00038 url += urllib.urlencode(params) 00039 if data: 00040 data = urllib.urlencode(data) 00041 try: 00042 return self._opener.open(url, data).read() 00043 except urllib2.HTTPError, e: 00044 raise Exception(e.read().strip()) 00045 00046 def _openjson(self, page, params = None, data = None): 00047 return json.loads(self._open(page, params, data)) 00048 00049 def signIn(self, username, password): 00050 """Sign in to TagCollector.""" 00051 self._open('signIn', data = {'password': password, 'user_name': username}) 00052 self.login = True 00053 00054 def signInInteractive(self): 00055 """Sign in to TagCollector, asking for the username and password.""" 00056 username = raw_input('Username: ') 00057 password = getpass.getpass() 00058 self.signIn(username, password) 00059 return username 00060 00061 def signOut(self): 00062 """Sign out of TagCollector.""" 00063 self._open('signOut') 00064 self.login = False 00065 00066 def getPackageTags(self, package): 00067 """Get the tags published in TagCollector for a package. 00068 Note: TagCollector's published tags are a subset of CVS' tags.""" 00069 return self._openjson('py_getPackageTags', {'package': package}) 00070 00071 def getPackageTagDescriptionFirstLine(self, package, tag): 00072 """Get the first line of the descriptions of a tag.""" 00073 return self._openjson('py_getPackageTagDescriptionFirstLine', {'package': package, 'tag': tag}) 00074 00075 def getPackageTagReleases(self, package, tag): 00076 """Get the releases where a tag is.""" 00077 return self._openjson('py_getPackageTagReleases', {'package': package, 'tag': tag}) 00078 00079 def getReleasesTags(self, releases, diff = False): 00080 """Get the tags of one or more release. 00081 Optionally, return only the tags that differ between releases.""" 00082 releases = json.dumps(releases) 00083 diff = json.dumps(diff) 00084 return self._openjson('py_getReleasesTags', {'releases': releases, 'diff': diff}) 00085 00086 def getReleaseTags(self, release): 00087 """Get the tags of one release.""" 00088 return self.getReleasesTags((release, )) 00089 00090 def getTagsetTags(self, tagset): 00091 """Get the tags of one tagset.""" 00092 return self._openjson('py_getTagsetTags', {'tagset': tagset}) 00093 00094 def getTagsetInformation(self, tagset): 00095 """Get the information of one tagset.""" 00096 return self._openjson('py_getTagsetInformation', {'tagset': tagset}) 00097 00098 def getPendingApprovalTags(self, args, allow_multiple_tags = False): 00099 """Prints Pending Approval tags of one or more releases, 00100 one or more tagsets, or both (i.e. it joins all the tags). 00101 Prints an error if several tags appear for a single package. 00102 Suitable for piping to addpkg (note: at the moment, 00103 addpkg does not read from stdin, use "-f" instead).""" 00104 args = json.dumps(args) 00105 allow_multiple_tags = json.dumps(allow_multiple_tags) 00106 return self._openjson('py_getPendingApprovalTags', {'args': args, 'allow_multiple_tags': allow_multiple_tags}) 00107 00108 def getTagsetsTagsPendingSignatures(self, user_name, show_all, author_tagsets, release_names = None): 00109 """Prints Pending Signature tags of one or more releases, 00110 one or more tagsets, or both (i.e. it joins all the tags). 00111 Prints an error if several tags appear for a single package. 00112 Suitable for piping to addpkg (note: at the moment, 00113 addpkg does not read from stdin, use "-f" instead).""" 00114 if not release_names == None: 00115 return self._openjson('py_getTagsetsTagsPendingSignatures', {'user_name': user_name, 'show_all': show_all, 'author_tagsets': author_tagsets, 'release_names': json.dumps(release_names)}) 00116 else: 00117 return self._openjson('py_getTagsetsTagsPendingSignatures', {'user_name': user_name, 'show_all': show_all, 'author_tagsets': author_tagsets}) 00118 00119 def commentTagsets(self, tagset_ids, comment): 00120 """Comment one or more tagsets. 00121 Requirement: Signed in.""" 00122 tagset_ids = json.dumps(tagset_ids) 00123 if len(comment) < 1: 00124 raise Exception("Error: Expected a comment.") 00125 self._open('commentTagsets', {'tagset_ids': tagset_ids, 'comment': comment}) 00126 00127 def signTagsets(self, tagset_ids, comment = ''): 00128 """Sign one or more tagsets. 00129 Requirement: Signed in as a L2.""" 00130 tagset_ids = json.dumps(tagset_ids) 00131 self._open('signTagsets', {'tagset_ids': tagset_ids, 'comment': comment}) 00132 00133 def signTagsetsAll(self, tagset_ids, comment = ''): 00134 """Sign all one or more tagsets. 00135 Requirement: Signed in as a top-level admin.""" 00136 tagset_ids = json.dumps(tagset_ids) 00137 self._open('signTagsetsAll', {'tagset_ids': tagset_ids, 'comment': comment}) 00138 00139 def rejectTagsetsPendingSignatures(self, tagset_ids, comment = ''): 00140 """Reject one or more tagsets Pending Signatures. 00141 Requirement: Signed in as a L2s or as a Release Manager 00142 for the tagset's release or as the author of the tagset.""" 00143 tagset_ids = json.dumps(tagset_ids) 00144 self._open('rejectTagsetsPendingSignatures', {'tagset_ids': tagset_ids, 'comment': comment}) 00145 00146 def approveTagsets(self, tagset_ids, comment = ''): 00147 """Approve one or more tagsets. 00148 Requirement: Signed in as a Release Manager for each tagset's release.""" 00149 tagset_ids = json.dumps(tagset_ids) 00150 self._open('approveTagsets', {'tagset_ids': tagset_ids, 'comment': comment}) 00151 00152 def bypassTagsets(self, tagset_ids, comment = ''): 00153 """Bypass one or more tagsets. 00154 Requirement: Signed in as a Release Manager for each tagset's release.""" 00155 tagset_ids = json.dumps(tagset_ids) 00156 self._open('bypassTagsets', {'tagset_ids': tagset_ids, 'comment': comment}) 00157 00158 def rejectTagsetsPendingApproval(self, tagset_ids, comment = ''): 00159 """Reject one or more tagsets Pending Approval. 00160 Requirement: Signed in as a Release Manager.""" 00161 tagset_ids = json.dumps(tagset_ids) 00162 self._open('rejectTagsetsPendingApproval', {'tagset_ids': tagset_ids, 'comment': comment}) 00163 00164 def removeTagsets(self, tagset_ids, comment = ''): 00165 """Remove one or more tagsets from the History (i.e. stack of the release). 00166 Requirement: Signed in as a Release Manager.""" 00167 tagset_ids = json.dumps(tagset_ids) 00168 self._open('removeTagsets', {'tagset_ids': tagset_ids, 'comment': comment}) 00169 00170 def getPackagesPendingApproval(self): 00171 """Get New Package Requests which are Pending Approval.""" 00172 return self._openjson('py_getPackagesPendingApproval') 00173 00174 def getPackageManagersRequested(self, package): 00175 """Get the Package Managers (administrators and developers) requested in a New Package Request.""" 00176 return self._openjson('py_getPackageManagersRequested', {'package': package}) 00177 00178 def search(self, term): 00179 """Searches for releases, packages, tagsets, users and categories. 00180 Requirement: Signed in.""" 00181 return self._openjson('search', {'term': term}) 00182 00183 def approvePackage(self, package): 00184 """Approve a New Package Request. 00185 Requirement: Signed in as a Creator (i.e. people in the top-level .admin/developers file). 00186 Warning: This does *not* create the package in CVS.""" 00187 self._open('approveNewPackageRequest', {'package_name': package}) 00188 00189 def getIBs(self, filt = '', limit = 10): 00190 """Get the name and creation date of Integration Builds. 00191 By default, it only returns the latest 10 IBs. 00192 Optionally, filter by name.""" 00193 return self._openjson('py_getIBs', {'filt': filt, 'limit': limit}) 00194 00195 def createRelease(self, base_release_name, new_release_name, new_state, new_private, new_type, new_description, release_managers, copy_queues, tags): 00196 """Create a new release. 00197 Requirement: Signed in as a release manager.""" 00198 if self.login: 00199 self._open('copyRelease', {'release_name': base_release_name, 'new_release_name': new_release_name, 'new_state': new_state, 'new_private': new_private, 'new_type': new_type, 'new_description': new_description, 'release_managers': release_managers, 'copy_queues': copy_queues, 'tags': tags}) 00200 else: 00201 raise Exception("Error: Not logged in?!") 00202 00203 def requestCustomIB(self, release_name, architectures, tags): 00204 """Request a CustomIB. 00205 Requirement: Signed in.""" 00206 if self.login: 00207 self._open('requestCustomIB', {'release_name': release_name, 'architecture_names': architectures, 'tags': tags}) 00208 else: 00209 raise Exception("Error: Not logged in?!") 00210 00211 def getReleaseArchitectures(self, release, default='0'): 00212 """Returns release architectures.""" 00213 return self._openjson('py_getReleaseArchitectures', {'release': release, 'default': default})