1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """\
20 Management tool for the Spacewalk Proxy.
21
22 This script performs various management operations on the Spacewalk Proxy:
23 - Creates the local directory structure needed to store local packages
24 - Uploads packages from a given directory to the RHN servers
25 - Optionally, once the packages are uploaded, they can be linked to (one or
26 more) channels, and copied in the local directories for these channels.
27 - Lists the RHN server's vision on a certain channel
28 - Checks if the local image of the channel (the local directory) is in sync
29 with the server's image, and prints the missing packages (or the extra
30 ones)
31 - Cache any RPM content locally to avoid needing to download them. This can be
32 particularly useful if bandwitdth is precious or the connection to the server
33 is slow.
34 """
35
36
37 import gzip
38 import os
39 from xml.dom import minidom
40 import sys
41 import shutil
42 import xmlrpclib
43 from optparse import Option, OptionParser
44
45
46 from spacewalk.common.rhnConfig import CFG, initCFG
47 from spacewalk.common.rhnLib import parseUrl
48 initCFG('proxy.package_manager')
49
50 from rhnpush.uploadLib import UploadError
51 from rhnpush import uploadLib
52 from proxy.broker.rhnRepository import computePackagePaths
53
54
55 PREFIX = 'rhn'
56
57
59
60 optionsTable = [
61 Option('-v', '--verbose', action='count', help='Increase verbosity'),
62 Option('-d', '--dir', action='store', help='Process packages from this directory'),
63 Option('-L', '--cache-locally', action='store_true',
64 help='Locally cache packages so that Proxy will not ever need to '
65 + 'download them. Changes nothing on the upstream server.'),
66 Option('-e', '--from-export', action='store', dest='export_location',
67 help='Process packages from this channel export. Can only be used '
68 + 'with --cache-locally or --copyonly.'),
69 Option('-c', '--channel', action='append',
70 help='Channel to operate on. When used with --from-export '
71 + 'specifies channels to cache rpms for, else specifies channels '
72 + 'that we will be pushing into.'),
73 Option('-n', '--count', action='store', help='Process this number of headers per call', type='int'),
74 Option('-l', '--list', action='store_true', help='Only list the specified channels'),
75 Option('-s', '--sync', action='store_true', help='Check if in sync with the server'),
76 Option('-p', '--printconf', action='store_true', help='Print the configuration and exit'),
77 Option('-X', '--exclude', action="append", help="Exclude packages that match this glob expression"),
78 Option('--newest', action='store_true', help='Only push the files that are newer than the server ones'),
79 Option('--stdin', action='store_true', help='Read the package names from stdin'),
80 Option('--nosig', action='store_true', help="Push unsigned packages"),
81 Option('--username', action='store', help='Use this username to connect to RHN'),
82 Option('--password', action='store', help='Use this password to connect to RHN'),
83 Option('--source', action='store_true', help='Upload source package headers'),
84 Option('--dontcopy', action='store_true', help='Do not copy packages to the local directory'),
85 Option('--copyonly', action='store_true',
86 help="Only copy packages; don't reimport. Same as --cache-locally"),
87 Option('--test', action='store_true', help='Only print the packages to be pushed'),
88 Option('-N', '--new-cache', action='store_true', help='Create a new username/password cache'),
89 Option('--no-ssl', action='store_true', help='Turn off SSL (not recommended).'),
90 Option('--no-session-caching', action='store_true',
91 help='Disables session-token authentication.'),
92 Option('-?', '--usage', action='store_true', help="Briefly describe the options"),
93 ]
94
95 optionParser = OptionParser(option_list=optionsTable, usage="USAGE: %prog [OPTION] [<package>]")
96 options, files = optionParser.parse_args()
97 upload = UploadClass(options, files=files)
98
99 if options.usage:
100 optionParser.print_usage()
101 sys.exit(0)
102
103 if options.printconf:
104 CFG.show()
105 return
106
107 if options.list:
108 upload.list()
109 return
110
111 if options.sync:
112 upload.checkSync()
113 return
114
115
116 if options.cache_locally:
117 options.copyonly = True
118
119
120 if options.dir:
121 upload.directory()
122 if options.export_location:
123 if not options.copyonly:
124 upload.die(0, "--from-export can only be used with --cache-locally"
125 + " or --copyonly")
126 if options.source:
127 upload.die(0, "--from-export cannot be used with --source")
128 upload.from_export()
129 if options.stdin:
130 upload.readStdin()
131
132
133
134
135 upload.files = sorted(list(set(upload.files)))
136
137 if options.copyonly:
138 if not upload.files:
139 upload.die(0, "Nothing to do; exiting. Try --help")
140 if options.test:
141 upload.test()
142 return
143 upload.copyonly()
144 return
145
146 if options.exclude:
147 upload.filter_excludes()
148
149 if options.newest:
150 upload.newest()
151
152 if not upload.files:
153 upload.die(0, "Nothing to do; exiting. Try --help")
154
155 if options.test:
156 upload.test()
157 return
158
159 try:
160 upload.uploadHeaders()
161 except UploadError, e:
162 sys.stderr.write("Upload error: %s\n" % e)
163
164
166
167
168 - def setURL(self, path='/APP'):
169
170 if not CFG.RHN_PARENT:
171 self.die(-1, "rhn_parent not set in the configuration file")
172 self.url = CFG.RHN_PARENT
173 scheme = 'http://'
174 if not self.options.no_ssl and CFG.USE_SSL:
175
176 scheme = 'https://'
177 self.url = CFG.RHN_PARENT or ''
178 self.url = parseUrl(self.url)[1].split(':')[0]
179 self.url = scheme + self.url + path
180
181
182
183
184
185
187 export_dir = self.options.export_location
188 self.warn(1, "Getting files from channel export: ", export_dir)
189 if not self.options.channel:
190 self.warn(2, "No channels specified, getting all files")
191
192
193 for hash_dir in uploadLib.listdir(os.path.join(
194 export_dir, "rpms")):
195 self.options.dir = hash_dir
196 self.directory()
197 return
198
199 self.warn(2, "Getting only files in these channels",
200 self.options.channel)
201
202 package_set = set([])
203 for channel in self.options.channel:
204 xml_path = os.path.join(export_dir, "channels", channel,
205 "channel.xml.gz")
206 if not os.access(xml_path, os.R_OK):
207 self.warn(0, "Could not find metadata for channel %s, skipping..." % channel)
208 print "Could not find metadata for channel %s, skipping..." % channel
209 continue
210 dom = minidom.parse(gzip.open(xml_path))
211
212 dom_channel = dom.getElementsByTagName('rhn-channel')[0]
213 package_set.update(dom_channel.attributes['packages']
214 .value.encode('ascii', 'ignore').split())
215
216 for hash_dir in uploadLib.listdir(os.path.join(export_dir, "rpms")):
217 for rpm in uploadLib.listdir(hash_dir):
218
219 if os.path.basename(rpm)[:-4] in package_set:
220 self.files.append(rpm)
221
233
239
241
242 self.proxyUsername = CFG.HTTP_PROXY_USERNAME
243 self.proxyPassword = CFG.HTTP_PROXY_PASSWORD
244
248
250
251 self.ca_chain = CFG.CA_CHAIN
252
255
257
258 self.setOrg()
259
260 self.setURL()
261
262 self.setChannels()
263
264 self.setServer()
265
266 self.authenticate()
267
268
269 channel_list = self._listChannel()
270
271
272 remotePackages = {}
273 for channel in self.channels:
274 remotePackages[channel] = {}
275 for p in channel_list:
276 channelName = p[-1]
277 key = tuple(p[:5])
278 remotePackages[channelName][key] = None
279
280 missing = []
281 for package in channel_list:
282 found = False
283
284 if self.use_checksum_paths:
285 checksum = package[6]
286 else:
287 checksum = None
288
289 packagePaths = computePackagePaths(package, 0, PREFIX, checksum)
290 for packagePath in packagePaths:
291 packagePath = "%s/%s" % (CFG.PKG_DIR, packagePath)
292 if os.path.isfile(packagePath):
293 found = True
294 break
295 if not found:
296 missing.append([package, packagePaths[0]])
297
298 if not missing:
299 self.warn(0, "Channels in sync with the server")
300 return
301
302 for package, packagePath in missing:
303 channelName = package[-1]
304 self.warn(0, "Missing: %s in channel %s (path %s)" % (
305 rpmPackageName(package), channelName, packagePath))
306
308 if self.options.dontcopy:
309 return
310
311 if not CFG.PKG_DIR:
312 self.warn(1, "No package directory specified; will not copy the package")
313 return
314
315 if not self.use_checksum_paths:
316 checksum = None
317
318 packagePath = computePackagePaths(package, self.options.source,
319 PREFIX, checksum)[0]
320 packagePath = "%s/%s" % (CFG.PKG_DIR, packagePath)
321 destdir = os.path.dirname(packagePath)
322 if not os.path.isdir(destdir):
323
324 try:
325 os.makedirs(destdir, 0755)
326 except OSError:
327 self.warn(0, "Could not create directory %s" % destdir)
328 return
329 self.warn(1, "Copying %s to %s" % (filename, packagePath))
330 shutil.copy2(filename, packagePath)
331
332
333 os.chmod(packagePath, 0644)
334
336 self.die(1, "Listing source rpms not supported")
337
361
362
364 return "%s-%s-%s.%s.rpm" % (p[0], p[1], p[2], p[4])
365
366 if __name__ == '__main__':
367 try:
368 main()
369 except SystemExit, se:
370 sys.exit(se.code)
371