-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhouston
More file actions
executable file
·465 lines (381 loc) · 17 KB
/
houston
File metadata and controls
executable file
·465 lines (381 loc) · 17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
#! /usr/bin/python3
''' Script to manage spacewalk channels.
'''
# imports
import sys
import time
import pprint
from Houston.libhouston import *
def clone(a):
'''Clones Channel
:param a: cmd line Args as returned from :func:`argparse.parse_args`
:returns: Boolean
'''
with Spacewalk(a.serverurl, a.username) as spw:
try:
channel = Channel(a.channel, spw)
except SpacewalkError as e:
sys.exit("Error: {}".format(e))
if channel['parent_channel_label']:
sys.exit('Error: {} is not Base channel.'.format(a.channel))
month = time.strftime("%b").lower() # abbrv month name
new_parent = {
'name':
'dev-{proj}-{mon}-{tag}-{orig}'.format(proj=a.project, mon=month,
tag=a.tag,
orig=channel['name']),
'label':
'dev-{proj}-{mon}-{tag}-{orig}'.format(proj=a.project, mon=month,
tag=a.tag,
orig=channel['label']),
'summary':
'''Development clone of {orig} for {proj} tag {tag}.
This will be only available during {mon} after \
which it will be updated.'''.format(orig=channel['name'],
proj=a.project, tag=a.tag,
mon=month),
}
try:
channel.clone(new_parent, False)
except SpacewalkError as e:
sys.exit("Error cloning channel {c}:\n"
"{err}".format(err=e, c=channel['label']))
for child in channel['children']:
new_channel = {
'name':
'dev-{proj}-{mon}-{tag}-{orig}'.format(proj=a.project,
mon=month, tag=a.tag,
orig=child['name']),
'label':
'dev-{proj}-{mon}-{tag}-{orig}'.format(proj=a.project,
mon=month, tag=a.tag,
orig=child['label']),
'summary':
'''Development clone of {orig} for {proj} tag {tag}.
This will be only available during {mon} after \
which it will be updated.'''.format(orig=child['name'],
proj=a.project, tag=a.tag,
mon=month),
'parent_label':
new_parent['label'],
}
try:
child.clone(new_channel, False)
except SpacewalkError as e:
sys.exit("Error cloning channel {c}:\n"
"{err}".format(c=child['label'], err=e))
def delete(a):
'''Removes Channel and optionally any subchannels
:param a: cmd line Args as returned from :func:`argparse.parse_args`
:returns: Boolean
'''
with Spacewalk(a.serverurl, a.username) as spw:
try:
channel = Channel(a.channel, spw)
except SpacewalkChannelNotFound as e:
sys.exit("Houston we have a problem:\n\n{}".format(e))
if len(channel['children']) > 0 and not a.recursive:
sys.exit('Channel {} has children. \n'
'Use recursive flag to delete these channels as well.'
"".format(channel['name']))
if a.recursive:
for child in channel['children']:
if a.verbose:
print("Deleting channel: {}".format(child))
child.delete()
if a.verbose:
print("Deleting channel: {}".format(a.channel))
channel.delete()
def migrate(a):
'''takes a list of all servers registered to from-channel and re-registers
them to the to-channel.
:param a: cmd line Args as returned from :func:`argparse.parse_args`
:returns: Boolean
'''
with Spacewalk(a.serverurl, a.username, a.verbose) as spw:
channel = Channel(a.from_channel, spw)
if a.systems:
migrations = [s for s in channel['systems']
if str(s) in a.systems]
else:
migrations = channel['systems']
for system in migrations:
if a.verbose:
print("Migrating {} to {}".format(system, a.to_channel))
spw.subscribe_base_channel(system, a.to_channel, a.recursive)
def rollout(a):
'''Rolls out channels the next environment up
:param a: cmd line Args as returned from :func:`argparse.parse_args`
:type a: Namespace obj :class:`argparse.Namespace`
:returns: Boolean
Rolls out a set of channels from one environment to the next according to
the matrix below:
======= =======
old env new env
======= =======
DEV QA
QA STAGE
STAGE PROD
======= =======
'''
with Spacewalk(a.serverurl, a.username, a.verbose) as spw:
if not spw.channel_exists(a.channel):
sys.exit("Error: Channel {c} Does not exist".format(c=a.channel))
rollout_order = ('dev', 'qa', 'stage', 'prod')
src_env = a.channel.split('-')[0]
dst_env = rollout_order[rollout_order.index(src_env) + 1]
old_channel = Channel(a.channel, spw)
new_channel = {
'label': old_channel['label'].replace(src_env, dst_env),
'name': old_channel['name'].replace(src_env, dst_env),
'summary': old_channel['summary'].replace(src_env, dst_env),
}
try:
old_channel.clone(new_channel, False)
except SpacewalkError as e:
sys.exit("Error cloning channel {c}:\n"
"{err}".format(err=e, c=a.channel))
for child in old_channel['children']:
# yeah this is a little tedious, but it appears that the dict
# returned from a get_details call has different keys than that
# accepted by the clone channel api call.
new_channel = {
'label':
child['label'].replace(src_env, dst_env),
'name':
child['name'].replace(src_env, dst_env),
'summary':
child['summary'].replace(src_env, dst_env),
'description':
child['description'].replace(src_env, dst_env),
'parent_label':
child['parent_channel_label'].replace(src_env, dst_env),
}
try:
child.clone(new_channel, False)
except SpacewalkError as e:
sys.exit("Error cloning channel {c}:\n"
"{err}".format(err=e, c=child))
def _generate_lucerne_query(kwargs):
'''generates a lucerne query from the tags provided.
:param **kwargs: keyword pairs to distill into lucerne query
:returns: string query
'''
retval = ""
for field,value in kwargs.items():
retval = retval + '{k}:"{v}" AND '.format(k=field, v=value)
return retval[:-5] # splice trims the final ' AND '
def _obtain_list_of_pkgs(a, spw, all_channels=False):
'''gets a list of packages from the spacewalk server
:param a: @todo
:type a: @todo
:param spw: instance of spacewalk
:type spw: ;class:`Spacewalklib.Spacewalk`
:param all_channels: search all channels?
If False looks for a.channels as a list of channels to look in.
This is the default behaviour.
:returns: dict
* dict
* _int_ id
* _str_ name
* _str_ epoch
* _str_ version
* _str_ release
* _str_ arch_label
* array "providing channels"
* _str_ channel label providing this package
* _str_ build_host
* _str_ description
* _str_ checksum
* _str_ checksum_type
* _str_ vendor
* _str_ summary
* _str_ cookie
* _str_ license
* _str_ file
* _str_ build_date
* _str_ last_modified_date
* _str_ size
* _str_ path - the path on the satellite servers file system
* _str_ payload_size
'''
if all_channels:
channels = None
else:
channels = a.channels
if not a.query:
lucerne = {}
for tag in ['name', 'version', 'release', 'epoch']:
if getattr(a, tag):
lucerne[tag] = getattr(a, tag)
a.query = _generate_lucerne_query(lucerne)
if a.name:
pkgs = [x for x in
spw.lucerne_query(a.query, channels=channels)
if x['name'] == a.name]
else:
pkgs = spw.lucerne_query(a.query, channels=channels)
if len(pkgs) == 0:
sys.exit("Package {n} Cannot be found.".format(n=a.name))
else:
cnt = 1
print("Please choose a package:")
w = {'name': 4,
'version': 7,
'release': 7,
'epoch': 5,
'arch': 4,
}
# obtain largest length of field for each field
for pkg in pkgs:
for key in w.keys():
if w[key] < len(pkg[key]):
w[key] = len(pkg[key])
format_string = "{c:>3} {n:<{nw}} {v:>{vw}} {r:>{rw}} " \
"{e:>{ew}} {a:>{aw}}"
print(format_string.format(c="", n="Name", nw=w['name'], v="Version",
vw=w['version'], r="Release",
rw=w['release'], e="Epoch", ew=w['epoch'],
a="Arch", aw=w['arch'], end=' '))
for pkg in pkgs:
print(format_string.format(c=cnt, n=pkg['name'], nw=w['name'],
v=pkg['version'], vw=w['version'],
r=pkg['release'], rw=w['release'],
e=pkg['epoch'], ew=w['epoch'],
a=pkg['arch'], aw=w['arch'], end=' '))
cnt += 1
print("\nPress 'q' to quit.")
try:
choice = input('Number: ')
except KeyboardInterrupt:
sys.exit("\nQuitter.")
if choice == 'q':
sys.exit("Quitter.")
return pkgs[int(choice)-1]
def pkg_remove(a):
'''@todo: Docstring for rollback
:param a: @todo
:type a: @todo
:returns: @todo
'''
print("Stubbed function")
def pkg_add(a):
'''adds a package to a specified channel(s)
'''
print("Stubbed function")
def parse_cmd_line():
'''Parses commad line
:returns: namespace object
'''
import argparse
# opts
parent_parser = argparse.ArgumentParser(description='Channel management script.')
parent_parser.add_argument('-s', '--serverurl', required=False,
help='Url for the spacewalk server.\
default https://cm1.dev.solutions.local/rpc/api')
parent_parser.add_argument('-u', '--username', required=False,
help='Username used to log into the spacewalk server.')
parent_parser.add_argument('-v', '--verbose', help="displays more info",
action='store_true')
parent_parser.add_argument('--version', action='version',
version='%(prog)s 0.1')
subparsers = parent_parser.add_subparsers(help="sub-command help",
title='Commands',
description='Valid Sub-commands')
channel_p = subparsers.add_parser('channel')
channel_sp = channel_p.add_subparsers(title='Channel Commands',
description='commands related to '
'channel management.')
package_p = subparsers.add_parser('pkg')
package_sp = package_p.add_subparsers(title='Package Commands',
description='Commands to manipulate'
'packages on spacewalk server')
######################
# Channel commands #
######################
channel_args = argparse.ArgumentParser(add_help=False)
channel_args.add_argument('-c', '--channel', required=True,
help='Channel to operate on')
# clone
parse_clone = channel_sp.add_parser('clone', parents=[channel_args],
help='''clones base channel and
children. This may only be called on a
base channel.''')
parse_clone.add_argument('-p', '--project', required=True,
help='Name of Project clone will be used for')
parse_clone.add_argument('-t', '--tag', required=True,
help='Name of Project Tag clone will be used for')
parse_clone.set_defaults(func=clone)
# delete
parse_delete = channel_sp.add_parser('delete', parents=[channel_args],
help='deletes a channel')
parse_delete.add_argument('-r', '--recursive', required=False,
default=False, action='store_true',
help='Delete all child channels as well.')
parse_delete.set_defaults(func=delete)
# Migrate
parse_migrate = channel_sp.add_parser('migrate',
help='''Migrates all servers
registered against <from-channel> and
re-registers them to <to-channel>''')
parse_migrate.add_argument('-f', '--from-channel', required=True,
help='Channel to migrate servers from')
parse_migrate.add_argument('-t', '--to-channel', required=True,
help='Channel to migrate servers to')
parse_migrate.add_argument('-S', '--systems', required=False, nargs='+',
help='''Restrict list of migrated servers to
these ids. Can be a comma seperated list.''')
parse_migrate.add_argument('-r', '--recursive', required=False,
action='store_true', help='''subscribe to any
children of <from-channel>''')
parse_migrate.set_defaults(func=migrate)
# rollout
parse_rollout = channel_sp.add_parser('rollout', parents=[channel_args],
help='Rolls out changes from one env'
'to the next. e.g. dev -> qa')
parse_rollout.set_defaults(func=rollout)
######################
# Package Commands #
######################
pkg_args = argparse.ArgumentParser(add_help=False)
lucerne_or_Nvrea = \
pkg_args.add_mutually_exclusive_group(required=True)
lucerne_or_Nvrea.add_argument('-q', '--query',
help='lucerne query '
'(see: http://lucene.apache.org/')
lucerne_or_Nvrea.add_argument('-n', '--name',
help='Name of package to remove')
pkg_args.add_argument('-v', '--version', required=False,
help='version of the package to remove')
pkg_args.add_argument('-r', '--release', required=False,
help='Release of the package to remove')
pkg_args.add_argument('-e', '--epoch', required=False,
help='Epoch of the package to remove')
pkg_args.add_argument('-c', '--channels', nargs='+', required=True,
help='''channels to restrict pkg operations to
Multiple comma seperated channels can be
specified''')
# remove
parse_pkg_remove = package_sp.add_parser('remove', parents=[pkg_args],
help='Remove package version'
'from specified channel. Will'
'prompt if multiple pckages match'
' the command line')
parse_pkg_remove.set_defaults(func=pkg_remove)
# add
parse_pkg_add = package_sp.add_parser('add', parents=[pkg_args],
help='''Adds Specified package to
channels provided.''')
parse_pkg_add.set_defaults(func=pkg_add)
ns = parent_parser.parse_args()
return ns
if __name__ == '__main__':
args = parse_cmd_line()
try:
if 'func' in args:
args.func(args)
else:
parent_parser.print_help()
except KeyboardInterrupt:
sys.exit("Telepathic skills indicate the user wishes to exit.\n"
"So I will.")