1#!/usr/local/bin/python3.5
2
3###################################################################################################
4#
5#  pyral.config - config and "consts" for the Rally 'pyral' package for REST API operations
6#
7###################################################################################################
8
9__version__ = (1, 5, 2)
10
11import datetime
12import os
13import platform
14import re
15import glob
16
17###################################################################################################
18
19PROTOCOL       = "https"
20SERVER         = "rally1.rallydev.com"
21WEB_SERVICE    = "slm/webservice/%s"
22SCHEMA_SERVICE = "slm/schema/%s"
23AUTH_ENDPOINT  = "security/authorize"
24WS_API_VERSION = "v2.0"
25
26USER_NAME = "wiley@acme.com"
27PASSWORD  = "G3ronim0!"
28
29START_INDEX  =   1
30MAX_PAGESIZE = 500
31MAX_ITEMS    = 1000000  # a million seems an eminently reasonable limit ...
32
33RALLY_REST_HEADERS = \
34    {
35      #'X-RallyIntegrationName'     : 'Python toolkit for Rally REST API', # although syntactically this is the more correct
36      'X-RallyIntegrationName'     : 'Rally REST API toolkit for Python',  # this matches the format of the other language toolkits
37      'X-RallyIntegrationVendor'   : 'Broadcom / Rally',
38      'X-RallyIntegrationVersion'  :       '%s.%s.%s' % __version__,
39      'X-RallyIntegrationLibrary'  : 'pyral-%s.%s.%s' % __version__,
40      'X-RallyIntegrationPlatform' : 'Python %s' % platform.python_version(),
41      'X-RallyIntegrationOS'       : platform.platform(),
42      'User-Agent'                 : 'Pyral Rally WebServices Agent',
43      'Content-Type'               : 'application/json',
44      'Accept-Encoding'            : 'gzip'
45    }
46
47##################################################################################################
48
49def timestamp():
50    # for now, don't worry about timezone fluff, and cut off the microseconds to become millis
51    return  datetime.datetime.today().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
52
53##################################################################################################
54
55CONFIG_SETTING_PATT     = re.compile(r'^([A-Z]+)\s*=\s*(.+)$')
56RALLY_ARG_SETTING_PATT1 = re.compile(r'^--(rally[SUPW][a-z]+)=(.+)\s*$')
57RALLY_ARG_SETTING_PATT2 = re.compile(r'^--([ASUPWasupw][a-z]+)=(.+)\s*$')
58RALLY_CONFIG_FILE_PATT  = re.compile(r'^--(cfg|conf|config|rallyConfig)=(\S+)$')
59
60TRUTHY_VALUES = ['t', 'true',  'y', 'yes', '1']
61FALSEY_VALUES = ['f', 'false', 'n', 'no',  '0']
62
63################################################################################
64
65def rallyWorkset(args):
66    """
67        intended to supplant rallySettings as of pyral 2.0.x
68
69        priority order of Python Rally REST API server ident, credentials, workspace/project:
70          1) command line args with --rallyServer, --rallyUser, --rallyPassword, --apikey,  --workspace, --project, --ping
71          2) command line arg specifying a config file --rallyConfig=<config_file_name>
72                                                    or --config=<config_file_name>
73                                                    or --conf=<config_file_name>
74                                                    or --cfg=<config_file_name>
75          3) ENV variable with location of rally-<version>.cfg --> RALLY_CONFIG
76          4) current directory with rally-<version>.cfg
77          5) RALLY_SERVER, RALLY_USER_NAME, RALLY_PASSWORD, APIKEY, RALLY_WORKSPACE, RALLY_PROJECT env VARS
78          6) SERVER, USER_NAME, PASSWORD defined in this module
79
80        start by priming the return values with #6 and work your way up the priority ladder
81    """
82    # #6
83    # start with the defaults defined in this module
84    server_creds = [SERVER, USER_NAME, PASSWORD, "", "default", "default"]
85
86    def snarfSettings(targetFile, server_creds):
87        """
88            read the filename and look for lines containing relevant Rally settings.
89            alter the server_creds list if there are entries in the file to do so.
90        """
91        if not os.path.exists(targetFile):
92            cfg_suffixed = "%s.cfg" % targetFile
93            if not os.path.exists(cfg_suffixed):
94                return server_creds
95            else:
96                targetFile = cfg_suffixed
97
98        try:
99            cf = open(targetFile, 'r')
100            for line in cf:
101                mo = CONFIG_SETTING_PATT.match(line)
102                if mo:
103                    item, value = mo.groups()
104                    if   item == 'SERVER':
105                        server_creds[0] = value
106                    elif item == 'USER':
107                        server_creds[1] = value
108                    elif item == 'PASSWORD':
109                        server_creds[2] = value
110                    elif item == "APIKEY" or item == "API_KEY":
111                        server_creds[3] = value
112                    elif item == 'WORKSPACE':
113                        server_creds[4] = value
114                    elif item == 'PROJECT':
115                        server_creds[5] = value
116            cf.close()
117            sc = "%s, %s, %s, %s, %s, %s" % tuple(server_creds)
118            return server_creds
119        except Exception as ex:
120            pass
121
122    # #5
123    # if there are environment vars, use them
124    #
125    for ix, name in enumerate(['RALLY_SERVER', 'RALLY_USER', 'RALLY_PASSWORD', 'APIKEY', 'RALLY_WORKSPACE', 'RALLY_PROJECT']):
126        if name in os.environ:
127            server_creds[ix] = os.environ[name]
128
129    # #4
130    # if there is a rally-<version>.cfg file in the current directory matching the WS_API_VERSION
131    # load with contents of that file
132    entries = glob.glob('rally-*.cfg')
133    target_version_config = 'rally-%s.cfg' % WS_API_VERSION
134    if entries:
135        if target_version_config in entries:
136            server_creds = snarfSettings(target_version_config, server_creds)
137        else:
138            print("Ignoring non-matching version of Rally config settings: %s (working version: %s)" % \
139                  (entries.pop(), WS_API_VERSION))
140
141    # #3
142    # if there is a RALLY_CONFIG environment variable pointing to a file, load with contents of file
143    config_file = os.environ.get('RALLY_CONFIG', None)
144    if config_file:
145        server_creds = snarfSettings(config_file, server_creds)
146
147    # #2
148    # now look at the args (from command line invocation)
149    # grab any --(rallyConfig|config|conf|cfg)=<filename> args,
150    # and if filename exists attempt to load with contents therein
151    for arg in args:
152        mo = RALLY_CONFIG_FILE_PATT.match(arg)
153        if mo:
154            config_token, config_file = mo.groups()
155            server_creds = snarfSettings(config_file, server_creds)
156
157    # #1
158    # now look at the args (from command line invocation)
159    # grab any --rallyServer=?, --rallyUser=?, --rallyPassword=?, --rallyWorkspace=?, --rallyProject=? in args
160    # grab any --server=?, --user=?, --password=?, --apikey=?, --workspace=?, --project=? --ping=?in args
161    for arg in args:
162        mo = RALLY_ARG_SETTING_PATT1.match(arg)
163        if mo:
164            item, value = mo.groups()
165            if   item == 'rallyServer':
166                server_creds[0] = value
167            elif item == 'rallyUser':
168                server_creds[1] = value
169            elif item == 'rallyPassword':
170                server_creds[2] = value
171            #elif item = 'rallyApikey':   # enable this if we ever decide that apikey arg should ever be specified as --rallyApikey
172            #    server_creds[3] = value
173            elif item == 'rallyWorkspace':
174                server_creds[4] = value
175            elif item == 'rallyProject':
176                server_creds[5] = value
177
178        mo = RALLY_ARG_SETTING_PATT2.match(arg)
179        if mo:
180            item, value = mo.groups()
181            if   item == 'server':
182                server_creds[0] = value
183            elif item == 'user':
184                server_creds[1] = value
185            elif item == 'password':
186                server_creds[2] = value
187            elif item == 'apikey' or item == 'api_key':
188                server_creds[3] = value
189            elif item == 'workspace':
190                server_creds[4] = value
191            elif item == 'project':
192                server_creds[5] = value
193
194    return server_creds
195
196################################################################################
197
198def rallySettings(args):
199    """
200        ***********  DEPRECATED   *************
201        priority order of Python Rally REST API server ident, credentials, workspace/project:
202          1) command line args with --rallyServer, --rallyUser, --rallyPassword, --workspace, --project
203          2) command line arg specifying a config file --rallyConfig=<config_file_name>
204                                                    or --config=<config_file_name>
205                                                    or --conf=<config_file_name>
206                                                    or --cfg=<config_file_name>
207          3) ENV variable with location of rally-<version>.cfg --> RALLY_CONFIG
208          4) current directory with rally-<version>.cfg
209          5) RALLY_SERVER, RALLY_USER_NAME, RALLY_PASSWORD, RALLY_WORKSPACE, RALLY_PROJECT env VARS
210          6) SERVER, USER_NAME, PASSWORD defined in this module
211
212        start by priming the return values with #6 and work your way up the priority ladder
213    """
214    # #6
215    # start with the defaults defined in this module
216    server_creds = [SERVER, USER_NAME, PASSWORD, "default", "default"]
217
218    def snarfSettings(targetFile, server_creds):
219        """
220            read the filename and look for lines containing relevant Rally settings.
221            alter the server_creds list if there are entries in the file to do so.
222        """
223        if not os.path.exists(targetFile):
224            cfg_suffixed = "%s.cfg" % targetFile
225            if not os.path.exists(cfg_suffixed):
226                return server_creds
227            else:
228                targetFile = cfg_suffixed
229
230        try:
231            cf = open(targetFile, 'r')
232            for line in cf:
233                mo = CONFIG_SETTING_PATT.match(line)
234                if mo:
235                    item, value = mo.groups()
236                    if   item == 'SERVER':
237                        server_creds[0] = value
238                    elif item == 'USER':
239                        server_creds[1] = value
240                    elif item == 'PASSWORD':
241                        server_creds[2] = value
242                    elif item == 'WORKSPACE':
243                        server_creds[3] = value
244                    elif item == 'PROJECT':
245                        server_creds[4] = value
246            cf.close()
247            sc = "%s, %s, %s, %s, %s" % tuple(server_creds)
248            return server_creds
249        except Exception as ex:
250            pass
251
252    # #5
253    # if there are environment vars, use them
254    #
255    for ix, name in enumerate(['RALLY_SERVER', 'RALLY_USER', 'RALLY_PASSWORD', 'RALLY_WORKSPACE', 'RALLY_PROJECT']):
256        if name in os.environ:
257            server_creds[ix] = os.environ[name]
258
259    # #4
260    # if there is a rally-<version>.cfg file in the current directory matching the WS_API_VERSION
261    # load with contents of that file
262    entries = glob.glob('rally-*.cfg')
263    target_version_config = 'rally-%s.cfg' % WS_API_VERSION
264    if entries:
265        if target_version_config in entries:
266            server_creds = snarfSettings(target_version_config, server_creds)
267        else:
268            print("Ignoring non-matching version of Rally config settings: %s (working version: %s)" % \
269                  (entries.pop(), WS_API_VERSION))
270
271    # #3
272    # if there is a RALLY_CONFIG environment variable pointing to a file, load with contents of file
273    config_file = os.environ.get('RALLY_CONFIG', None)
274    if config_file:
275        server_creds = snarfSettings(config_file, server_creds)
276
277    # #2
278    # now look at the args (from command line invocation)
279    # grab any --(rallyConfig|config|conf|cfg)=<filename> args,
280    # and if filename exists attempt to load with contents therein
281    for arg in args:
282        mo = RALLY_CONFIG_FILE_PATT.match(arg)
283        if mo:
284            config_token, config_file = mo.groups()
285            server_creds = snarfSettings(config_file, server_creds)
286
287    # #1
288    # now look at the args (from command line invocation)
289    # grab any --rallyServer=?, --rallyUser=?, --rallyPassword=?, --rallyWorkspace=?, --rallyProject=? in args
290    # grab any --server=?, --user=?, --password=?, --workspace=?, --project=? in args
291    for arg in args:
292        mo = RALLY_ARG_SETTING_PATT1.match(arg)
293        if mo:
294            item, value = mo.groups()
295            if   item == 'rallyServer':
296                server_creds[0] = value
297            elif item == 'rallyUser':
298                server_creds[1] = value
299            elif item == 'rallyPassword':
300                server_creds[2] = value
301            elif item == 'rallyWorkspace':
302                server_creds[3] = value
303            elif item == 'rallyProject':
304                server_creds[4] = value
305
306        mo = RALLY_ARG_SETTING_PATT2.match(arg)
307        if mo:
308            item, value = mo.groups()
309            if   item == 'server':
310                server_creds[0] = value
311            elif item == 'user':
312                server_creds[1] = value
313            elif item == 'password':
314                server_creds[2] = value
315            elif item == 'workspace':
316                server_creds[3] = value
317            elif item == 'project':
318                server_creds[4] = value
319
320    return server_creds
321
322###################################################################################################
323###################################################################################################
324