summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorTom Zanussi <tom.zanussi@linux.intel.com>2013-08-24 15:31:34 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-10-01 22:56:03 +0100
commit9fc88f96d40b17c90bac53b90045a87b2d2cff84 (patch)
tree63010e5aabf895697655baf89bd668d6752b3f97 /scripts
parent53a1d9a788fd9f970af980da2ab975cca60685c4 (diff)
downloadpoky-9fc88f96d40b17c90bac53b90045a87b2d2cff84.tar.gz
wic: Add mic w/pykickstart
This is the starting point for the implemention described in [YOCTO 3847] which came to the conclusion that it would make sense to use kickstart syntax to implement image creation in OpenEmbedded. I subsequently realized that there was an existing tool that already implemented image creation using kickstart syntax, the Tizen/Meego mic tool. As such, it made sense to use that as a starting point - this commit essentially just copies the relevant Python code from the MIC tool to the scripts/lib dir, where it can be accessed by the previously created wic tool. Most of this will be removed or renamed by later commits, since we're initially focusing on partitioning only. Care should be taken so that we can easily add back any additional functionality should we decide later to expand the tool, though (we may also want to contribute our local changes to the mic tool to the Tizen project if it makes sense, and therefore should avoid gratuitous changes to the original code if possible). Added the /mic subdir from Tizen mic repo as a starting point: git clone git://review.tizen.org/tools/mic.git For reference, the top commit: commit 20164175ddc234a17b8a12c33d04b012347b1530 Author: Gui Chen <gui.chen@intel.com> Date: Sun Jun 30 22:32:16 2013 -0400 bump up to 0.19.2 Also added the /plugins subdir, moved to under the /mic subdir (to match the default plugin_dir location in mic.conf.in, which was renamed to yocto-image.conf (moved and renamed by later patches) and put into /scripts. (From OE-Core rev: 31f0360f1fd4ebc9dfcaed42d1c50d2448b4632e) Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Saul Wold <sgw@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/__init__.py0
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/base.py466
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py26
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py40
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py119
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py55
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py265
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py86
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/device.py125
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py40
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py68
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py91
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py184
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py114
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py193
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py62
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/group.py88
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py139
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py58
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py133
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py54
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/key.py64
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py55
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/lang.py60
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py58
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py54
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/logging.py66
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py304
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py53
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/method.py186
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py106
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py70
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py111
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/network.py363
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/partition.py353
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/raid.py365
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py79
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/repo.py249
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py68
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py93
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py64
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/services.py71
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py54
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py105
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py86
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/updates.py60
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py106
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/user.py173
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py114
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py102
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py184
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py69
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py134
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/constants.py57
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/errors.py103
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py0
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/control.py1307
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py24
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/ko.py37
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/options.py204
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/parser.py702
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/sections.py244
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py53
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py463
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py1477
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py617
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py458
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py530
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py90
-rw-r--r--scripts/lib/mic/3rdparty/pykickstart/version.py197
-rw-r--r--scripts/lib/mic/__init__.py4
-rw-r--r--scripts/lib/mic/__version__.py1
-rw-r--r--scripts/lib/mic/bootstrap.py279
-rw-r--r--scripts/lib/mic/chroot.py343
-rw-r--r--scripts/lib/mic/conf.py239
-rw-r--r--scripts/lib/mic/creator.py354
-rw-r--r--scripts/lib/mic/imager/__init__.py0
-rw-r--r--scripts/lib/mic/imager/baseimager.py1335
-rw-r--r--scripts/lib/mic/imager/fs.py99
-rw-r--r--scripts/lib/mic/imager/livecd.py750
-rw-r--r--scripts/lib/mic/imager/liveusb.py308
-rw-r--r--scripts/lib/mic/imager/loop.py418
-rw-r--r--scripts/lib/mic/imager/raw.py501
-rw-r--r--scripts/lib/mic/kickstart/__init__.py892
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/__init__.py12
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/desktop.py95
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/installerfw.py63
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/micboot.py49
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/micrepo.py127
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/partition.py57
-rw-r--r--scripts/lib/mic/msger.py309
-rw-r--r--scripts/lib/mic/plugin.py98
-rw-r--r--scripts/lib/mic/pluginbase.py101
-rw-r--r--scripts/lib/mic/plugins/backend/yumpkgmgr.py490
-rwxr-xr-xscripts/lib/mic/plugins/backend/zypppkgmgr.py973
-rw-r--r--scripts/lib/mic/plugins/hook/.py0
-rw-r--r--scripts/lib/mic/plugins/hook/empty_hook.py3
-rw-r--r--scripts/lib/mic/plugins/imager/fs_plugin.py143
-rw-r--r--scripts/lib/mic/plugins/imager/livecd_plugin.py255
-rw-r--r--scripts/lib/mic/plugins/imager/liveusb_plugin.py260
-rw-r--r--scripts/lib/mic/plugins/imager/loop_plugin.py255
-rw-r--r--scripts/lib/mic/plugins/imager/raw_plugin.py275
-rw-r--r--scripts/lib/mic/rt_util.py223
-rw-r--r--scripts/lib/mic/test1
-rw-r--r--scripts/lib/mic/utils/BmapCreate.py298
-rw-r--r--scripts/lib/mic/utils/Fiemap.py252
-rw-r--r--scripts/lib/mic/utils/__init__.py0
-rw-r--r--scripts/lib/mic/utils/cmdln.py1586
-rw-r--r--scripts/lib/mic/utils/errors.py71
-rw-r--r--scripts/lib/mic/utils/fs_related.py1029
-rw-r--r--scripts/lib/mic/utils/gpt_parser.py331
-rw-r--r--scripts/lib/mic/utils/grabber.py97
-rw-r--r--scripts/lib/mic/utils/misc.py1067
-rw-r--r--scripts/lib/mic/utils/partitionedfs.py790
-rw-r--r--scripts/lib/mic/utils/proxy.py183
-rw-r--r--scripts/lib/mic/utils/rpmmisc.py600
-rw-r--r--scripts/lib/mic/utils/runner.py109
-rw-r--r--scripts/yocto-image.conf35
135 files changed, 29216 insertions, 0 deletions
diff --git a/scripts/lib/mic/3rdparty/pykickstart/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/__init__.py
diff --git a/scripts/lib/mic/3rdparty/pykickstart/base.py b/scripts/lib/mic/3rdparty/pykickstart/base.py
new file mode 100644
index 0000000000..e6c8f56f9d
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/base.py
@@ -0,0 +1,466 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2006, 2007, 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20"""
21Base classes for creating commands and syntax version object.
22
23This module exports several important base classes:
24
25 BaseData - The base abstract class for all data objects. Data objects
26 are contained within a BaseHandler object.
27
28 BaseHandler - The base abstract class from which versioned kickstart
29 handler are derived. Subclasses of BaseHandler hold
30 BaseData and KickstartCommand objects.
31
32 DeprecatedCommand - An abstract subclass of KickstartCommand that should
33 be further subclassed by users of this module. When
34 a subclass is used, a warning message will be
35 printed.
36
37 KickstartCommand - The base abstract class for all kickstart commands.
38 Command objects are contained within a BaseHandler
39 object.
40"""
41import gettext
42gettext.textdomain("pykickstart")
43_ = lambda x: gettext.ldgettext("pykickstart", x)
44
45import types
46import warnings
47from pykickstart.errors import *
48from pykickstart.ko import *
49from pykickstart.parser import Packages
50from pykickstart.version import versionToString
51
52###
53### COMMANDS
54###
55class KickstartCommand(KickstartObject):
56 """The base class for all kickstart commands. This is an abstract class."""
57 removedKeywords = []
58 removedAttrs = []
59
60 def __init__(self, writePriority=0, *args, **kwargs):
61 """Create a new KickstartCommand instance. This method must be
62 provided by all subclasses, but subclasses must call
63 KickstartCommand.__init__ first. Instance attributes:
64
65 currentCmd -- The name of the command in the input file that
66 caused this handler to be run.
67 currentLine -- The current unprocessed line from the input file
68 that caused this handler to be run.
69 handler -- A reference to the BaseHandler subclass this
70 command is contained withing. This is needed to
71 allow referencing of Data objects.
72 lineno -- The current line number in the input file.
73 writePriority -- An integer specifying when this command should be
74 printed when iterating over all commands' __str__
75 methods. The higher the number, the later this
76 command will be written. All commands with the
77 same priority will be written alphabetically.
78 """
79
80 # We don't want people using this class by itself.
81 if self.__class__ is KickstartCommand:
82 raise TypeError, "KickstartCommand is an abstract class."
83
84 KickstartObject.__init__(self, *args, **kwargs)
85
86 self.writePriority = writePriority
87
88 # These will be set by the dispatcher.
89 self.currentCmd = ""
90 self.currentLine = ""
91 self.handler = None
92 self.lineno = 0
93
94 # If a subclass provides a removedKeywords list, remove all the
95 # members from the kwargs list before we start processing it. This
96 # ensures that subclasses don't continue to recognize arguments that
97 # were removed.
98 for arg in filter(kwargs.has_key, self.removedKeywords):
99 kwargs.pop(arg)
100
101 def __call__(self, *args, **kwargs):
102 """Set multiple attributes on a subclass of KickstartCommand at once
103 via keyword arguments. Valid attributes are anything specified in
104 a subclass, but unknown attributes will be ignored.
105 """
106 for (key, val) in kwargs.items():
107 # Ignore setting attributes that were removed in a subclass, as
108 # if they were unknown attributes.
109 if key in self.removedAttrs:
110 continue
111
112 if hasattr(self, key):
113 setattr(self, key, val)
114
115 def __str__(self):
116 """Return a string formatted for output to a kickstart file. This
117 method must be provided by all subclasses.
118 """
119 return KickstartObject.__str__(self)
120
121 def parse(self, args):
122 """Parse the list of args and set data on the KickstartCommand object.
123 This method must be provided by all subclasses.
124 """
125 raise TypeError, "parse() not implemented for KickstartCommand"
126
127 def apply(self, instroot="/"):
128 """Write out the configuration related to the KickstartCommand object.
129 Subclasses which do not provide this method will not have their
130 configuration written out.
131 """
132 return
133
134 def dataList(self):
135 """For commands that can occur multiple times in a single kickstart
136 file (like network, part, etc.), return the list that we should
137 append more data objects to.
138 """
139 return None
140
141 def deleteRemovedAttrs(self):
142 """Remove all attributes from self that are given in the removedAttrs
143 list. This method should be called from __init__ in a subclass,
144 but only after the superclass's __init__ method has been called.
145 """
146 for attr in filter(lambda k: hasattr(self, k), self.removedAttrs):
147 delattr(self, attr)
148
149 # Set the contents of the opts object (an instance of optparse.Values
150 # returned by parse_args) as attributes on the KickstartCommand object.
151 # It's useful to call this from KickstartCommand subclasses after parsing
152 # the arguments.
153 def _setToSelf(self, optParser, opts):
154 self._setToObj(optParser, opts, self)
155
156 # Sets the contents of the opts object (an instance of optparse.Values
157 # returned by parse_args) as attributes on the provided object obj. It's
158 # useful to call this from KickstartCommand subclasses that handle lists
159 # of objects (like partitions, network devices, etc.) and need to populate
160 # a Data object.
161 def _setToObj(self, optParser, opts, obj):
162 for key in filter (lambda k: getattr(opts, k) != None, optParser.keys()):
163 setattr(obj, key, getattr(opts, key))
164
165class DeprecatedCommand(KickstartCommand):
166 """Specify that a command is deprecated and no longer has any function.
167 Any command that is deprecated should be subclassed from this class,
168 only specifying an __init__ method that calls the superclass's __init__.
169 This is an abstract class.
170 """
171 def __init__(self, writePriority=None, *args, **kwargs):
172 # We don't want people using this class by itself.
173 if self.__class__ is KickstartCommand:
174 raise TypeError, "DeprecatedCommand is an abstract class."
175
176 # Create a new DeprecatedCommand instance.
177 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
178
179 def __str__(self):
180 """Placeholder since DeprecatedCommands don't work anymore."""
181 return ""
182
183 def parse(self, args):
184 """Print a warning message if the command is seen in the input file."""
185 mapping = {"lineno": self.lineno, "cmd": self.currentCmd}
186 warnings.warn(_("Ignoring deprecated command on line %(lineno)s: The %(cmd)s command has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this command.") % mapping, DeprecationWarning)
187
188
189###
190### HANDLERS
191###
192class BaseHandler(KickstartObject):
193 """Each version of kickstart syntax is provided by a subclass of this
194 class. These subclasses are what users will interact with for parsing,
195 extracting data, and writing out kickstart files. This is an abstract
196 class.
197
198 version -- The version this syntax handler supports. This is set by
199 a class attribute of a BaseHandler subclass and is used to
200 set up the command dict. It is for read-only use.
201 """
202 version = None
203
204 def __init__(self, mapping=None, dataMapping=None, commandUpdates=None,
205 dataUpdates=None, *args, **kwargs):
206 """Create a new BaseHandler instance. This method must be provided by
207 all subclasses, but subclasses must call BaseHandler.__init__ first.
208
209 mapping -- A custom map from command strings to classes,
210 useful when creating your own handler with
211 special command objects. It is otherwise unused
212 and rarely needed. If you give this argument,
213 the mapping takes the place of the default one
214 and so must include all commands you want
215 recognized.
216 dataMapping -- This is the same as mapping, but for data
217 objects. All the same comments apply.
218 commandUpdates -- This is similar to mapping, but does not take
219 the place of the defaults entirely. Instead,
220 this mapping is applied after the defaults and
221 updates it with just the commands you want to
222 modify.
223 dataUpdates -- This is the same as commandUpdates, but for
224 data objects.
225
226
227 Instance attributes:
228
229 commands -- A mapping from a string command to a KickstartCommand
230 subclass object that handles it. Multiple strings can
231 map to the same object, but only one instance of the
232 command object should ever exist. Most users should
233 never have to deal with this directly, as it is
234 manipulated internally and called through dispatcher.
235 currentLine -- The current unprocessed line from the input file
236 that caused this handler to be run.
237 packages -- An instance of pykickstart.parser.Packages which
238 describes the packages section of the input file.
239 platform -- A string describing the hardware platform, which is
240 needed only by system-config-kickstart.
241 scripts -- A list of pykickstart.parser.Script instances, which is
242 populated by KickstartParser.addScript and describes the
243 %pre/%post/%traceback script section of the input file.
244 """
245
246 # We don't want people using this class by itself.
247 if self.__class__ is BaseHandler:
248 raise TypeError, "BaseHandler is an abstract class."
249
250 KickstartObject.__init__(self, *args, **kwargs)
251
252 # This isn't really a good place for these, but it's better than
253 # everything else I can think of.
254 self.scripts = []
255 self.packages = Packages()
256 self.platform = ""
257
258 # These will be set by the dispatcher.
259 self.commands = {}
260 self.currentLine = 0
261
262 # A dict keyed by an integer priority number, with each value being a
263 # list of KickstartCommand subclasses. This dict is maintained by
264 # registerCommand and used in __str__. No one else should be touching
265 # it.
266 self._writeOrder = {}
267
268 self._registerCommands(mapping, dataMapping, commandUpdates, dataUpdates)
269
270 def __str__(self):
271 """Return a string formatted for output to a kickstart file."""
272 retval = ""
273
274 if self.platform != "":
275 retval += "#platform=%s\n" % self.platform
276
277 retval += "#version=%s\n" % versionToString(self.version)
278
279 lst = self._writeOrder.keys()
280 lst.sort()
281
282 for prio in lst:
283 for obj in self._writeOrder[prio]:
284 retval += obj.__str__()
285
286 for script in self.scripts:
287 retval += script.__str__()
288
289 retval += self.packages.__str__()
290
291 return retval
292
293 def _insertSorted(self, lst, obj):
294 length = len(lst)
295 i = 0
296
297 while i < length:
298 # If the two classes have the same name, it's because we are
299 # overriding an existing class with one from a later kickstart
300 # version, so remove the old one in favor of the new one.
301 if obj.__class__.__name__ > lst[i].__class__.__name__:
302 i += 1
303 elif obj.__class__.__name__ == lst[i].__class__.__name__:
304 lst[i] = obj
305 return
306 elif obj.__class__.__name__ < lst[i].__class__.__name__:
307 break
308
309 if i >= length:
310 lst.append(obj)
311 else:
312 lst.insert(i, obj)
313
314 def _setCommand(self, cmdObj):
315 # Add an attribute on this version object. We need this to provide a
316 # way for clients to access the command objects. We also need to strip
317 # off the version part from the front of the name.
318 if cmdObj.__class__.__name__.find("_") != -1:
319 name = unicode(cmdObj.__class__.__name__.split("_", 1)[1])
320 else:
321 name = unicode(cmdObj.__class__.__name__).lower()
322
323 setattr(self, name.lower(), cmdObj)
324
325 # Also, add the object into the _writeOrder dict in the right place.
326 if cmdObj.writePriority is not None:
327 if self._writeOrder.has_key(cmdObj.writePriority):
328 self._insertSorted(self._writeOrder[cmdObj.writePriority], cmdObj)
329 else:
330 self._writeOrder[cmdObj.writePriority] = [cmdObj]
331
332 def _registerCommands(self, mapping=None, dataMapping=None, commandUpdates=None,
333 dataUpdates=None):
334 if mapping == {} or mapping == None:
335 from pykickstart.handlers.control import commandMap
336 cMap = commandMap[self.version]
337 else:
338 cMap = mapping
339
340 if dataMapping == {} or dataMapping == None:
341 from pykickstart.handlers.control import dataMap
342 dMap = dataMap[self.version]
343 else:
344 dMap = dataMapping
345
346 if type(commandUpdates) == types.DictType:
347 cMap.update(commandUpdates)
348
349 if type(dataUpdates) == types.DictType:
350 dMap.update(dataUpdates)
351
352 for (cmdName, cmdClass) in cMap.iteritems():
353 # First make sure we haven't instantiated this command handler
354 # already. If we have, we just need to make another mapping to
355 # it in self.commands.
356 cmdObj = None
357
358 for (key, val) in self.commands.iteritems():
359 if val.__class__.__name__ == cmdClass.__name__:
360 cmdObj = val
361 break
362
363 # If we didn't find an instance in self.commands, create one now.
364 if cmdObj == None:
365 cmdObj = cmdClass()
366 self._setCommand(cmdObj)
367
368 # Finally, add the mapping to the commands dict.
369 self.commands[cmdName] = cmdObj
370 self.commands[cmdName].handler = self
371
372 # We also need to create attributes for the various data objects.
373 # No checks here because dMap is a bijection. At least, that's what
374 # the comment says. Hope no one screws that up.
375 for (dataName, dataClass) in dMap.iteritems():
376 setattr(self, dataName, dataClass)
377
378 def dispatcher(self, args, lineno):
379 """Call the appropriate KickstartCommand handler for the current line
380 in the kickstart file. A handler for the current command should
381 be registered, though a handler of None is not an error. Returns
382 the data object returned by KickstartCommand.parse.
383
384 args -- A list of arguments to the current command
385 lineno -- The line number in the file, for error reporting
386 """
387 cmd = args[0]
388
389 if not self.commands.has_key(cmd):
390 raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown command: %s" % cmd))
391 elif self.commands[cmd] != None:
392 self.commands[cmd].currentCmd = cmd
393 self.commands[cmd].currentLine = self.currentLine
394 self.commands[cmd].lineno = lineno
395
396 # The parser returns the data object that was modified. This could
397 # be a BaseData subclass that should be put into a list, or it
398 # could be the command handler object itself.
399 obj = self.commands[cmd].parse(args[1:])
400 lst = self.commands[cmd].dataList()
401 if lst is not None:
402 lst.append(obj)
403
404 return obj
405
406 def maskAllExcept(self, lst):
407 """Set all entries in the commands dict to None, except the ones in
408 the lst. All other commands will not be processed.
409 """
410 self._writeOrder = {}
411
412 for (key, val) in self.commands.iteritems():
413 if not key in lst:
414 self.commands[key] = None
415
416 def hasCommand(self, cmd):
417 """Return true if there is a handler for the string cmd."""
418 return hasattr(self, cmd)
419
420
421###
422### DATA
423###
424class BaseData(KickstartObject):
425 """The base class for all data objects. This is an abstract class."""
426 removedKeywords = []
427 removedAttrs = []
428
429 def __init__(self, *args, **kwargs):
430 """Create a new BaseData instance.
431
432 lineno -- Line number in the ks-file where this object was defined
433 """
434
435 # We don't want people using this class by itself.
436 if self.__class__ is BaseData:
437 raise TypeError, "BaseData is an abstract class."
438
439 KickstartObject.__init__(self, *args, **kwargs)
440 self.lineno = 0
441
442 def __str__(self):
443 """Return a string formatted for output to a kickstart file."""
444 return ""
445
446 def __call__(self, *args, **kwargs):
447 """Set multiple attributes on a subclass of BaseData at once via
448 keyword arguments. Valid attributes are anything specified in a
449 subclass, but unknown attributes will be ignored.
450 """
451 for (key, val) in kwargs.items():
452 # Ignore setting attributes that were removed in a subclass, as
453 # if they were unknown attributes.
454 if key in self.removedAttrs:
455 continue
456
457 if hasattr(self, key):
458 setattr(self, key, val)
459
460 def deleteRemovedAttrs(self):
461 """Remove all attributes from self that are given in the removedAttrs
462 list. This method should be called from __init__ in a subclass,
463 but only after the superclass's __init__ method has been called.
464 """
465 for attr in filter(lambda k: hasattr(self, k), self.removedAttrs):
466 delattr(self, attr)
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py
new file mode 100644
index 0000000000..da48ff50d5
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py
@@ -0,0 +1,26 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20import authconfig, autopart, autostep, bootloader, clearpart, device
21import deviceprobe, displaymode, dmraid, driverdisk, fcoe, firewall, firstboot
22import group, ignoredisk, interactive, iscsi, iscsiname, key, keyboard, lang
23import langsupport, lilocheck, logging, logvol, mediacheck, method, monitor
24import mouse, multipath, network, partition, raid, reboot, repo, rescue, rootpw
25import selinux, services, skipx, sshpw, timezone, updates, upgrade, user, vnc
26import volgroup, xconfig, zerombr, zfcp
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py b/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py
new file mode 100644
index 0000000000..9af9c0ff14
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py
@@ -0,0 +1,40 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21
22class FC3_Authconfig(KickstartCommand):
23 removedKeywords = KickstartCommand.removedKeywords
24 removedAttrs = KickstartCommand.removedAttrs
25
26 def __init__(self, writePriority=0, *args, **kwargs):
27 KickstartCommand.__init__(self, *args, **kwargs)
28 self.authconfig = kwargs.get("authconfig", "")
29
30 def __str__(self):
31 retval = KickstartCommand.__str__(self)
32
33 if self.authconfig:
34 retval += "# System authorization information\nauth %s\n" % self.authconfig
35
36 return retval
37
38 def parse(self, args):
39 self.authconfig = self.currentLine[len(self.currentCmd):].strip()
40 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py b/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py
new file mode 100644
index 0000000000..cf28b5c7f7
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py
@@ -0,0 +1,119 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_AutoPart(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=100, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.autopart = kwargs.get("autopart", False)
34
35 def __str__(self):
36 retval = KickstartCommand.__str__(self)
37
38 if self.autopart:
39 retval += "autopart\n"
40
41 return retval
42
43 def parse(self, args):
44 if len(args) > 0:
45 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "autopart")
46
47 self.autopart = True
48 return self
49
50class F9_AutoPart(FC3_AutoPart):
51 removedKeywords = FC3_AutoPart.removedKeywords
52 removedAttrs = FC3_AutoPart.removedAttrs
53
54 def __init__(self, writePriority=100, *args, **kwargs):
55 FC3_AutoPart.__init__(self, writePriority=writePriority, *args, **kwargs)
56 self.encrypted = kwargs.get("encrypted", False)
57 self.passphrase = kwargs.get("passphrase", "")
58
59 self.op = self._getParser()
60
61 def __str__(self):
62 retval = KickstartCommand.__str__(self)
63
64 if self.autopart:
65 retval += "autopart"
66
67 if self.encrypted:
68 retval += " --encrypted"
69
70 if self.passphrase != "":
71 retval += " --passphrase=\"%s\""% self.passphrase
72
73 if retval != "":
74 retval += "\n"
75
76 return retval
77
78 def _getParser(self):
79 op = KSOptionParser()
80 op.add_option("--encrypted", action="store_true", default=False)
81 op.add_option("--passphrase")
82 return op
83
84 def parse(self, args):
85 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
86 self._setToSelf(self.op, opts)
87 self.autopart = True
88 return self
89
90class F12_AutoPart(F9_AutoPart):
91 removedKeywords = F9_AutoPart.removedKeywords
92 removedAttrs = F9_AutoPart.removedAttrs
93
94 def __init__(self, writePriority=100, *args, **kwargs):
95 F9_AutoPart.__init__(self, writePriority=writePriority, *args, **kwargs)
96
97 self.escrowcert = kwargs.get("escrowcert", "")
98 self.backuppassphrase = kwargs.get("backuppassphrase", False)
99
100 def __str__(self):
101 retval = F9_AutoPart.__str__(self)
102
103 if self.encrypted and self.escrowcert != "":
104 retval = retval.strip()
105
106 retval += " --escrowcert=\"%s\"" % self.escrowcert
107
108 if self.backuppassphrase:
109 retval += " --backuppassphrase"
110
111 retval += "\n"
112
113 return retval
114
115 def _getParser(self):
116 op = F9_AutoPart._getParser(self)
117 op.add_option("--escrowcert")
118 op.add_option("--backuppassphrase", action="store_true", default=False)
119 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py b/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py
new file mode 100644
index 0000000000..e6ae71cefc
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py
@@ -0,0 +1,55 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23class FC3_AutoStep(KickstartCommand):
24 removedKeywords = KickstartCommand.removedKeywords
25 removedAttrs = KickstartCommand.removedAttrs
26
27 def __init__(self, writePriority=0, *args, **kwargs):
28 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
29 self.op = self._getParser()
30
31 self.autostep = kwargs.get("autostep", False)
32 self.autoscreenshot = kwargs.get("autoscreenshot", False)
33
34 def __str__(self):
35 retval = KickstartCommand.__str__(self)
36
37 if self.autostep:
38 if self.autoscreenshot:
39 retval += "autostep --autoscreenshot\n"
40 else:
41 retval += "autostep\n"
42
43 return retval
44
45 def _getParser(self):
46 op = KSOptionParser()
47 op.add_option("--autoscreenshot", dest="autoscreenshot",
48 action="store_true", default=False)
49 return op
50
51 def parse(self, args):
52 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
53 self._setToSelf(self.op, opts)
54 self.autostep = True
55 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py b/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py
new file mode 100644
index 0000000000..b227fac3be
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py
@@ -0,0 +1,265 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23class FC3_Bootloader(KickstartCommand):
24 removedKeywords = KickstartCommand.removedKeywords
25 removedAttrs = KickstartCommand.removedAttrs
26
27 def __init__(self, writePriority=10, *args, **kwargs):
28 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
29 self.op = self._getParser()
30
31 self.driveorder = kwargs.get("driveorder", [])
32 self.appendLine = kwargs.get("appendLine", "")
33 self.forceLBA = kwargs.get("forceLBA", False)
34 self.linear = kwargs.get("linear", True)
35 self.location = kwargs.get("location", "")
36 self.md5pass = kwargs.get("md5pass", "")
37 self.password = kwargs.get("password", "")
38 self.upgrade = kwargs.get("upgrade", False)
39 self.useLilo = kwargs.get("useLilo", False)
40
41 self.deleteRemovedAttrs()
42
43 def _getArgsAsStr(self):
44 retval = ""
45
46 if self.appendLine != "":
47 retval += " --append=\"%s\"" % self.appendLine
48 if self.linear:
49 retval += " --linear"
50 if self.location:
51 retval += " --location=%s" % self.location
52 if hasattr(self, "forceLBA") and self.forceLBA:
53 retval += " --lba32"
54 if self.password != "":
55 retval += " --password=\"%s\"" % self.password
56 if self.md5pass != "":
57 retval += " --md5pass=\"%s\"" % self.md5pass
58 if self.upgrade:
59 retval += " --upgrade"
60 if self.useLilo:
61 retval += " --useLilo"
62 if len(self.driveorder) > 0:
63 retval += " --driveorder=\"%s\"" % ",".join(self.driveorder)
64
65 return retval
66
67 def __str__(self):
68 retval = KickstartCommand.__str__(self)
69
70 if self.location != "":
71 retval += "# System bootloader configuration\nbootloader"
72 retval += self._getArgsAsStr() + "\n"
73
74 return retval
75
76 def _getParser(self):
77 def driveorder_cb (option, opt_str, value, parser):
78 for d in value.split(','):
79 parser.values.ensure_value(option.dest, []).append(d)
80
81 op = KSOptionParser()
82 op.add_option("--append", dest="appendLine")
83 op.add_option("--linear", dest="linear", action="store_true",
84 default=True)
85 op.add_option("--nolinear", dest="linear", action="store_false")
86 op.add_option("--location", dest="location", type="choice",
87 default="mbr",
88 choices=["mbr", "partition", "none", "boot"])
89 op.add_option("--lba32", dest="forceLBA", action="store_true",
90 default=False)
91 op.add_option("--password", dest="password", default="")
92 op.add_option("--md5pass", dest="md5pass", default="")
93 op.add_option("--upgrade", dest="upgrade", action="store_true",
94 default=False)
95 op.add_option("--useLilo", dest="useLilo", action="store_true",
96 default=False)
97 op.add_option("--driveorder", dest="driveorder", action="callback",
98 callback=driveorder_cb, nargs=1, type="string")
99 return op
100
101 def parse(self, args):
102 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
103 self._setToSelf(self.op, opts)
104
105 if self.currentCmd == "lilo":
106 self.useLilo = True
107
108 return self
109
110class FC4_Bootloader(FC3_Bootloader):
111 removedKeywords = FC3_Bootloader.removedKeywords + ["linear", "useLilo"]
112 removedAttrs = FC3_Bootloader.removedAttrs + ["linear", "useLilo"]
113
114 def __init__(self, writePriority=10, *args, **kwargs):
115 FC3_Bootloader.__init__(self, writePriority, *args, **kwargs)
116
117 def _getArgsAsStr(self):
118 retval = ""
119 if self.appendLine != "":
120 retval += " --append=\"%s\"" % self.appendLine
121 if self.location:
122 retval += " --location=%s" % self.location
123 if hasattr(self, "forceLBA") and self.forceLBA:
124 retval += " --lba32"
125 if self.password != "":
126 retval += " --password=\"%s\"" % self.password
127 if self.md5pass != "":
128 retval += " --md5pass=\"%s\"" % self.md5pass
129 if self.upgrade:
130 retval += " --upgrade"
131 if len(self.driveorder) > 0:
132 retval += " --driveorder=\"%s\"" % ",".join(self.driveorder)
133 return retval
134
135 def _getParser(self):
136 op = FC3_Bootloader._getParser(self)
137 op.remove_option("--linear")
138 op.remove_option("--nolinear")
139 op.remove_option("--useLilo")
140 return op
141
142 def parse(self, args):
143 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
144 self._setToSelf(self.op, opts)
145 return self
146
147class F8_Bootloader(FC4_Bootloader):
148 removedKeywords = FC4_Bootloader.removedKeywords
149 removedAttrs = FC4_Bootloader.removedAttrs
150
151 def __init__(self, writePriority=10, *args, **kwargs):
152 FC4_Bootloader.__init__(self, writePriority, *args, **kwargs)
153
154 self.timeout = kwargs.get("timeout", None)
155 self.default = kwargs.get("default", "")
156
157 def _getArgsAsStr(self):
158 ret = FC4_Bootloader._getArgsAsStr(self)
159
160 if self.timeout is not None:
161 ret += " --timeout=%d" %(self.timeout,)
162 if self.default:
163 ret += " --default=%s" %(self.default,)
164
165 return ret
166
167 def _getParser(self):
168 op = FC4_Bootloader._getParser(self)
169 op.add_option("--timeout", dest="timeout", type="int")
170 op.add_option("--default", dest="default")
171 return op
172
173class F12_Bootloader(F8_Bootloader):
174 removedKeywords = F8_Bootloader.removedKeywords
175 removedAttrs = F8_Bootloader.removedAttrs
176
177 def _getParser(self):
178 op = F8_Bootloader._getParser(self)
179 op.add_option("--lba32", dest="forceLBA", deprecated=1, action="store_true")
180 return op
181
182class F14_Bootloader(F12_Bootloader):
183 removedKeywords = F12_Bootloader.removedKeywords + ["forceLBA"]
184 removedAttrs = F12_Bootloader.removedKeywords + ["forceLBA"]
185
186 def _getParser(self):
187 op = F12_Bootloader._getParser(self)
188 op.remove_option("--lba32")
189 return op
190
191class F15_Bootloader(F14_Bootloader):
192 removedKeywords = F14_Bootloader.removedKeywords
193 removedAttrs = F14_Bootloader.removedAttrs
194
195 def __init__(self, writePriority=10, *args, **kwargs):
196 F14_Bootloader.__init__(self, writePriority, *args, **kwargs)
197
198 self.isCrypted = kwargs.get("isCrypted", False)
199
200 def _getArgsAsStr(self):
201 ret = F14_Bootloader._getArgsAsStr(self)
202
203 if self.isCrypted:
204 ret += " --iscrypted"
205
206 return ret
207
208 def _getParser(self):
209 def password_cb(option, opt_str, value, parser):
210 parser.values.isCrypted = True
211 parser.values.password = value
212
213 op = F14_Bootloader._getParser(self)
214 op.add_option("--iscrypted", dest="isCrypted", action="store_true", default=False)
215 op.add_option("--md5pass", action="callback", callback=password_cb, nargs=1, type="string")
216 return op
217
218class RHEL5_Bootloader(FC4_Bootloader):
219 removedKeywords = FC4_Bootloader.removedKeywords
220 removedAttrs = FC4_Bootloader.removedAttrs
221
222 def __init__(self, writePriority=10, *args, **kwargs):
223 FC4_Bootloader.__init__(self, writePriority, *args, **kwargs)
224
225 self.hvArgs = kwargs.get("hvArgs", "")
226
227 def _getArgsAsStr(self):
228 ret = FC4_Bootloader._getArgsAsStr(self)
229
230 if self.hvArgs:
231 ret += " --hvargs=\"%s\"" %(self.hvArgs,)
232
233 return ret
234
235 def _getParser(self):
236 op = FC4_Bootloader._getParser(self)
237 op.add_option("--hvargs", dest="hvArgs", type="string")
238 return op
239
240class RHEL6_Bootloader(F12_Bootloader):
241 removedKeywords = F12_Bootloader.removedKeywords
242 removedAttrs = F12_Bootloader.removedAttrs
243
244 def __init__(self, writePriority=10, *args, **kwargs):
245 F12_Bootloader.__init__(self, writePriority, *args, **kwargs)
246
247 self.isCrypted = kwargs.get("isCrypted", False)
248
249 def _getArgsAsStr(self):
250 ret = F12_Bootloader._getArgsAsStr(self)
251
252 if self.isCrypted:
253 ret += " --iscrypted"
254
255 return ret
256
257 def _getParser(self):
258 def password_cb(option, opt_str, value, parser):
259 parser.values.isCrypted = True
260 parser.values.password = value
261
262 op = F12_Bootloader._getParser(self)
263 op.add_option("--iscrypted", dest="isCrypted", action="store_true", default=False)
264 op.add_option("--md5pass", action="callback", callback=password_cb, nargs=1, type="string")
265 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py b/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py
new file mode 100644
index 0000000000..a8089fcb99
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py
@@ -0,0 +1,86 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25class FC3_ClearPart(KickstartCommand):
26 removedKeywords = KickstartCommand.removedKeywords
27 removedAttrs = KickstartCommand.removedAttrs
28
29 def __init__(self, writePriority=120, *args, **kwargs):
30 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
31 self.op = self._getParser()
32
33 self.drives = kwargs.get("drives", [])
34 self.initAll = kwargs.get("initAll", False)
35 self.type = kwargs.get("type", None)
36
37 def __str__(self):
38 retval = KickstartCommand.__str__(self)
39
40 if self.type is None:
41 return retval
42
43 if self.type == CLEARPART_TYPE_NONE:
44 clearstr = "--none"
45 elif self.type == CLEARPART_TYPE_LINUX:
46 clearstr = "--linux"
47 elif self.type == CLEARPART_TYPE_ALL:
48 clearstr = "--all"
49 else:
50 clearstr = ""
51
52 if self.initAll:
53 initstr = "--initlabel"
54 else:
55 initstr = ""
56
57 if len(self.drives) > 0:
58 drivestr = "--drives=" + ",".join(self.drives)
59 else:
60 drivestr = ""
61
62 retval += "# Partition clearing information\nclearpart %s %s %s\n" % (clearstr, initstr, drivestr)
63 return retval
64
65 def _getParser(self):
66 def drive_cb (option, opt_str, value, parser):
67 for d in value.split(','):
68 parser.values.ensure_value(option.dest, []).append(d)
69
70 op = KSOptionParser()
71 op.add_option("--all", dest="type", action="store_const",
72 const=CLEARPART_TYPE_ALL)
73 op.add_option("--drives", dest="drives", action="callback",
74 callback=drive_cb, nargs=1, type="string")
75 op.add_option("--initlabel", dest="initAll", action="store_true",
76 default=False)
77 op.add_option("--linux", dest="type", action="store_const",
78 const=CLEARPART_TYPE_LINUX)
79 op.add_option("--none", dest="type", action="store_const",
80 const=CLEARPART_TYPE_NONE)
81 return op
82
83 def parse(self, args):
84 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
85 self._setToSelf(self.op, opts)
86 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/device.py b/scripts/lib/mic/3rdparty/pykickstart/commands/device.py
new file mode 100644
index 0000000000..321410e2e2
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/device.py
@@ -0,0 +1,125 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23import gettext
24import warnings
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class F8_DeviceData(BaseData):
28 removedKeywords = BaseData.removedKeywords
29 removedAttrs = BaseData.removedAttrs
30
31 def __init__(self, *args, **kwargs):
32 BaseData.__init__(self, *args, **kwargs)
33 self.moduleName = kwargs.get("moduleName", "")
34 self.moduleOpts = kwargs.get("moduleOpts", "")
35
36 def __eq__(self, y):
37 return self.moduleName == y.moduleName
38
39 def __str__(self):
40 retval = BaseData.__str__(self)
41
42 if self.moduleName != "":
43 retval += "device %s" % self.moduleName
44
45 if self.moduleOpts != "":
46 retval += " --opts=\"%s\"" % self.moduleOpts
47
48 return retval + "\n"
49
50class FC3_Device(KickstartCommand):
51 removedKeywords = KickstartCommand.removedKeywords
52 removedAttrs = KickstartCommand.removedAttrs
53
54 def __init__(self, writePriority=0, *args, **kwargs):
55 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
56 self.op = self._getParser()
57
58 self.type = kwargs.get("type", "")
59 self.moduleName = kwargs.get("moduleName", "")
60 self.moduleOpts = kwargs.get("moduleOpts", "")
61
62 def __eq__(self, y):
63 return self.moduleName == y.moduleName
64
65 def __str__(self):
66 retval = KickstartCommand.__str__(self)
67
68 if self.moduleName != "":
69 retval += "device %s %s" % (self.type, self.moduleName)
70
71 if self.moduleOpts != "":
72 retval += " --opts=\"%s\"" % self.moduleOpts
73
74 return retval + "\n"
75
76 def _getParser(self):
77 op = KSOptionParser()
78 op.add_option("--opts", dest="moduleOpts", default="")
79 return op
80
81 def parse(self, args):
82 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
83
84 if len(extra) != 2:
85 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("device command requires two arguments: module type and name"))
86
87 self.moduleOpts = opts.moduleOpts
88 self.type = extra[0]
89 self.moduleName = extra[1]
90 return self
91
92class F8_Device(FC3_Device):
93 removedKeywords = FC3_Device.removedKeywords
94 removedAttrs = FC3_Device.removedAttrs
95
96 def __init__(self, writePriority=0, *args, **kwargs):
97 FC3_Device.__init__(self, writePriority, *args, **kwargs)
98 self.deviceList = kwargs.get("deviceList", [])
99
100 def __str__(self):
101 retval = ""
102 for device in self.deviceList:
103 retval += device.__str__()
104
105 return retval
106
107 def parse(self, args):
108 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
109
110 if len(extra) != 1:
111 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("%s command requires a single argument: %s") % ("device", "module name"))
112
113 dd = F8_DeviceData()
114 self._setToObj(self.op, opts, dd)
115 dd.lineno = self.lineno
116 dd.moduleName = extra[0]
117
118 # Check for duplicates in the data list.
119 if dd in self.dataList():
120 warnings.warn(_("A module with the name %s has already been defined.") % dd.moduleName)
121
122 return dd
123
124 def dataList(self):
125 return self.deviceList
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py b/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py
new file mode 100644
index 0000000000..9f462fdff7
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py
@@ -0,0 +1,40 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21
22class FC3_DeviceProbe(KickstartCommand):
23 removedKeywords = KickstartCommand.removedKeywords
24 removedAttrs = KickstartCommand.removedAttrs
25
26 def __init__(self, writePriority=0, *args, **kwargs):
27 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
28 self.deviceprobe = kwargs.get("deviceprobe", "")
29
30 def __str__(self):
31 retval = KickstartCommand.__str__(self)
32
33 if self.deviceprobe != "":
34 retval += "deviceprobe %s\n" % self.deviceprobe
35
36 return retval
37
38 def parse(self, args):
39 self.deviceprobe = " ".join(args)
40 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py b/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py
new file mode 100644
index 0000000000..6a12d58ec2
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py
@@ -0,0 +1,68 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_DisplayMode(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34 self.displayMode = kwargs.get("displayMode", None)
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if self.displayMode is None:
40 return retval
41
42 if self.displayMode == DISPLAY_MODE_CMDLINE:
43 retval += "cmdline\n"
44 elif self.displayMode == DISPLAY_MODE_GRAPHICAL:
45 retval += "# Use graphical install\ngraphical\n"
46 elif self.displayMode == DISPLAY_MODE_TEXT:
47 retval += "# Use text mode install\ntext\n"
48
49 return retval
50
51 def _getParser(self):
52 op = KSOptionParser()
53 return op
54
55 def parse(self, args):
56 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
57
58 if len(extra) > 0:
59 raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % self.currentCmd)
60
61 if self.currentCmd == "cmdline":
62 self.displayMode = DISPLAY_MODE_CMDLINE
63 elif self.currentCmd == "graphical":
64 self.displayMode = DISPLAY_MODE_GRAPHICAL
65 elif self.currentCmd == "text":
66 self.displayMode = DISPLAY_MODE_TEXT
67
68 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py b/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py
new file mode 100644
index 0000000000..993575a041
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py
@@ -0,0 +1,91 @@
1#
2# Chris Lumens <clumens@redhat.com>
3# Peter Jones <pjones@redhat.com>
4#
5# Copyright 2006, 2007 Red Hat, Inc.
6#
7# This copyrighted material is made available to anyone wishing to use, modify,
8# copy, or redistribute it subject to the terms and conditions of the GNU
9# General Public License v.2. This program is distributed in the hope that it
10# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
11# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12# See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 51
16# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
17# trademarks that are incorporated in the source code or documentation are not
18# subject to the GNU General Public License and may only be used or replicated
19# with the express permission of Red Hat, Inc.
20#
21from pykickstart.base import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26import warnings
27_ = lambda x: gettext.ldgettext("pykickstart", x)
28
29class FC6_DmRaidData(BaseData):
30 removedKeywords = BaseData.removedKeywords
31 removedAttrs = BaseData.removedAttrs
32
33 def __init__(self, *args, **kwargs):
34 BaseData.__init__(self, *args, **kwargs)
35
36 self.name = kwargs.get("name", "")
37 self.devices = kwargs.get("devices", [])
38 self.dmset = kwargs.get("dmset", None)
39
40 def __eq__(self, y):
41 return self.name == y.name and self.devices == y.devices
42
43 def __str__(self):
44 retval = BaseData.__str__(self)
45 retval += "dmraid --name=%s" % self.name
46
47 for dev in self.devices:
48 retval += " --dev=\"%s\"" % dev
49
50 return retval + "\n"
51
52class FC6_DmRaid(KickstartCommand):
53 removedKeywords = KickstartCommand.removedKeywords
54 removedAttrs = KickstartCommand.removedAttrs
55
56 def __init__(self, writePriority=60, *args, **kwargs):
57 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
58 self.op = self._getParser()
59
60 self.dmraids = kwargs.get("dmraids", [])
61
62 def __str__(self):
63 retval = ""
64 for dm in self.dmraids:
65 retval += dm.__str__()
66
67 return retval
68
69 def _getParser(self):
70 op = KSOptionParser()
71 op.add_option("--name", dest="name", action="store", type="string",
72 required=1)
73 op.add_option("--dev", dest="devices", action="append", type="string",
74 required=1)
75 return op
76
77 def parse(self, args):
78 dm = FC6_DmRaidData()
79 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
80 dm.name = dm.name.split('/')[-1]
81 self._setToObj(self.op, opts, dm)
82 dm.lineno = self.lineno
83
84 # Check for duplicates in the data list.
85 if dm in self.dataList():
86 warnings.warn(_("A DM RAID device with the name %s and devices %s has already been defined.") % (dm.name, dm.devices))
87
88 return dm
89
90 def dataList(self):
91 return self.dmraids
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py b/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py
new file mode 100644
index 0000000000..82a58c0e28
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py
@@ -0,0 +1,184 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23import gettext
24_ = lambda x: gettext.ldgettext("pykickstart", x)
25
26class FC3_DriverDiskData(BaseData):
27 removedKeywords = BaseData.removedKeywords
28 removedAttrs = BaseData.removedAttrs
29
30 def __init__(self, writePriority=0, *args, **kwargs):
31 BaseData.__init__(self, *args, **kwargs)
32
33 self.partition = kwargs.get("partition", "")
34 self.source = kwargs.get("source", "")
35 self.type = kwargs.get("type", "")
36
37 def _getArgsAsStr(self):
38 retval = ""
39
40 if self.partition:
41 retval += "%s" % self.partition
42
43 if hasattr(self, "type") and self.type:
44 retval += " --type=%s" % self.type
45 elif self.source:
46 retval += "--source=%s" % self.source
47 return retval
48
49 def __str__(self):
50 retval = BaseData.__str__(self)
51 retval += "driverdisk %s\n" % self._getArgsAsStr()
52 return retval
53
54class FC4_DriverDiskData(FC3_DriverDiskData):
55 removedKeywords = FC3_DriverDiskData.removedKeywords
56 removedAttrs = FC3_DriverDiskData.removedAttrs
57
58 def __init__(self, writePriority=0, *args, **kwargs):
59 FC3_DriverDiskData.__init__(self, *args, **kwargs)
60 self.deleteRemovedAttrs()
61
62 self.biospart = kwargs.get("biospart", "")
63
64 def _getArgsAsStr(self):
65 retval = ""
66
67 if self.partition:
68 retval += "%s" % self.partition
69
70 if hasattr(self, "type") and self.type:
71 retval += " --type=%s" % self.type
72 elif self.source:
73 retval += "--source=%s" % self.source
74 elif self.biospart:
75 retval += "--biospart=%s" % self.biospart
76
77 return retval
78
79class F12_DriverDiskData(FC4_DriverDiskData):
80 removedKeywords = FC4_DriverDiskData.removedKeywords + ["type"]
81 removedAttrs = FC4_DriverDiskData.removedAttrs + ["type"]
82
83 def __init__(self, *args, **kwargs):
84 FC4_DriverDiskData.__init__(self, *args, **kwargs)
85 self.deleteRemovedAttrs()
86
87F14_DriverDiskData = F12_DriverDiskData
88
89class FC3_DriverDisk(KickstartCommand):
90 removedKeywords = KickstartCommand.removedKeywords
91 removedAttrs = KickstartCommand.removedAttrs
92
93 def __init__(self, writePriority=0, *args, **kwargs):
94 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
95 self.op = self._getParser()
96
97 self.driverdiskList = kwargs.get("driverdiskList", [])
98
99 def __str__(self):
100 retval = ""
101 for dd in self.driverdiskList:
102 retval += dd.__str__()
103
104 return retval
105
106 def _getParser(self):
107 op = KSOptionParser()
108 op.add_option("--source")
109 op.add_option("--type")
110 return op
111
112 def parse(self, args):
113 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
114
115 if len(extra) > 1:
116 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one partition may be specified for driverdisk command."))
117
118 if len(extra) == 1 and opts.source:
119 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --source and partition may be specified for driverdisk command."))
120
121 if not extra and not opts.source:
122 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --source or partition must be specified for driverdisk command."))
123
124 ddd = self.handler.DriverDiskData()
125 self._setToObj(self.op, opts, ddd)
126 ddd.lineno = self.lineno
127 if len(extra) == 1:
128 ddd.partition = extra[0]
129
130 return ddd
131
132 def dataList(self):
133 return self.driverdiskList
134
135class FC4_DriverDisk(FC3_DriverDisk):
136 removedKeywords = FC3_DriverDisk.removedKeywords
137 removedAttrs = FC3_DriverDisk.removedKeywords
138
139 def _getParser(self):
140 op = FC3_DriverDisk._getParser(self)
141 op.add_option("--biospart")
142 return op
143
144 def parse(self, args):
145 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
146
147 if len(extra) > 1:
148 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one partition may be specified for driverdisk command."))
149
150 if len(extra) == 1 and opts.source:
151 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --source and partition may be specified for driverdisk command."))
152 elif len(extra) == 1 and opts.biospart:
153 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --biospart and partition may be specified for driverdisk command."))
154 elif opts.source and opts.biospart:
155 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --biospart and --source may be specified for driverdisk command."))
156
157 if not extra and not opts.source and not opts.biospart:
158 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --source, --biospart, or partition must be specified for driverdisk command."))
159
160 ddd = self.handler.DriverDiskData()
161 self._setToObj(self.op, opts, ddd)
162 ddd.lineno = self.lineno
163 if len(extra) == 1:
164 ddd.partition = extra[0]
165
166 return ddd
167
168class F12_DriverDisk(FC4_DriverDisk):
169 removedKeywords = FC4_DriverDisk.removedKeywords
170 removedAttrs = FC4_DriverDisk.removedKeywords
171
172 def _getParser(self):
173 op = FC4_DriverDisk._getParser(self)
174 op.add_option("--type", deprecated=1)
175 return op
176
177class F14_DriverDisk(F12_DriverDisk):
178 removedKeywords = F12_DriverDisk.removedKeywords
179 removedAttrs = F12_DriverDisk.removedKeywords
180
181 def _getParser(self):
182 op = F12_DriverDisk._getParser(self)
183 op.remove_option("--type")
184 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py b/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py
new file mode 100644
index 0000000000..33208499b3
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py
@@ -0,0 +1,114 @@
1#
2# Hans de Goede <hdegoede@redhat.com>
3#
4# Copyright 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23import gettext
24import warnings
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class F12_FcoeData(BaseData):
28 removedKeywords = BaseData.removedKeywords
29 removedAttrs = BaseData.removedAttrs
30
31 def __init__(self, *args, **kwargs):
32 BaseData.__init__(self, *args, **kwargs)
33 self.nic = kwargs.get("nic", None)
34
35 def __eq__(self, y):
36 return self.nic == y.nic
37
38 def _getArgsAsStr(self):
39 retval = ""
40
41 if self.nic:
42 retval += " --nic=%s" % self.nic
43
44 return retval
45
46 def __str__(self):
47 retval = BaseData.__str__(self)
48 retval += "fcoe%s\n" % self._getArgsAsStr()
49 return retval
50
51class F13_FcoeData(F12_FcoeData):
52 removedKeywords = F12_FcoeData.removedKeywords
53 removedAttrs = F12_FcoeData.removedAttrs
54
55 def __init__(self, *args, **kwargs):
56 F12_FcoeData.__init__(self, *args, **kwargs)
57 self.dcb = kwargs.get("dcb", False)
58
59 def _getArgsAsStr(self):
60 retval = F12_FcoeData._getArgsAsStr(self)
61
62 if self.dcb:
63 retval += " --dcb"
64
65 return retval
66
67class F12_Fcoe(KickstartCommand):
68 removedKeywords = KickstartCommand.removedKeywords
69 removedAttrs = KickstartCommand.removedAttrs
70
71 def __init__(self, writePriority=71, *args, **kwargs):
72 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
73 self.op = self._getParser()
74 self.fcoe = kwargs.get("fcoe", [])
75
76 def __str__(self):
77 retval = ""
78 for fcoe in self.fcoe:
79 retval += fcoe.__str__()
80
81 return retval
82
83 def _getParser(self):
84 op = KSOptionParser()
85 op.add_option("--nic", dest="nic", required=1)
86 return op
87
88 def parse(self, args):
89 zd = self.handler.FcoeData()
90 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
91 if len(extra) > 0:
92 mapping = {"command": "fcoe", "options": extra}
93 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
94
95 self._setToObj(self.op, opts, zd)
96 zd.lineno = self.lineno
97
98 # Check for duplicates in the data list.
99 if zd in self.dataList():
100 warnings.warn(_("A FCOE device with the name %s has already been defined.") % zd.nic)
101
102 return zd
103
104 def dataList(self):
105 return self.fcoe
106
107class F13_Fcoe(F12_Fcoe):
108 removedKeywords = F12_Fcoe.removedKeywords
109 removedAttrs = F12_Fcoe.removedAttrs
110
111 def _getParser(self):
112 op = F12_Fcoe._getParser(self)
113 op.add_option("--dcb", dest="dcb", action="store_true", default=False)
114 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py b/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py
new file mode 100644
index 0000000000..24a01bd610
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py
@@ -0,0 +1,193 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_Firewall(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.enabled = kwargs.get("enabled", None)
36 self.ports = kwargs.get("ports", [])
37 self.trusts = kwargs.get("trusts", [])
38
39 def __str__(self):
40 extra = []
41 filteredPorts = []
42
43 retval = KickstartCommand.__str__(self)
44
45 if self.enabled is None:
46 return retval
47
48 if self.enabled:
49 # It's possible we have words in the ports list instead of
50 # port:proto (s-c-kickstart may do this). So, filter those
51 # out into their own list leaving what we expect.
52 for port in self.ports:
53 if port == "ssh":
54 extra.append(" --ssh")
55 elif port == "telnet":
56 extra.append(" --telnet")
57 elif port == "smtp":
58 extra.append(" --smtp")
59 elif port == "http":
60 extra.append(" --http")
61 elif port == "ftp":
62 extra.append(" --ftp")
63 else:
64 filteredPorts.append(port)
65
66 # All the port:proto strings go into a comma-separated list.
67 portstr = ",".join(filteredPorts)
68 if len(portstr) > 0:
69 portstr = " --port=" + portstr
70 else:
71 portstr = ""
72
73 extrastr = "".join(extra)
74 truststr = ",".join(self.trusts)
75
76 if len(truststr) > 0:
77 truststr = " --trust=" + truststr
78
79 # The output port list consists only of port:proto for
80 # everything that we don't recognize, and special options for
81 # those that we do.
82 retval += "# Firewall configuration\nfirewall --enabled%s%s%s\n" % (extrastr, portstr, truststr)
83 else:
84 retval += "# Firewall configuration\nfirewall --disabled\n"
85
86 return retval
87
88 def _getParser(self):
89 def firewall_port_cb (option, opt_str, value, parser):
90 for p in value.split(","):
91 p = p.strip()
92 if p.find(":") == -1:
93 p = "%s:tcp" % p
94 parser.values.ensure_value(option.dest, []).append(p)
95
96 op = KSOptionParser(mapping={"ssh":["22:tcp"], "telnet":["23:tcp"],
97 "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"],
98 "ftp":["21:tcp"]})
99
100 op.add_option("--disable", "--disabled", dest="enabled",
101 action="store_false")
102 op.add_option("--enable", "--enabled", dest="enabled",
103 action="store_true", default=True)
104 op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet",
105 dest="ports", action="map_extend")
106 op.add_option("--high", deprecated=1)
107 op.add_option("--medium", deprecated=1)
108 op.add_option("--port", dest="ports", action="callback",
109 callback=firewall_port_cb, nargs=1, type="string")
110 op.add_option("--trust", dest="trusts", action="append")
111 return op
112
113 def parse(self, args):
114 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
115
116 if len(extra) != 0:
117 mapping = {"command": "firewall", "options": extra}
118 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
119
120 self._setToSelf(self.op, opts)
121 return self
122
123class F9_Firewall(FC3_Firewall):
124 removedKeywords = FC3_Firewall.removedKeywords
125 removedAttrs = FC3_Firewall.removedAttrs
126
127 def _getParser(self):
128 op = FC3_Firewall._getParser(self)
129 op.remove_option("--high")
130 op.remove_option("--medium")
131 return op
132
133class F10_Firewall(F9_Firewall):
134 removedKeywords = F9_Firewall.removedKeywords
135 removedAttrs = F9_Firewall.removedAttrs
136
137 def __init__(self, writePriority=0, *args, **kwargs):
138 F9_Firewall.__init__(self, writePriority, *args, **kwargs)
139 self.services = kwargs.get("services", [])
140
141 def __str__(self):
142 if self.enabled is None:
143 return ""
144
145 retval = F9_Firewall.__str__(self)
146 if self.enabled:
147 retval = retval.strip()
148
149 svcstr = ",".join(self.services)
150 if len(svcstr) > 0:
151 svcstr = " --service=" + svcstr
152 else:
153 svcstr = ""
154
155 return retval + "%s\n" % svcstr
156 else:
157 return retval
158
159 def _getParser(self):
160 def service_cb (option, opt_str, value, parser):
161 # python2.4 does not support action="append_const" that we were
162 # using for these options. Instead, we have to fake it by
163 # appending whatever the option string is to the service list.
164 if not value:
165 parser.values.ensure_value(option.dest, []).append(opt_str[2:])
166 return
167
168 for p in value.split(","):
169 p = p.strip()
170 parser.values.ensure_value(option.dest, []).append(p)
171
172 op = F9_Firewall._getParser(self)
173 op.add_option("--service", dest="services", action="callback",
174 callback=service_cb, nargs=1, type="string")
175 op.add_option("--ftp", dest="services", action="callback",
176 callback=service_cb)
177 op.add_option("--http", dest="services", action="callback",
178 callback=service_cb)
179 op.add_option("--smtp", dest="services", action="callback",
180 callback=service_cb)
181 op.add_option("--ssh", dest="services", action="callback",
182 callback=service_cb)
183 op.add_option("--telnet", deprecated=1)
184 return op
185
186class F14_Firewall(F10_Firewall):
187 removedKeywords = F10_Firewall.removedKeywords + ["telnet"]
188 removedAttrs = F10_Firewall.removedAttrs + ["telnet"]
189
190 def _getParser(self):
191 op = F10_Firewall._getParser(self)
192 op.remove_option("--telnet")
193 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py b/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py
new file mode 100644
index 0000000000..05c0ac11c6
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py
@@ -0,0 +1,62 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.options import *
23
24class FC3_Firstboot(KickstartCommand):
25 removedKeywords = KickstartCommand.removedKeywords
26 removedAttrs = KickstartCommand.removedAttrs
27
28 def __init__(self, writePriority=0, *args, **kwargs):
29 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
30 self.op = self._getParser()
31
32 self.firstboot = kwargs.get("firstboot", None)
33
34 def __str__(self):
35 retval = KickstartCommand.__str__(self)
36
37 if self.firstboot is None:
38 return retval
39
40 if self.firstboot == FIRSTBOOT_SKIP:
41 retval += "firstboot --disable\n"
42 elif self.firstboot == FIRSTBOOT_DEFAULT:
43 retval += "# Run the Setup Agent on first boot\nfirstboot --enable\n"
44 elif self.firstboot == FIRSTBOOT_RECONFIG:
45 retval += "# Run the Setup Agent on first boot\nfirstboot --reconfig\n"
46
47 return retval
48
49 def _getParser(self):
50 op = KSOptionParser()
51 op.add_option("--disable", "--disabled", dest="firstboot",
52 action="store_const", const=FIRSTBOOT_SKIP)
53 op.add_option("--enable", "--enabled", dest="firstboot",
54 action="store_const", const=FIRSTBOOT_DEFAULT)
55 op.add_option("--reconfig", dest="firstboot", action="store_const",
56 const=FIRSTBOOT_RECONFIG)
57 return op
58
59 def parse(self, args):
60 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
61 self.firstboot = opts.firstboot
62 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/group.py b/scripts/lib/mic/3rdparty/pykickstart/commands/group.py
new file mode 100644
index 0000000000..80ba5bdca6
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/group.py
@@ -0,0 +1,88 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26import warnings
27_ = lambda x: gettext.ldgettext("pykickstart", x)
28
29class F12_GroupData(BaseData):
30 removedKeywords = BaseData.removedKeywords
31 removedAttrs = BaseData.removedAttrs
32
33 def __init__(self, *args, **kwargs):
34 BaseData.__init__(self, *args, **kwargs)
35 self.name = kwargs.get("name", "")
36 self.gid = kwargs.get("gid", None)
37
38 def __eq__(self, y):
39 return self.name == y.name
40
41 def __str__(self):
42 retval = BaseData.__str__(self)
43 retval += "group"
44
45 if self.name:
46 retval += " --name=%s" % self.name
47 if self.gid:
48 retval += " --gid=%s" % self.gid
49
50 return retval + "\n"
51
52class F12_Group(KickstartCommand):
53 removedKeywords = KickstartCommand.removedKeywords
54 removedAttrs = KickstartCommand.removedAttrs
55
56 def __init__(self, writePriority=0, *args, **kwargs):
57 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
58 self.op = self._getParser()
59
60 self.groupList = kwargs.get("groupList", [])
61
62 def __str__(self):
63 retval = ""
64 for user in self.groupList:
65 retval += user.__str__()
66
67 return retval
68
69 def _getParser(self):
70 op = KSOptionParser()
71 op.add_option("--name", required=1)
72 op.add_option("--gid", type="int")
73 return op
74
75 def parse(self, args):
76 gd = self.handler.GroupData()
77 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
78 self._setToObj(self.op, opts, gd)
79 gd.lineno = self.lineno
80
81 # Check for duplicates in the data list.
82 if gd in self.dataList():
83 warnings.warn(_("A group with the name %s has already been defined.") % gd.name)
84
85 return gd
86
87 def dataList(self):
88 return self.groupList
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py b/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py
new file mode 100644
index 0000000000..676d080836
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py
@@ -0,0 +1,139 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23import gettext
24_ = lambda x: gettext.ldgettext("pykickstart", x)
25
26class FC3_IgnoreDisk(KickstartCommand):
27 removedKeywords = KickstartCommand.removedKeywords
28 removedAttrs = KickstartCommand.removedAttrs
29
30 def __init__(self, writePriority=0, *args, **kwargs):
31 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
32 self.op = self._getParser()
33
34 self.ignoredisk = kwargs.get("ignoredisk", [])
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if len(self.ignoredisk) > 0:
40 retval += "ignoredisk --drives=%s\n" % ",".join(self.ignoredisk)
41
42 return retval
43
44 def _getParser(self):
45 def drive_cb (option, opt_str, value, parser):
46 for d in value.split(','):
47 parser.values.ensure_value(option.dest, []).append(d)
48
49 op = KSOptionParser()
50 op.add_option("--drives", dest="ignoredisk", action="callback",
51 callback=drive_cb, nargs=1, type="string", required=1)
52 return op
53
54 def parse(self, args):
55 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
56 self._setToSelf(self.op, opts)
57 return self
58
59class F8_IgnoreDisk(FC3_IgnoreDisk):
60 removedKeywords = FC3_IgnoreDisk.removedKeywords
61 removedAttrs = FC3_IgnoreDisk.removedAttrs
62
63 def __init__(self, writePriority=0, *args, **kwargs):
64 FC3_IgnoreDisk.__init__(self, writePriority, *args, **kwargs)
65
66 self.onlyuse = kwargs.get("onlyuse", [])
67
68 def __str__(self):
69 retval = KickstartCommand.__str__(self)
70
71 if len(self.ignoredisk) > 0:
72 retval += "ignoredisk --drives=%s\n" % ",".join(self.ignoredisk)
73 elif len(self.onlyuse) > 0:
74 retval += "ignoredisk --only-use=%s\n" % ",".join(self.onlyuse)
75
76 return retval
77
78 def parse(self, args, errorCheck=True):
79 retval = FC3_IgnoreDisk.parse(self, args)
80
81 if errorCheck:
82 if (len(self.ignoredisk) == 0 and len(self.onlyuse) == 0) or (len(self.ignoredisk) > 0 and (len(self.onlyuse) > 0)):
83 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --drives or --only-use must be specified for ignoredisk command."))
84
85 return retval
86
87 def _getParser(self):
88 def drive_cb (option, opt_str, value, parser):
89 for d in value.split(','):
90 parser.values.ensure_value(option.dest, []).append(d)
91
92 op = FC3_IgnoreDisk._getParser(self)
93 op.add_option("--drives", dest="ignoredisk", action="callback",
94 callback=drive_cb, nargs=1, type="string")
95 op.add_option("--only-use", dest="onlyuse", action="callback",
96 callback=drive_cb, nargs=1, type="string")
97 return op
98
99class RHEL6_IgnoreDisk(F8_IgnoreDisk):
100 removedKeywords = F8_IgnoreDisk.removedKeywords
101 removedAttrs = F8_IgnoreDisk.removedAttrs
102
103 def __init__(self, writePriority=0, *args, **kwargs):
104 F8_IgnoreDisk.__init__(self, writePriority, *args, **kwargs)
105
106 self.interactive = kwargs.get("interactive", False)
107 if self.interactive:
108 self.ignoredisk = []
109
110 def __str__(self):
111 retval = F8_IgnoreDisk.__str__(self)
112
113 if self.interactive:
114 retval = "ignoredisk --interactive\n"
115
116 return retval
117
118 def parse(self, args):
119 retval = F8_IgnoreDisk.parse(self, args, errorCheck=False)
120
121 howmany = 0
122 if len(self.ignoredisk) > 0:
123 howmany += 1
124 if len(self.onlyuse) > 0:
125 howmany += 1
126 if self.interactive:
127 howmany += 1
128 if howmany != 1:
129 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --drives , --only-use , or --interactive must be specified for ignoredisk command."))
130
131 return retval
132
133 def _getParser(self):
134 op = F8_IgnoreDisk._getParser(self)
135 op.add_option("--interactive", dest="interactive", action="store_true",
136 default=False)
137 return op
138
139F14_IgnoreDisk = RHEL6_IgnoreDisk
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py b/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py
new file mode 100644
index 0000000000..fa3dc025b1
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py
@@ -0,0 +1,58 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_Interactive(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34 self.interactive = kwargs.get("interactive", False)
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if self.interactive:
40 retval += "# Use interactive kickstart installation method\ninteractive\n"
41
42 return retval
43
44 def _getParser(self):
45 op = KSOptionParser()
46 return op
47
48 def parse(self, args):
49 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
50 if len(extra) > 0:
51 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "interactive")
52
53 self.interactive = True
54 return self
55
56class F14_Interactive(DeprecatedCommand):
57 def __init__(self):
58 DeprecatedCommand.__init__(self)
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py
new file mode 100644
index 0000000000..da5a544e86
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py
@@ -0,0 +1,133 @@
1#
2# Chris Lumens <clumens@redhat.com>
3# Peter Jones <pjones@redhat.com>
4#
5# Copyright 2005, 2006, 2007 Red Hat, Inc.
6#
7# This copyrighted material is made available to anyone wishing to use, modify,
8# copy, or redistribute it subject to the terms and conditions of the GNU
9# General Public License v.2. This program is distributed in the hope that it
10# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
11# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12# See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 51
16# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
17# trademarks that are incorporated in the source code or documentation are not
18# subject to the GNU General Public License and may only be used or replicated
19# with the express permission of Red Hat, Inc.
20#
21from pykickstart.base import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26_ = lambda x: gettext.ldgettext("pykickstart", x)
27
28class FC6_IscsiData(BaseData):
29 removedKeywords = BaseData.removedKeywords
30 removedAttrs = BaseData.removedAttrs
31
32 def __init__(self, *args, **kwargs):
33 BaseData.__init__(self, *args, **kwargs)
34 self.ipaddr = kwargs.get("ipaddr", "")
35 self.port = kwargs.get("port", "3260")
36 self.target = kwargs.get("target", "")
37 self.user = kwargs.get("user", None)
38 self.password = kwargs.get("password", None)
39
40 def _getArgsAsStr(self):
41 retval = ""
42
43 if self.target != "":
44 retval += " --target=%s" % self.target
45 if self.ipaddr != "":
46 retval += " --ipaddr=%s" % self.ipaddr
47 if self.port != "3260":
48 retval += " --port=%s" % self.port
49 if self.user is not None:
50 retval += " --user=%s" % self.user
51 if self.password is not None:
52 retval += " --password=%s" % self.password
53
54 return retval
55
56 def __str__(self):
57 retval = BaseData.__str__(self)
58 retval += "iscsi%s\n" % self._getArgsAsStr()
59 return retval
60
61class F10_IscsiData(FC6_IscsiData):
62 removedKeywords = FC6_IscsiData.removedKeywords
63 removedAttrs = FC6_IscsiData.removedAttrs
64
65 def __init__(self, *args, **kwargs):
66 FC6_IscsiData.__init__(self, *args, **kwargs)
67 self.user_in = kwargs.get("user_in", None)
68 self.password_in = kwargs.get("password_in", None)
69
70 def _getArgsAsStr(self):
71 retval = FC6_IscsiData._getArgsAsStr(self)
72
73 if self.user_in is not None:
74 retval += " --reverse-user=%s" % self.user_in
75 if self.password_in is not None:
76 retval += " --reverse-password=%s" % self.password_in
77
78 return retval
79
80class FC6_Iscsi(KickstartCommand):
81 removedKeywords = KickstartCommand.removedKeywords
82 removedAttrs = KickstartCommand.removedAttrs
83
84 def __init__(self, writePriority=71, *args, **kwargs):
85 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
86 self.op = self._getParser()
87
88 self.iscsi = kwargs.get("iscsi", [])
89
90 def __str__(self):
91 retval = ""
92 for iscsi in self.iscsi:
93 retval += iscsi.__str__()
94
95 return retval
96
97 def _getParser(self):
98 op = KSOptionParser()
99 op.add_option("--target", dest="target", action="store", type="string")
100 op.add_option("--ipaddr", dest="ipaddr", action="store", type="string",
101 required=1)
102 op.add_option("--port", dest="port", action="store", type="string")
103 op.add_option("--user", dest="user", action="store", type="string")
104 op.add_option("--password", dest="password", action="store",
105 type="string")
106 return op
107
108 def parse(self, args):
109 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
110
111 if len(extra) != 0:
112 mapping = {"command": "iscsi", "options": extra}
113 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
114
115 dd = self.handler.IscsiData()
116 self._setToObj(self.op, opts, dd)
117 dd.lineno = self.lineno
118 return dd
119
120 def dataList(self):
121 return self.iscsi
122
123class F10_Iscsi(FC6_Iscsi):
124 removedKeywords = FC6_Iscsi.removedKeywords
125 removedAttrs = FC6_Iscsi.removedAttrs
126
127 def _getParser(self):
128 op = FC6_Iscsi._getParser(self)
129 op.add_option("--reverse-user", dest="user_in", action="store",
130 type="string")
131 op.add_option("--reverse-password", dest="password_in", action="store",
132 type="string")
133 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py
new file mode 100644
index 0000000000..a87d0637d6
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py
@@ -0,0 +1,54 @@
1#
2# Chris Lumens <clumens@redhat.com>
3# Peter Jones <pjones@redhat.com>
4#
5# Copyright 2006, 2007 Red Hat, Inc.
6#
7# This copyrighted material is made available to anyone wishing to use, modify,
8# copy, or redistribute it subject to the terms and conditions of the GNU
9# General Public License v.2. This program is distributed in the hope that it
10# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
11# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12# See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 51
16# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
17# trademarks that are incorporated in the source code or documentation are not
18# subject to the GNU General Public License and may only be used or replicated
19# with the express permission of Red Hat, Inc.
20#
21from pykickstart.base import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26_ = lambda x: gettext.ldgettext("pykickstart", x)
27
28class FC6_IscsiName(KickstartCommand):
29 removedKeywords = KickstartCommand.removedKeywords
30 removedAttrs = KickstartCommand.removedAttrs
31
32 def __init__(self, writePriority=70, *args, **kwargs):
33 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
34 self.op = self._getParser()
35 self.iscsiname = kwargs.get("iscsiname", "")
36
37 def __str__(self):
38 retval = KickstartCommand.__str__(self)
39
40 if self.iscsiname != "":
41 retval += "iscsiname %s\n" % self.iscsiname
42
43 return retval
44
45 def _getParser(self):
46 op = KSOptionParser()
47 return op
48
49 def parse(self, args):
50 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
51 if len(extra) != 1:
52 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "iscsiname")
53 self.iscsiname = extra[0]
54 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/key.py b/scripts/lib/mic/3rdparty/pykickstart/commands/key.py
new file mode 100644
index 0000000000..c20c4231f6
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/key.py
@@ -0,0 +1,64 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26_ = lambda x: gettext.ldgettext("pykickstart", x)
27
28class RHEL5_Key(KickstartCommand):
29 removedKeywords = KickstartCommand.removedKeywords
30 removedAttrs = KickstartCommand.removedAttrs
31
32 def __init__(self, writePriority=0, *args, **kwargs):
33 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
34 self.op = self._getParser()
35 self.key = kwargs.get("key", "")
36 self.skip = kwargs.get("skip", False)
37
38 def __str__(self):
39 retval = KickstartCommand.__str__(self)
40
41 if self.key == KS_INSTKEY_SKIP:
42 retval += "key --skip\n"
43 elif self.key != "":
44 retval += "key %s\n" % self.key
45
46 return retval
47
48 def _getParser(self):
49 op = KSOptionParser()
50 op.add_option("--skip", action="store_true", default=False)
51 return op
52
53 def parse(self, args):
54 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
55 self._setToSelf(self.op, opts)
56
57 if self.skip:
58 self.key = KS_INSTKEY_SKIP
59 elif len(extra) != 1:
60 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "key")
61 else:
62 self.key = extra[0]
63
64 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py b/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py
new file mode 100644
index 0000000000..babc2acd4c
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py
@@ -0,0 +1,55 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_Keyboard(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34 self.keyboard = kwargs.get("keyboard", "")
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if self.keyboard != "":
40 retval += "# System keyboard\nkeyboard %s\n" % self.keyboard
41
42 return retval
43
44 def _getParser(self):
45 op = KSOptionParser()
46 return op
47
48 def parse(self, args):
49 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
50
51 if len(extra) != 1:
52 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "keyboard")
53
54 self.keyboard = extra[0]
55 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py b/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py
new file mode 100644
index 0000000000..cf5e46cda7
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py
@@ -0,0 +1,60 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_Lang(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34 self.lang = kwargs.get("lang", "")
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if self.lang != "":
40 retval += "# System language\nlang %s\n" % self.lang
41
42 return retval
43
44 def _getParser(self):
45 op = KSOptionParser()
46 return op
47
48 def parse(self, args):
49 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
50 if len(extra) != 1:
51 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "lang")
52
53 self.lang = extra[0]
54 return self
55
56 def apply(self, instroot="/"):
57 if self.lang == "": return
58 f = open(instroot + "/etc/sysconfig/i18n", "w+")
59 f.write("LANG=\"%s\"\n" %(self.lang,))
60 f.close()
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py b/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py
new file mode 100644
index 0000000000..73a9e537a9
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py
@@ -0,0 +1,58 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23class FC3_LangSupport(KickstartCommand):
24 removedKeywords = KickstartCommand.removedKeywords
25 removedAttrs = KickstartCommand.removedAttrs
26
27 def __init__(self, writePriority=0, *args, **kwargs):
28 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
29 self.op = self._getParser()
30
31 self.deflang = kwargs.get("deflang", "")
32 self.supported = kwargs.get("supported", [])
33
34 def __str__(self):
35 retval = KickstartCommand.__str__(self)
36
37 if self.deflang:
38 retval += "langsupport --default=%s" % self.deflang
39
40 if self.supported:
41 retval += " %s" % " ".join(self.supported)
42
43 return retval + "\n"
44
45 def _getParser(self):
46 op = KSOptionParser()
47 op.add_option("--default", dest="deflang", default="en_US.UTF-8")
48 return op
49
50 def parse(self, args):
51 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
52 self._setToSelf(self.op, opts)
53 self.supported = extra
54 return self
55
56class FC5_LangSupport(DeprecatedCommand):
57 def __init__(self):
58 DeprecatedCommand.__init__(self)
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py b/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py
new file mode 100644
index 0000000000..92b3f930b6
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py
@@ -0,0 +1,54 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_LiloCheck(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34 self.check = kwargs.get("check", False)
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if self.check:
40 retval += "lilocheck\n"
41
42 return retval
43
44 def _getParser(self):
45 op = KSOptionParser()
46 return op
47
48 def parse(self, args):
49 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
50 if len(extra) > 0:
51 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "lilocheck")
52
53 self.check = True
54 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py b/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py
new file mode 100644
index 0000000000..698561994d
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py
@@ -0,0 +1,66 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007, 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC6_Logging(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.host = kwargs.get("host", "")
36 self.level = kwargs.get("level", "info")
37 self.port = kwargs.get("port", "")
38
39 def __str__(self):
40 retval = KickstartCommand.__str__(self)
41 retval += "# Installation logging level\nlogging --level=%s" % self.level
42
43 if self.host != "":
44 retval += " --host=%s" % self.host
45
46 if self.port != "":
47 retval += " --port=%s" % self.port
48
49 return retval + "\n"
50
51 def _getParser(self):
52 op = KSOptionParser()
53 op.add_option("--host")
54 op.add_option("--level", type="choice", default="info",
55 choices=["debug", "info", "warning", "error", "critical"])
56 op.add_option("--port")
57 return op
58
59 def parse(self, args):
60 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
61
62 if opts.port and not opts.host:
63 raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Can't specify --port without --host."))
64
65 self._setToSelf(self.op, opts)
66 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py b/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py
new file mode 100644
index 0000000000..c1b9cc3a61
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py
@@ -0,0 +1,304 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25import warnings
26_ = lambda x: gettext.ldgettext("pykickstart", x)
27
28class FC3_LogVolData(BaseData):
29 removedKeywords = BaseData.removedKeywords
30 removedAttrs = BaseData.removedAttrs
31
32 def __init__(self, *args, **kwargs):
33 BaseData.__init__(self, *args, **kwargs)
34 self.fstype = kwargs.get("fstype", "")
35 self.grow = kwargs.get("grow", False)
36 self.maxSizeMB = kwargs.get("maxSizeMB", 0)
37 self.name = kwargs.get("name", "")
38 self.format = kwargs.get("format", True)
39 self.percent = kwargs.get("percent", 0)
40 self.recommended = kwargs.get("recommended", False)
41 self.size = kwargs.get("size", None)
42 self.preexist = kwargs.get("preexist", False)
43 self.vgname = kwargs.get("vgname", "")
44 self.mountpoint = kwargs.get("mountpoint", "")
45
46 def __eq__(self, y):
47 return self.vgname == y.vgname and self.name == y.name
48
49 def _getArgsAsStr(self):
50 retval = ""
51
52 if self.fstype != "":
53 retval += " --fstype=\"%s\"" % self.fstype
54 if self.grow:
55 retval += " --grow"
56 if self.maxSizeMB > 0:
57 retval += " --maxsize=%d" % self.maxSizeMB
58 if not self.format:
59 retval += " --noformat"
60 if self.percent > 0:
61 retval += " --percent=%d" % self.percent
62 if self.recommended:
63 retval += " --recommended"
64 if self.size > 0:
65 retval += " --size=%d" % self.size
66 if self.preexist:
67 retval += " --useexisting"
68
69 return retval
70
71 def __str__(self):
72 retval = BaseData.__str__(self)
73 retval += "logvol %s %s --name=%s --vgname=%s\n" % (self.mountpoint, self._getArgsAsStr(), self.name, self.vgname)
74 return retval
75
76class FC4_LogVolData(FC3_LogVolData):
77 removedKeywords = FC3_LogVolData.removedKeywords
78 removedAttrs = FC3_LogVolData.removedAttrs
79
80 def __init__(self, *args, **kwargs):
81 FC3_LogVolData.__init__(self, *args, **kwargs)
82 self.bytesPerInode = kwargs.get("bytesPerInode", 4096)
83 self.fsopts = kwargs.get("fsopts", "")
84
85 def _getArgsAsStr(self):
86 retval = FC3_LogVolData._getArgsAsStr(self)
87
88 if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0:
89 retval += " --bytes-per-inode=%d" % self.bytesPerInode
90 if self.fsopts != "":
91 retval += " --fsoptions=\"%s\"" % self.fsopts
92
93 return retval
94
95class RHEL5_LogVolData(FC4_LogVolData):
96 removedKeywords = FC4_LogVolData.removedKeywords
97 removedAttrs = FC4_LogVolData.removedAttrs
98
99 def __init__(self, *args, **kwargs):
100 FC4_LogVolData.__init__(self, *args, **kwargs)
101 self.encrypted = kwargs.get("encrypted", False)
102 self.passphrase = kwargs.get("passphrase", "")
103
104 def _getArgsAsStr(self):
105 retval = FC4_LogVolData._getArgsAsStr(self)
106
107 if self.encrypted:
108 retval += " --encrypted"
109
110 if self.passphrase != "":
111 retval += " --passphrase=\"%s\"" % self.passphrase
112
113 return retval
114
115class F9_LogVolData(FC4_LogVolData):
116 removedKeywords = FC4_LogVolData.removedKeywords + ["bytesPerInode"]
117 removedAttrs = FC4_LogVolData.removedAttrs + ["bytesPerInode"]
118
119 def __init__(self, *args, **kwargs):
120 FC4_LogVolData.__init__(self, *args, **kwargs)
121 self.deleteRemovedAttrs()
122
123 self.fsopts = kwargs.get("fsopts", "")
124 self.fsprofile = kwargs.get("fsprofile", "")
125 self.encrypted = kwargs.get("encrypted", False)
126 self.passphrase = kwargs.get("passphrase", "")
127
128 def _getArgsAsStr(self):
129 retval = FC4_LogVolData._getArgsAsStr(self)
130
131 if self.fsprofile != "":
132 retval += " --fsprofile=\"%s\"" % self.fsprofile
133 if self.encrypted:
134 retval += " --encrypted"
135
136 if self.passphrase != "":
137 retval += " --passphrase=\"%s\"" % self.passphrase
138
139 return retval
140
141class F12_LogVolData(F9_LogVolData):
142 removedKeywords = F9_LogVolData.removedKeywords
143 removedAttrs = F9_LogVolData.removedAttrs
144
145 def __init__(self, *args, **kwargs):
146 F9_LogVolData.__init__(self, *args, **kwargs)
147 self.deleteRemovedAttrs()
148
149 self.escrowcert = kwargs.get("escrowcert", "")
150 self.backuppassphrase = kwargs.get("backuppassphrase", False)
151
152 def _getArgsAsStr(self):
153 retval = F9_LogVolData._getArgsAsStr(self)
154
155 if self.encrypted and self.escrowcert != "":
156 retval += " --escrowcert=\"%s\"" % self.escrowcert
157
158 if self.backuppassphrase:
159 retval += " --backuppassphrase"
160
161 return retval
162
163F14_LogVolData = F12_LogVolData
164
165class F15_LogVolData(F14_LogVolData):
166 removedKeywords = F14_LogVolData.removedKeywords
167 removedAttrs = F14_LogVolData.removedAttrs
168
169 def __init__(self, *args, **kwargs):
170 F14_LogVolData.__init__(self, *args, **kwargs)
171 self.label = kwargs.get("label", "")
172
173 def _getArgsAsStr(self):
174 retval = F14_LogVolData._getArgsAsStr(self)
175
176 if self.label != "":
177 retval += " --label=\"%s\"" % self.label
178
179 return retval
180
181class FC3_LogVol(KickstartCommand):
182 removedKeywords = KickstartCommand.removedKeywords
183 removedAttrs = KickstartCommand.removedAttrs
184
185 def __init__(self, writePriority=133, *args, **kwargs):
186 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
187 self.op = self._getParser()
188
189 self.lvList = kwargs.get("lvList", [])
190
191 def __str__(self):
192 retval = ""
193
194 for part in self.lvList:
195 retval += part.__str__()
196
197 return retval
198
199 def _getParser(self):
200 def lv_cb (option, opt_str, value, parser):
201 parser.values.format = False
202 parser.values.preexist = True
203
204 op = KSOptionParser()
205 op.add_option("--fstype", dest="fstype")
206 op.add_option("--grow", dest="grow", action="store_true",
207 default=False)
208 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
209 nargs=1)
210 op.add_option("--name", dest="name", required=1)
211 op.add_option("--noformat", action="callback", callback=lv_cb,
212 dest="format", default=True, nargs=0)
213 op.add_option("--percent", dest="percent", action="store", type="int",
214 nargs=1)
215 op.add_option("--recommended", dest="recommended", action="store_true",
216 default=False)
217 op.add_option("--size", dest="size", action="store", type="int",
218 nargs=1)
219 op.add_option("--useexisting", dest="preexist", action="store_true",
220 default=False)
221 op.add_option("--vgname", dest="vgname", required=1)
222 return op
223
224 def parse(self, args):
225 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
226
227 if len(extra) == 0:
228 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "logvol")
229
230 lvd = self.handler.LogVolData()
231 self._setToObj(self.op, opts, lvd)
232 lvd.lineno = self.lineno
233 lvd.mountpoint=extra[0]
234
235 # Check for duplicates in the data list.
236 if lvd in self.dataList():
237 warnings.warn(_("A logical volume with the name %s has already been defined in volume group %s.") % (lvd.device, lvd.vgname))
238
239 return lvd
240
241 def dataList(self):
242 return self.lvList
243
244class FC4_LogVol(FC3_LogVol):
245 removedKeywords = FC3_LogVol.removedKeywords
246 removedAttrs = FC3_LogVol.removedAttrs
247
248 def _getParser(self):
249 op = FC3_LogVol._getParser(self)
250 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
251 type="int", nargs=1)
252 op.add_option("--fsoptions", dest="fsopts")
253 return op
254
255class RHEL5_LogVol(FC4_LogVol):
256 removedKeywords = FC4_LogVol.removedKeywords
257 removedAttrs = FC4_LogVol.removedAttrs
258
259 def _getParser(self):
260 op = FC4_LogVol._getParser(self)
261 op.add_option("--encrypted", action="store_true", default=False)
262 op.add_option("--passphrase")
263 return op
264
265class F9_LogVol(FC4_LogVol):
266 removedKeywords = FC4_LogVol.removedKeywords
267 removedAttrs = FC4_LogVol.removedAttrs
268
269 def _getParser(self):
270 op = FC4_LogVol._getParser(self)
271 op.add_option("--bytes-per-inode", deprecated=1)
272 op.add_option("--fsprofile", dest="fsprofile", action="store",
273 type="string", nargs=1)
274 op.add_option("--encrypted", action="store_true", default=False)
275 op.add_option("--passphrase")
276 return op
277
278class F12_LogVol(F9_LogVol):
279 removedKeywords = F9_LogVol.removedKeywords
280 removedAttrs = F9_LogVol.removedAttrs
281
282 def _getParser(self):
283 op = F9_LogVol._getParser(self)
284 op.add_option("--escrowcert")
285 op.add_option("--backuppassphrase", action="store_true", default=False)
286 return op
287
288class F14_LogVol(F12_LogVol):
289 removedKeywords = F12_LogVol.removedKeywords
290 removedAttrs = F12_LogVol.removedAttrs
291
292 def _getParser(self):
293 op = F12_LogVol._getParser(self)
294 op.remove_option("--bytes-per-inode")
295 return op
296
297class F15_LogVol(F14_LogVol):
298 removedKeywords = F14_LogVol.removedKeywords
299 removedAttrs = F14_LogVol.removedAttrs
300
301 def _getParser(self):
302 op = F14_LogVol._getParser(self)
303 op.add_option("--label")
304 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py b/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py
new file mode 100644
index 0000000000..388823a839
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py
@@ -0,0 +1,53 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC4_MediaCheck(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34 self.mediacheck = kwargs.get("mediacheck", False)
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38 if self.mediacheck:
39 retval += "mediacheck\n"
40
41 return retval
42
43 def _getParser(self):
44 op = KSOptionParser()
45 return op
46
47 def parse(self, args):
48 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
49 if len(extra) > 0:
50 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "mediacheck")
51
52 self.mediacheck = True
53 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/method.py b/scripts/lib/mic/3rdparty/pykickstart/commands/method.py
new file mode 100644
index 0000000000..e21064acda
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/method.py
@@ -0,0 +1,186 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007, 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_Method(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.method = kwargs.get("method", "")
34
35 # Set all these attributes so calls to this command's __call__
36 # method can set them. However we don't want to provide them as
37 # arguments to __init__ because method is special.
38 self.biospart = None
39 self.partition = None
40 self.server = None
41 self.dir = None
42 self.url = None
43
44 def __str__(self):
45 retval = KickstartCommand.__str__(self)
46
47 if self.method == "cdrom":
48 retval += "# Use CDROM installation media\ncdrom\n"
49 elif self.method == "harddrive":
50 msg = "# Use hard drive installation media\nharddrive --dir=%s" % self.dir
51
52 if self.biospart is not None:
53 retval += msg + " --biospart=%s\n" % self.biospart
54 else:
55 retval += msg + " --partition=%s\n" % self.partition
56 elif self.method == "nfs":
57 retval += "# Use NFS installation media\nnfs --server=%s --dir=%s\n" % (self.server, self.dir)
58 elif self.method == "url":
59 retval += "# Use network installation\nurl --url=\"%s\"\n" % self.url
60
61 return retval
62
63 def _getParser(self):
64 op = KSOptionParser()
65
66 # method = "cdrom" falls through to the return
67 if self.currentCmd == "harddrive":
68 op.add_option("--biospart", dest="biospart")
69 op.add_option("--partition", dest="partition")
70 op.add_option("--dir", dest="dir", required=1)
71 elif self.currentCmd == "nfs":
72 op.add_option("--server", dest="server", required=1)
73 op.add_option("--dir", dest="dir", required=1)
74 elif self.currentCmd == "url":
75 op.add_option("--url", dest="url", required=1)
76
77 return op
78
79 def parse(self, args):
80 self.method = self.currentCmd
81
82 op = self._getParser()
83 (opts, extra) = op.parse_args(args=args, lineno=self.lineno)
84 self._setToSelf(op, opts)
85
86 if self.currentCmd == "harddrive":
87 if self.biospart is None and self.partition is None or \
88 self.biospart is not None and self.partition is not None:
89 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of biospart or partition options must be specified."))
90
91 return self
92
93class FC6_Method(FC3_Method):
94 removedKeywords = FC3_Method.removedKeywords
95 removedAttrs = FC3_Method.removedAttrs
96
97 def __init__(self, writePriority=0, *args, **kwargs):
98 FC3_Method.__init__(self, writePriority, *args, **kwargs)
99
100 # Same reason for this attribute as the comment in FC3_Method.
101 self.opts = None
102
103 def __str__(self):
104 retval = KickstartCommand.__str__(self)
105
106 if self.method == "cdrom":
107 retval += "# Use CDROM installation media\ncdrom\n"
108 elif self.method == "harddrive":
109 msg = "# Use hard drive installation media\nharddrive --dir=%s" % self.dir
110
111 if self.biospart is not None:
112 retval += msg + " --biospart=%s\n" % self.biospart
113 else:
114 retval += msg + " --partition=%s\n" % self.partition
115 elif self.method == "nfs":
116 retval += "# Use NFS installation media\nnfs --server=%s --dir=%s" % (self.server, self.dir)
117 if self.opts is not None:
118 retval += " --opts=\"%s\"" % self.opts
119 retval += "\n"
120 elif self.method == "url":
121 retval += "# Use network installation\nurl --url=\"%s\"\n" % self.url
122
123 return retval
124
125 def _getParser(self):
126 op = FC3_Method._getParser(self)
127
128 if self.currentCmd == "nfs":
129 op.add_option("--opts", dest="opts")
130
131 return op
132
133class F13_Method(FC6_Method):
134 removedKeywords = FC6_Method.removedKeywords
135 removedAttrs = FC6_Method.removedAttrs
136
137 def __init__(self, *args, **kwargs):
138 FC6_Method.__init__(self, *args, **kwargs)
139
140 # And same as all the other __init__ methods.
141 self.proxy = ""
142
143 def __str__(self):
144 retval = FC6_Method.__str__(self)
145
146 if self.method == "url" and self.proxy:
147 retval = retval.strip()
148 retval += " --proxy=\"%s\"\n" % self.proxy
149
150 return retval
151
152 def _getParser(self):
153 op = FC6_Method._getParser(self)
154
155 if self.currentCmd == "url":
156 op.add_option("--proxy")
157
158 return op
159
160class F14_Method(F13_Method):
161 removedKeywords = F13_Method.removedKeywords
162 removedAttrs = F13_Method.removedAttrs
163
164 def __init__(self, *args, **kwargs):
165 F13_Method.__init__(self, *args, **kwargs)
166
167 self.noverifyssl = False
168
169 def __str__(self):
170 retval = F13_Method.__str__(self)
171
172 if self.method == "url" and self.noverifyssl:
173 retval = retval.strip()
174 retval += " --noverifyssl\n"
175
176 return retval
177
178 def _getParser(self):
179 op = F13_Method._getParser(self)
180
181 if self.currentCmd == "url":
182 op.add_option("--noverifyssl", action="store_true", default=False)
183
184 return op
185
186RHEL6_Method = F14_Method
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py b/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py
new file mode 100644
index 0000000000..8c8c2c4fc9
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py
@@ -0,0 +1,106 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_Monitor(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.hsync = kwargs.get("hsync", "")
36 self.monitor = kwargs.get("monitor", "")
37 self.vsync = kwargs.get("vsync", "")
38
39 def __str__(self):
40 retval = KickstartCommand.__str__(self)
41 retval += "monitor"
42
43 if self.hsync != "":
44 retval += " --hsync=%s" % self.hsync
45 if self.monitor != "":
46 retval += " --monitor=\"%s\"" % self.monitor
47 if self.vsync != "":
48 retval += " --vsync=%s" % self.vsync
49
50 if retval != "monitor":
51 return retval + "\n"
52 else:
53 return ""
54
55 def _getParser(self):
56 op = KSOptionParser()
57 op.add_option("--hsync")
58 op.add_option("--monitor")
59 op.add_option("--vsync")
60 return op
61
62 def parse(self, args):
63 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
64
65 if extra:
66 mapping = {"cmd": "monitor", "options": extra}
67 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(cmd)s command: %(options)s") % mapping)
68
69 self._setToSelf(self.op, opts)
70 return self
71
72class FC6_Monitor(FC3_Monitor):
73 removedKeywords = FC3_Monitor.removedKeywords
74 removedAttrs = FC3_Monitor.removedAttrs
75
76 def __init__(self, writePriority=0, *args, **kwargs):
77 FC3_Monitor.__init__(self, writePriority, *args, **kwargs)
78 self.probe = kwargs.get("probe", True)
79
80 def __str__(self):
81 retval = KickstartCommand.__str__(self)
82 retval += "monitor"
83
84 if self.hsync != "":
85 retval += " --hsync=%s" % self.hsync
86 if self.monitor != "":
87 retval += " --monitor=\"%s\"" % self.monitor
88 if not self.probe:
89 retval += " --noprobe"
90 if self.vsync != "":
91 retval += " --vsync=%s" % self.vsync
92
93 if retval != "monitor":
94 return retval + "\n"
95 else:
96 return ""
97
98 def _getParser(self):
99 op = FC3_Monitor._getParser(self)
100 op.add_option("--noprobe", dest="probe", action="store_false",
101 default=True)
102 return op
103
104class F10_Monitor(DeprecatedCommand):
105 def __init__(self):
106 DeprecatedCommand.__init__(self)
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py b/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py
new file mode 100644
index 0000000000..c643bcedc3
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py
@@ -0,0 +1,70 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class RHEL3_Mouse(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.device = kwargs.get("device", "")
36 self.emulthree = kwargs.get("emulthree", False)
37 self.mouse = kwargs.get("mouse", "")
38
39 def __str__(self):
40 retval = KickstartCommand.__str__(self)
41
42 opts = ""
43 if self.device:
44 opts += "--device=%s " % self.device
45 if self.emulthree:
46 opts += "--emulthree "
47
48 if self.mouse:
49 retval += "# System mouse\nmouse %s%s\n" % (opts, self.mouse)
50 return retval
51
52 def _getParser(self):
53 op = KSOptionParser()
54 op.add_option("--device", dest="device", default="")
55 op.add_option("--emulthree", dest="emulthree", default=False, action="store_true")
56 return op
57
58 def parse(self, args):
59 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
60 self._setToSelf(self.op, opts)
61
62 if len(extra) != 1:
63 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "mouse")
64
65 self.mouse = extra[0]
66 return self
67
68class FC3_Mouse(DeprecatedCommand):
69 def __init__(self):
70 DeprecatedCommand.__init__(self)
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py b/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py
new file mode 100644
index 0000000000..84ba755e68
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py
@@ -0,0 +1,111 @@
1#
2# Chris Lumens <clumens@redhat.com>
3# Peter Jones <pjones@redhat.com>
4#
5# Copyright 2006, 2007 Red Hat, Inc.
6#
7# This copyrighted material is made available to anyone wishing to use, modify,
8# copy, or redistribute it subject to the terms and conditions of the GNU
9# General Public License v.2. This program is distributed in the hope that it
10# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
11# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12# See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 51
16# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
17# trademarks that are incorporated in the source code or documentation are not
18# subject to the GNU General Public License and may only be used or replicated
19# with the express permission of Red Hat, Inc.
20#
21from pykickstart.base import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26_ = lambda x: gettext.ldgettext("pykickstart", x)
27
28class FC6_MpPathData(BaseData):
29 removedKeywords = BaseData.removedKeywords
30 removedAttrs = BaseData.removedAttrs
31
32 def __init__(self, *args, **kwargs):
33 BaseData.__init__(self, *args, **kwargs)
34 self.mpdev = kwargs.get("mpdev", "")
35 self.device = kwargs.get("device", "")
36 self.rule = kwargs.get("rule", "")
37
38 def __str__(self):
39 return " --device=%s --rule=\"%s\"" % (self.device, self.rule)
40
41class FC6_MultiPathData(BaseData):
42 removedKeywords = BaseData.removedKeywords
43 removedAttrs = BaseData.removedAttrs
44
45 def __init__(self, *args, **kwargs):
46 BaseData.__init__(self, *args, **kwargs)
47 self.name = kwargs.get("name", "")
48 self.paths = kwargs.get("paths", [])
49
50 def __str__(self):
51 retval = BaseData.__str__(self)
52
53 for path in self.paths:
54 retval += "multipath --mpdev=%s %s\n" % (self.name, path.__str__())
55
56 return retval
57
58class FC6_MultiPath(KickstartCommand):
59 removedKeywords = KickstartCommand.removedKeywords
60 removedAttrs = KickstartCommand.removedAttrs
61
62 def __init__(self, writePriority=50, *args, **kwargs):
63 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
64 self.op = self._getParser()
65
66 self.mpaths = kwargs.get("mpaths", [])
67
68 def __str__(self):
69 retval = ""
70 for mpath in self.mpaths:
71 retval += mpath.__str__()
72
73 return retval
74
75 def _getParser(self):
76 op = KSOptionParser()
77 op.add_option("--name", dest="name", action="store", type="string",
78 required=1)
79 op.add_option("--device", dest="device", action="store", type="string",
80 required=1)
81 op.add_option("--rule", dest="rule", action="store", type="string",
82 required=1)
83 return op
84
85 def parse(self, args):
86 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
87 dd = FC6_MpPathData()
88 self._setToObj(self.op, opts, dd)
89 dd.lineno = self.lineno
90 dd.mpdev = dd.mpdev.split('/')[-1]
91
92 parent = None
93 for x in range(0, len(self.mpaths)):
94 mpath = self.mpaths[x]
95 for path in mpath.paths:
96 if path.device == dd.device:
97 mapping = {"device": path.device, "multipathdev": path.mpdev}
98 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Device '%(device)s' is already used in multipath '%(multipathdev)s'") % mapping)
99 if mpath.name == dd.mpdev:
100 parent = x
101
102 if parent is None:
103 mpath = FC6_MultiPathData()
104 return mpath
105 else:
106 mpath = self.mpaths[parent]
107
108 return dd
109
110 def dataList(self):
111 return self.mpaths
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/network.py b/scripts/lib/mic/3rdparty/pykickstart/commands/network.py
new file mode 100644
index 0000000000..9b67f92831
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/network.py
@@ -0,0 +1,363 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26import warnings
27_ = lambda x: gettext.ldgettext("pykickstart", x)
28
29class FC3_NetworkData(BaseData):
30 removedKeywords = BaseData.removedKeywords
31 removedAttrs = BaseData.removedAttrs
32
33 def __init__(self, *args, **kwargs):
34 BaseData.__init__(self, *args, **kwargs)
35 self.bootProto = kwargs.get("bootProto", BOOTPROTO_DHCP)
36 self.dhcpclass = kwargs.get("dhcpclass", "")
37 self.device = kwargs.get("device", "")
38 self.essid = kwargs.get("essid", "")
39 self.ethtool = kwargs.get("ethtool", "")
40 self.gateway = kwargs.get("gateway", "")
41 self.hostname = kwargs.get("hostname", "")
42 self.ip = kwargs.get("ip", "")
43 self.mtu = kwargs.get("mtu", "")
44 self.nameserver = kwargs.get("nameserver", "")
45 self.netmask = kwargs.get("netmask", "")
46 self.nodns = kwargs.get("nodns", False)
47 self.onboot = kwargs.get("onboot", True)
48 self.wepkey = kwargs.get("wepkey", "")
49
50 def __eq__(self, y):
51 return self.device and self.device == y.device
52
53 def _getArgsAsStr(self):
54 retval = ""
55
56 if self.bootProto != "":
57 retval += " --bootproto=%s" % self.bootProto
58 if self.dhcpclass != "":
59 retval += " --dhcpclass=%s" % self.dhcpclass
60 if self.device != "":
61 retval += " --device=%s" % self.device
62 if self.essid != "":
63 retval += " --essid=\"%s\"" % self.essid
64 if self.ethtool != "":
65 retval += " --ethtool=\"%s\"" % self.ethtool
66 if self.gateway != "":
67 retval += " --gateway=%s" % self.gateway
68 if self.hostname != "":
69 retval += " --hostname=%s" % self.hostname
70 if self.ip != "":
71 retval += " --ip=%s" % self.ip
72 if self.mtu != "":
73 retval += " --mtu=%s" % self.mtu
74 if self.nameserver != "":
75 retval += " --nameserver=%s" % self.nameserver
76 if self.netmask != "":
77 retval += " --netmask=%s" % self.netmask
78 if self.nodns:
79 retval += " --nodns"
80 if not self.onboot:
81 retval += " --onboot=off"
82 if self.wepkey != "":
83 retval += " --wepkey=%s" % self.wepkey
84
85 return retval
86
87 def __str__(self):
88 retval = BaseData.__str__(self)
89 retval += "network %s\n" % self._getArgsAsStr()
90 return retval
91
92class FC4_NetworkData(FC3_NetworkData):
93 removedKeywords = FC3_NetworkData.removedKeywords
94 removedAttrs = FC3_NetworkData.removedAttrs
95
96 def __init__(self, *args, **kwargs):
97 FC3_NetworkData.__init__(self, *args, **kwargs)
98 self.notksdevice = kwargs.get("notksdevice", False)
99
100 def _getArgsAsStr(self):
101 retval = FC3_NetworkData._getArgsAsStr(self)
102
103 if self.notksdevice:
104 retval += " --notksdevice"
105
106 return retval
107
108class FC6_NetworkData(FC4_NetworkData):
109 removedKeywords = FC4_NetworkData.removedKeywords
110 removedAttrs = FC4_NetworkData.removedAttrs
111
112 def __init__(self, *args, **kwargs):
113 FC4_NetworkData.__init__(self, *args, **kwargs)
114 self.noipv4 = kwargs.get("noipv4", False)
115 self.noipv6 = kwargs.get("noipv6", False)
116
117 def _getArgsAsStr(self):
118 retval = FC4_NetworkData._getArgsAsStr(self)
119
120 if self.noipv4:
121 retval += " --noipv4"
122 if self.noipv6:
123 retval += " --noipv6"
124
125 return retval
126
127class F8_NetworkData(FC6_NetworkData):
128 removedKeywords = FC6_NetworkData.removedKeywords
129 removedAttrs = FC6_NetworkData.removedAttrs
130
131 def __init__(self, *args, **kwargs):
132 FC6_NetworkData.__init__(self, *args, **kwargs)
133 self.ipv6 = kwargs.get("ipv6", "")
134
135 def _getArgsAsStr(self):
136 retval = FC6_NetworkData._getArgsAsStr(self)
137
138 if self.ipv6 != "":
139 retval += " --ipv6" % self.ipv6
140
141 return retval
142
143class F16_NetworkData(F8_NetworkData):
144 removedKeywords = F8_NetworkData.removedKeywords
145 removedAttrs = F8_NetworkData.removedAttrs
146
147 def __init__(self, *args, **kwargs):
148 F8_NetworkData.__init__(self, *args, **kwargs)
149 self.activate = kwargs.get("activate", False)
150 self.nodefroute = kwargs.get("nodefroute", False)
151 self.wpakey = kwargs.get("wpakey", "")
152
153 def _getArgsAsStr(self):
154 retval = F8_NetworkData._getArgsAsStr(self)
155
156 if self.activate:
157 retval += " --activate"
158 if self.nodefroute:
159 retval += " --nodefroute"
160 if self.wpakey != "":
161 retval += "--wpakey=%s" % self.wpakey
162
163 return retval
164
165class RHEL4_NetworkData(FC3_NetworkData):
166 removedKeywords = FC3_NetworkData.removedKeywords
167 removedAttrs = FC3_NetworkData.removedAttrs
168
169 def __init__(self, *args, **kwargs):
170 FC3_NetworkData.__init__(self, *args, **kwargs)
171 self.notksdevice = kwargs.get("notksdevice", False)
172
173 def _getArgsAsStr(self):
174 retval = FC3_NetworkData._getArgsAsStr(self)
175
176 if self.notksdevice:
177 retval += " --notksdevice"
178
179 return retval
180
181class RHEL6_NetworkData(F8_NetworkData):
182 removedKeywords = F8_NetworkData.removedKeywords
183 removedAttrs = F8_NetworkData.removedAttrs
184
185 def __init__(self, *args, **kwargs):
186 F8_NetworkData.__init__(self, *args, **kwargs)
187 self.activate = kwargs.get("activate", False)
188 self.nodefroute = kwargs.get("nodefroute", False)
189
190 def _getArgsAsStr(self):
191 retval = F8_NetworkData._getArgsAsStr(self)
192
193 if self.activate:
194 retval += " --activate"
195 if self.nodefroute:
196 retval += " --nodefroute"
197
198 return retval
199
200class FC3_Network(KickstartCommand):
201 removedKeywords = KickstartCommand.removedKeywords
202 removedAttrs = KickstartCommand.removedAttrs
203
204 def __init__(self, writePriority=0, *args, **kwargs):
205 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
206 self.bootprotoList = [BOOTPROTO_DHCP, BOOTPROTO_BOOTP,
207 BOOTPROTO_STATIC]
208
209 self.op = self._getParser()
210
211 self.network = kwargs.get("network", [])
212
213 def __str__(self):
214 retval = ""
215
216 for nic in self.network:
217 retval += nic.__str__()
218
219 if retval != "":
220 return "# Network information\n" + retval
221 else:
222 return ""
223
224 def _getParser(self):
225 op = KSOptionParser()
226 op.add_option("--bootproto", dest="bootProto",
227 default=BOOTPROTO_DHCP,
228 choices=self.bootprotoList)
229 op.add_option("--dhcpclass", dest="dhcpclass")
230 op.add_option("--device", dest="device")
231 op.add_option("--essid", dest="essid")
232 op.add_option("--ethtool", dest="ethtool")
233 op.add_option("--gateway", dest="gateway")
234 op.add_option("--hostname", dest="hostname")
235 op.add_option("--ip", dest="ip")
236 op.add_option("--mtu", dest="mtu")
237 op.add_option("--nameserver", dest="nameserver")
238 op.add_option("--netmask", dest="netmask")
239 op.add_option("--nodns", dest="nodns", action="store_true",
240 default=False)
241 op.add_option("--onboot", dest="onboot", action="store",
242 type="ksboolean")
243 op.add_option("--wepkey", dest="wepkey")
244 return op
245
246 def parse(self, args):
247 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
248 nd = self.handler.NetworkData()
249 self._setToObj(self.op, opts, nd)
250 nd.lineno = self.lineno
251
252 # Check for duplicates in the data list.
253 if nd in self.dataList():
254 warnings.warn(_("A network device with the name %s has already been defined.") % nd.device)
255
256 return nd
257
258 def dataList(self):
259 return self.network
260
261class FC4_Network(FC3_Network):
262 removedKeywords = FC3_Network.removedKeywords
263 removedAttrs = FC3_Network.removedAttrs
264
265 def _getParser(self):
266 op = FC3_Network._getParser(self)
267 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
268 default=False)
269 return op
270
271class FC6_Network(FC4_Network):
272 removedKeywords = FC4_Network.removedKeywords
273 removedAttrs = FC4_Network.removedAttrs
274
275 def _getParser(self):
276 op = FC4_Network._getParser(self)
277 op.add_option("--noipv4", dest="noipv4", action="store_true",
278 default=False)
279 op.add_option("--noipv6", dest="noipv6", action="store_true",
280 default=False)
281 return op
282
283class F8_Network(FC6_Network):
284 removedKeywords = FC6_Network.removedKeywords
285 removedAttrs = FC6_Network.removedAttrs
286
287 def _getParser(self):
288 op = FC6_Network._getParser(self)
289 op.add_option("--ipv6", dest="ipv6")
290 return op
291
292class F9_Network(F8_Network):
293 removedKeywords = F8_Network.removedKeywords
294 removedAttrs = F8_Network.removedAttrs
295
296 def __init__(self, writePriority=0, *args, **kwargs):
297 F8_Network.__init__(self, writePriority, *args, **kwargs)
298 self.bootprotoList.append(BOOTPROTO_QUERY)
299
300 def _getParser(self):
301 op = F8_Network._getParser(self)
302 op.add_option("--bootproto", dest="bootProto",
303 default=BOOTPROTO_DHCP,
304 choices=self.bootprotoList)
305 return op
306
307class F16_Network(F9_Network):
308 removedKeywords = F9_Network.removedKeywords
309 removedAttrs = F9_Network.removedAttrs
310
311 def __init__(self, writePriority=0, *args, **kwargs):
312 F9_Network.__init__(self, writePriority, *args, **kwargs)
313 self.bootprotoList.append(BOOTPROTO_IBFT)
314
315 def _getParser(self):
316 op = F9_Network._getParser(self)
317 op.add_option("--activate", dest="activate", action="store_true",
318 default=False)
319 op.add_option("--nodefroute", dest="nodefroute", action="store_true",
320 default=False)
321 op.add_option("--wpakey", dest="wpakey", action="store", default="")
322 return op
323
324class RHEL4_Network(FC3_Network):
325 removedKeywords = FC3_Network.removedKeywords
326 removedAttrs = FC3_Network.removedAttrs
327
328 def _getParser(self):
329 op = FC3_Network._getParser(self)
330 op.add_option("--notksdevice", dest="notksdevice", action="store_true",
331 default=False)
332 return op
333
334class RHEL5_Network(FC6_Network):
335 removedKeywords = FC6_Network.removedKeywords
336 removedAttrs = FC6_Network.removedAttrs
337
338 def __init__(self, writePriority=0, *args, **kwargs):
339 FC6_Network.__init__(self, writePriority, *args, **kwargs)
340 self.bootprotoList.append(BOOTPROTO_QUERY)
341
342 def _getParser(self):
343 op = FC6_Network._getParser(self)
344 op.add_option("--bootproto", dest="bootProto",
345 default=BOOTPROTO_DHCP,
346 choices=self.bootprotoList)
347 return op
348
349class RHEL6_Network(F9_Network):
350 removedKeywords = F9_Network.removedKeywords
351 removedAttrs = F9_Network.removedAttrs
352
353 def __init__(self, writePriority=0, *args, **kwargs):
354 F9_Network.__init__(self, writePriority, *args, **kwargs)
355 self.bootprotoList.append(BOOTPROTO_IBFT)
356
357 def _getParser(self):
358 op = F9_Network._getParser(self)
359 op.add_option("--activate", dest="activate", action="store_true",
360 default=False)
361 op.add_option("--nodefroute", dest="nodefroute", action="store_true",
362 default=False)
363 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py b/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py
new file mode 100644
index 0000000000..e65e012d02
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py
@@ -0,0 +1,353 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25import warnings
26_ = lambda x: gettext.ldgettext("pykickstart", x)
27
28class FC3_PartData(BaseData):
29 removedKeywords = BaseData.removedKeywords
30 removedAttrs = BaseData.removedAttrs
31
32 def __init__(self, *args, **kwargs):
33 BaseData.__init__(self, *args, **kwargs)
34 self.active = kwargs.get("active", False)
35 self.primOnly = kwargs.get("primOnly", False)
36 self.end = kwargs.get("end", 0)
37 self.fstype = kwargs.get("fstype", "")
38 self.grow = kwargs.get("grow", False)
39 self.maxSizeMB = kwargs.get("maxSizeMB", 0)
40 self.format = kwargs.get("format", True)
41 self.onbiosdisk = kwargs.get("onbiosdisk", "")
42 self.disk = kwargs.get("disk", "")
43 self.onPart = kwargs.get("onPart", "")
44 self.recommended = kwargs.get("recommended", False)
45 self.size = kwargs.get("size", None)
46 self.start = kwargs.get("start", 0)
47 self.mountpoint = kwargs.get("mountpoint", "")
48
49 def __eq__(self, y):
50 if self.mountpoint:
51 return self.mountpoint == y.mountpoint
52 else:
53 return False
54
55 def _getArgsAsStr(self):
56 retval = ""
57
58 if self.active:
59 retval += " --active"
60 if self.primOnly:
61 retval += " --asprimary"
62 if hasattr(self, "end") and self.end != 0:
63 retval += " --end=%s" % self.end
64 if self.fstype != "":
65 retval += " --fstype=\"%s\"" % self.fstype
66 if self.grow:
67 retval += " --grow"
68 if self.maxSizeMB > 0:
69 retval += " --maxsize=%d" % self.maxSizeMB
70 if not self.format:
71 retval += " --noformat"
72 if self.onbiosdisk != "":
73 retval += " --onbiosdisk=%s" % self.onbiosdisk
74 if self.disk != "":
75 retval += " --ondisk=%s" % self.disk
76 if self.onPart != "":
77 retval += " --onpart=%s" % self.onPart
78 if self.recommended:
79 retval += " --recommended"
80 if self.size and self.size != 0:
81 retval += " --size=%s" % self.size
82 if hasattr(self, "start") and self.start != 0:
83 retval += " --start=%s" % self.start
84
85 return retval
86
87 def __str__(self):
88 retval = BaseData.__str__(self)
89 if self.mountpoint:
90 mountpoint_str = "%s" % self.mountpoint
91 else:
92 mountpoint_str = "(No mount point)"
93 retval += "part %s%s\n" % (mountpoint_str, self._getArgsAsStr())
94 return retval
95
96class FC4_PartData(FC3_PartData):
97 removedKeywords = FC3_PartData.removedKeywords
98 removedAttrs = FC3_PartData.removedAttrs
99
100 def __init__(self, *args, **kwargs):
101 FC3_PartData.__init__(self, *args, **kwargs)
102 self.bytesPerInode = kwargs.get("bytesPerInode", 4096)
103 self.fsopts = kwargs.get("fsopts", "")
104 self.label = kwargs.get("label", "")
105
106 def _getArgsAsStr(self):
107 retval = FC3_PartData._getArgsAsStr(self)
108
109 if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0:
110 retval += " --bytes-per-inode=%d" % self.bytesPerInode
111 if self.fsopts != "":
112 retval += " --fsoptions=\"%s\"" % self.fsopts
113 if self.label != "":
114 retval += " --label=%s" % self.label
115
116 return retval
117
118class RHEL5_PartData(FC4_PartData):
119 removedKeywords = FC4_PartData.removedKeywords
120 removedAttrs = FC4_PartData.removedAttrs
121
122 def __init__(self, *args, **kwargs):
123 FC4_PartData.__init__(self, *args, **kwargs)
124 self.encrypted = kwargs.get("encrypted", False)
125 self.passphrase = kwargs.get("passphrase", "")
126
127 def _getArgsAsStr(self):
128 retval = FC4_PartData._getArgsAsStr(self)
129
130 if self.encrypted:
131 retval += " --encrypted"
132
133 if self.passphrase != "":
134 retval += " --passphrase=\"%s\"" % self.passphrase
135
136 return retval
137
138class F9_PartData(FC4_PartData):
139 removedKeywords = FC4_PartData.removedKeywords + ["bytesPerInode"]
140 removedAttrs = FC4_PartData.removedAttrs + ["bytesPerInode"]
141
142 def __init__(self, *args, **kwargs):
143 FC4_PartData.__init__(self, *args, **kwargs)
144 self.deleteRemovedAttrs()
145
146 self.fsopts = kwargs.get("fsopts", "")
147 self.label = kwargs.get("label", "")
148 self.fsprofile = kwargs.get("fsprofile", "")
149 self.encrypted = kwargs.get("encrypted", False)
150 self.passphrase = kwargs.get("passphrase", "")
151
152 def _getArgsAsStr(self):
153 retval = FC4_PartData._getArgsAsStr(self)
154
155 if self.fsprofile != "":
156 retval += " --fsprofile=\"%s\"" % self.fsprofile
157 if self.encrypted:
158 retval += " --encrypted"
159
160 if self.passphrase != "":
161 retval += " --passphrase=\"%s\"" % self.passphrase
162
163 return retval
164
165class F11_PartData(F9_PartData):
166 removedKeywords = F9_PartData.removedKeywords + ["start", "end"]
167 removedAttrs = F9_PartData.removedAttrs + ["start", "end"]
168
169class F12_PartData(F11_PartData):
170 removedKeywords = F11_PartData.removedKeywords
171 removedAttrs = F11_PartData.removedAttrs
172
173 def __init__(self, *args, **kwargs):
174 F11_PartData.__init__(self, *args, **kwargs)
175
176 self.escrowcert = kwargs.get("escrowcert", "")
177 self.backuppassphrase = kwargs.get("backuppassphrase", False)
178
179 def _getArgsAsStr(self):
180 retval = F11_PartData._getArgsAsStr(self)
181
182 if self.encrypted and self.escrowcert != "":
183 retval += " --escrowcert=\"%s\"" % self.escrowcert
184
185 if self.backuppassphrase:
186 retval += " --backuppassphrase"
187
188 return retval
189
190F14_PartData = F12_PartData
191
192class FC3_Partition(KickstartCommand):
193 removedKeywords = KickstartCommand.removedKeywords
194 removedAttrs = KickstartCommand.removedAttrs
195
196 def __init__(self, writePriority=130, *args, **kwargs):
197 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
198 self.op = self._getParser()
199
200 self.partitions = kwargs.get("partitions", [])
201
202 def __str__(self):
203 retval = ""
204
205 for part in self.partitions:
206 retval += part.__str__()
207
208 if retval != "":
209 return "# Disk partitioning information\n" + retval
210 else:
211 return ""
212
213 def _getParser(self):
214 def part_cb (option, opt_str, value, parser):
215 if value.startswith("/dev/"):
216 parser.values.ensure_value(option.dest, value[5:])
217 else:
218 parser.values.ensure_value(option.dest, value)
219
220 op = KSOptionParser()
221 op.add_option("--active", dest="active", action="store_true",
222 default=False)
223 op.add_option("--asprimary", dest="primOnly", action="store_true",
224 default=False)
225 op.add_option("--end", dest="end", action="store", type="int",
226 nargs=1)
227 op.add_option("--fstype", "--type", dest="fstype")
228 op.add_option("--grow", dest="grow", action="store_true", default=False)
229 op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
230 nargs=1)
231 op.add_option("--noformat", dest="format", action="store_false",
232 default=True)
233 op.add_option("--onbiosdisk", dest="onbiosdisk")
234 op.add_option("--ondisk", "--ondrive", dest="disk")
235 op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
236 callback=part_cb, nargs=1, type="string")
237 op.add_option("--recommended", dest="recommended", action="store_true",
238 default=False)
239 op.add_option("--size", dest="size", action="store", type="int",
240 nargs=1)
241 op.add_option("--start", dest="start", action="store", type="int",
242 nargs=1)
243 return op
244
245 def parse(self, args):
246 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
247
248 pd = self.handler.PartData()
249 self._setToObj(self.op, opts, pd)
250 pd.lineno = self.lineno
251 if extra:
252 pd.mountpoint = extra[0]
253 if pd in self.dataList():
254 warnings.warn(_("A partition with the mountpoint %s has already been defined.") % pd.mountpoint)
255 else:
256 pd.mountpoint = None
257
258 return pd
259
260 def dataList(self):
261 return self.partitions
262
263class FC4_Partition(FC3_Partition):
264 removedKeywords = FC3_Partition.removedKeywords
265 removedAttrs = FC3_Partition.removedAttrs
266
267 def __init__(self, writePriority=130, *args, **kwargs):
268 FC3_Partition.__init__(self, writePriority, *args, **kwargs)
269
270 def part_cb (option, opt_str, value, parser):
271 if value.startswith("/dev/"):
272 parser.values.ensure_value(option.dest, value[5:])
273 else:
274 parser.values.ensure_value(option.dest, value)
275
276 def _getParser(self):
277 op = FC3_Partition._getParser(self)
278 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
279 type="int", nargs=1)
280 op.add_option("--fsoptions", dest="fsopts")
281 op.add_option("--label", dest="label")
282 return op
283
284class RHEL5_Partition(FC4_Partition):
285 removedKeywords = FC4_Partition.removedKeywords
286 removedAttrs = FC4_Partition.removedAttrs
287
288 def __init__(self, writePriority=130, *args, **kwargs):
289 FC4_Partition.__init__(self, writePriority, *args, **kwargs)
290
291 def part_cb (option, opt_str, value, parser):
292 if value.startswith("/dev/"):
293 parser.values.ensure_value(option.dest, value[5:])
294 else:
295 parser.values.ensure_value(option.dest, value)
296
297 def _getParser(self):
298 op = FC4_Partition._getParser(self)
299 op.add_option("--encrypted", action="store_true", default=False)
300 op.add_option("--passphrase")
301 return op
302
303class F9_Partition(FC4_Partition):
304 removedKeywords = FC4_Partition.removedKeywords
305 removedAttrs = FC4_Partition.removedAttrs
306
307 def __init__(self, writePriority=130, *args, **kwargs):
308 FC4_Partition.__init__(self, writePriority, *args, **kwargs)
309
310 def part_cb (option, opt_str, value, parser):
311 if value.startswith("/dev/"):
312 parser.values.ensure_value(option.dest, value[5:])
313 else:
314 parser.values.ensure_value(option.dest, value)
315
316 def _getParser(self):
317 op = FC4_Partition._getParser(self)
318 op.add_option("--bytes-per-inode", deprecated=1)
319 op.add_option("--fsprofile")
320 op.add_option("--encrypted", action="store_true", default=False)
321 op.add_option("--passphrase")
322 return op
323
324class F11_Partition(F9_Partition):
325 removedKeywords = F9_Partition.removedKeywords
326 removedAttrs = F9_Partition.removedAttrs
327
328 def _getParser(self):
329 op = F9_Partition._getParser(self)
330 op.add_option("--start", deprecated=1)
331 op.add_option("--end", deprecated=1)
332 return op
333
334class F12_Partition(F11_Partition):
335 removedKeywords = F11_Partition.removedKeywords
336 removedAttrs = F11_Partition.removedAttrs
337
338 def _getParser(self):
339 op = F11_Partition._getParser(self)
340 op.add_option("--escrowcert")
341 op.add_option("--backuppassphrase", action="store_true", default=False)
342 return op
343
344class F14_Partition(F12_Partition):
345 removedKeywords = F12_Partition.removedKeywords
346 removedAttrs = F12_Partition.removedAttrs
347
348 def _getParser(self):
349 op = F12_Partition._getParser(self)
350 op.remove_option("--bytes-per-inode")
351 op.remove_option("--start")
352 op.remove_option("--end")
353 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py b/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py
new file mode 100644
index 0000000000..0f4c92a107
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py
@@ -0,0 +1,365 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25import warnings
26_ = lambda x: gettext.ldgettext("pykickstart", x)
27
28class FC3_RaidData(BaseData):
29 removedKeywords = BaseData.removedKeywords
30 removedAttrs = BaseData.removedAttrs
31
32 def __init__(self, *args, **kwargs):
33 BaseData.__init__(self, *args, **kwargs)
34 self.device = kwargs.get("device", None)
35 self.fstype = kwargs.get("fstype", "")
36 self.level = kwargs.get("level", "")
37 self.format = kwargs.get("format", True)
38 self.spares = kwargs.get("spares", 0)
39 self.preexist = kwargs.get("preexist", False)
40 self.mountpoint = kwargs.get("mountpoint", "")
41 self.members = kwargs.get("members", [])
42
43 def __eq__(self, y):
44 return self.device == y.device
45
46 def _getArgsAsStr(self):
47 retval = ""
48
49 if self.device != "":
50 retval += " --device=%s" % self.device
51 if self.fstype != "":
52 retval += " --fstype=\"%s\"" % self.fstype
53 if self.level != "":
54 retval += " --level=%s" % self.level
55 if not self.format:
56 retval += " --noformat"
57 if self.spares != 0:
58 retval += " --spares=%d" % self.spares
59 if self.preexist:
60 retval += " --useexisting"
61
62 return retval
63
64 def __str__(self):
65 retval = BaseData.__str__(self)
66 retval += "raid %s%s %s\n" % (self.mountpoint, self._getArgsAsStr(),
67 " ".join(self.members))
68 return retval
69
70class FC4_RaidData(FC3_RaidData):
71 removedKeywords = FC3_RaidData.removedKeywords
72 removedAttrs = FC3_RaidData.removedAttrs
73
74 def __init__(self, *args, **kwargs):
75 FC3_RaidData.__init__(self, *args, **kwargs)
76 self.fsopts = kwargs.get("fsopts", "")
77
78 def _getArgsAsStr(self):
79 retval = FC3_RaidData._getArgsAsStr(self)
80
81 if self.fsopts != "":
82 retval += " --fsoptions=\"%s\"" % self.fsopts
83
84 return retval
85
86class FC5_RaidData(FC4_RaidData):
87 removedKeywords = FC4_RaidData.removedKeywords
88 removedAttrs = FC4_RaidData.removedAttrs
89
90 def __init__(self, *args, **kwargs):
91 FC4_RaidData.__init__(self, *args, **kwargs)
92 self.bytesPerInode = kwargs.get("bytesPerInode", 4096)
93
94 def _getArgsAsStr(self):
95 retval = FC4_RaidData._getArgsAsStr(self)
96
97 if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0:
98 retval += " --bytes-per-inode=%d" % self.bytesPerInode
99
100 return retval
101
102class RHEL5_RaidData(FC5_RaidData):
103 removedKeywords = FC5_RaidData.removedKeywords
104 removedAttrs = FC5_RaidData.removedAttrs
105
106 def __init__(self, *args, **kwargs):
107 FC5_RaidData.__init__(self, *args, **kwargs)
108 self.encrypted = kwargs.get("encrypted", False)
109 self.passphrase = kwargs.get("passphrase", "")
110
111 def _getArgsAsStr(self):
112 retval = FC5_RaidData._getArgsAsStr(self)
113
114 if self.encrypted:
115 retval += " --encrypted"
116
117 if self.passphrase != "":
118 retval += " --passphrase=\"%s\"" % self.passphrase
119
120 return retval
121
122F7_RaidData = FC5_RaidData
123
124class F9_RaidData(FC5_RaidData):
125 removedKeywords = FC5_RaidData.removedKeywords + ["bytesPerInode"]
126 removedAttrs = FC5_RaidData.removedAttrs + ["bytesPerInode"]
127
128 def __init__(self, *args, **kwargs):
129 FC5_RaidData.__init__(self, *args, **kwargs)
130 self.deleteRemovedAttrs()
131
132 self.fsprofile = kwargs.get("fsprofile", "")
133 self.encrypted = kwargs.get("encrypted", False)
134 self.passphrase = kwargs.get("passphrase", "")
135
136 def _getArgsAsStr(self):
137 retval = FC5_RaidData._getArgsAsStr(self)
138
139 if self.fsprofile != "":
140 retval += " --fsprofile=\"%s\"" % self.fsprofile
141 if self.encrypted:
142 retval += " --encrypted"
143
144 if self.passphrase != "":
145 retval += " --passphrase=\"%s\"" % self.passphrase
146
147 return retval
148
149class F12_RaidData(F9_RaidData):
150 removedKeywords = F9_RaidData.removedKeywords
151 removedAttrs = F9_RaidData.removedAttrs
152
153 def __init__(self, *args, **kwargs):
154 F9_RaidData.__init__(self, *args, **kwargs)
155 self.deleteRemovedAttrs()
156
157 self.escrowcert = kwargs.get("escrowcert", "")
158 self.backuppassphrase = kwargs.get("backuppassphrase", False)
159
160 def _getArgsAsStr(self):
161 retval = F9_RaidData._getArgsAsStr(self)
162
163 if self.encrypted and self.escrowcert != "":
164 retval += " --escrowcert=\"%s\"" % self.escrowcert
165
166 if self.backuppassphrase:
167 retval += " --backuppassphrase"
168 return retval
169
170F13_RaidData = F12_RaidData
171
172F14_RaidData = F13_RaidData
173
174class F15_RaidData(F14_RaidData):
175 removedKeywords = F14_RaidData.removedKeywords
176 removedAttrs = F14_RaidData.removedAttrs
177
178 def __init__(self, *args, **kwargs):
179 F14_RaidData.__init__(self, *args, **kwargs)
180 self.deleteRemovedAttrs()
181
182 self.label = kwargs.get("label", "")
183
184 def _getArgsAsStr(self):
185 retval = F14_RaidData._getArgsAsStr(self)
186
187 if self.label != "":
188 retval += " --label=%s" % self.label
189
190 return retval
191
192class FC3_Raid(KickstartCommand):
193 removedKeywords = KickstartCommand.removedKeywords
194 removedAttrs = KickstartCommand.removedAttrs
195
196 def __init__(self, writePriority=131, *args, **kwargs):
197 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
198 self.op = self._getParser()
199
200 # A dict of all the RAID levels we support. This means that if we
201 # support more levels in the future, subclasses don't have to
202 # duplicate too much.
203 self.levelMap = { "RAID0": "RAID0", "0": "RAID0",
204 "RAID1": "RAID1", "1": "RAID1",
205 "RAID5": "RAID5", "5": "RAID5",
206 "RAID6": "RAID6", "6": "RAID6" }
207
208 self.raidList = kwargs.get("raidList", [])
209
210 def __str__(self):
211 retval = ""
212
213 for raid in self.raidList:
214 retval += raid.__str__()
215
216 return retval
217
218 def _getParser(self):
219 def raid_cb (option, opt_str, value, parser):
220 parser.values.format = False
221 parser.values.preexist = True
222
223 def device_cb (option, opt_str, value, parser):
224 if value[0:2] == "md":
225 parser.values.ensure_value(option.dest, value[2:])
226 else:
227 parser.values.ensure_value(option.dest, value)
228
229 def level_cb (option, opt_str, value, parser):
230 if self.levelMap.has_key(value):
231 parser.values.ensure_value(option.dest, self.levelMap[value])
232
233 op = KSOptionParser()
234 op.add_option("--device", action="callback", callback=device_cb,
235 dest="device", type="string", nargs=1, required=1)
236 op.add_option("--fstype", dest="fstype")
237 op.add_option("--level", dest="level", action="callback",
238 callback=level_cb, type="string", nargs=1)
239 op.add_option("--noformat", action="callback", callback=raid_cb,
240 dest="format", default=True, nargs=0)
241 op.add_option("--spares", dest="spares", action="store", type="int",
242 nargs=1, default=0)
243 op.add_option("--useexisting", dest="preexist", action="store_true",
244 default=False)
245 return op
246
247 def parse(self, args):
248 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
249
250 if len(extra) == 0:
251 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "raid")
252 if len(extra) == 1:
253 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Partitions required for %s") % "raid")
254
255 rd = self.handler.RaidData()
256 self._setToObj(self.op, opts, rd)
257 rd.lineno = self.lineno
258
259 # --device can't just take an int in the callback above, because it
260 # could be specificed as "mdX", which causes optparse to error when
261 # it runs int().
262 rd.device = int(rd.device)
263 rd.mountpoint = extra[0]
264 rd.members = extra[1:]
265
266 # Check for duplicates in the data list.
267 if rd in self.dataList():
268 warnings.warn(_("A RAID device with the name %s has already been defined.") % rd.device)
269
270 return rd
271
272 def dataList(self):
273 return self.raidList
274
275class FC4_Raid(FC3_Raid):
276 removedKeywords = FC3_Raid.removedKeywords
277 removedAttrs = FC3_Raid.removedAttrs
278
279 def _getParser(self):
280 op = FC3_Raid._getParser(self)
281 op.add_option("--fsoptions", dest="fsopts")
282 return op
283
284class FC5_Raid(FC4_Raid):
285 removedKeywords = FC4_Raid.removedKeywords
286 removedAttrs = FC4_Raid.removedAttrs
287
288 def _getParser(self):
289 op = FC4_Raid._getParser(self)
290 op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
291 type="int", nargs=1)
292 return op
293
294class RHEL5_Raid(FC5_Raid):
295 removedKeywords = FC5_Raid.removedKeywords
296 removedAttrs = FC5_Raid.removedAttrs
297
298 def __init__(self, writePriority=131, *args, **kwargs):
299 FC5_Raid.__init__(self, writePriority, *args, **kwargs)
300
301 self.levelMap.update({"RAID10": "RAID10", "10": "RAID10"})
302
303 def _getParser(self):
304 op = FC5_Raid._getParser(self)
305 op.add_option("--encrypted", action="store_true", default=False)
306 op.add_option("--passphrase")
307 return op
308
309class F7_Raid(FC5_Raid):
310 removedKeywords = FC5_Raid.removedKeywords
311 removedAttrs = FC5_Raid.removedAttrs
312
313 def __init__(self, writePriority=131, *args, **kwargs):
314 FC5_Raid.__init__(self, writePriority, *args, **kwargs)
315
316 self.levelMap.update({"RAID10": "RAID10", "10": "RAID10"})
317
318class F9_Raid(F7_Raid):
319 removedKeywords = F7_Raid.removedKeywords
320 removedAttrs = F7_Raid.removedAttrs
321
322 def _getParser(self):
323 op = F7_Raid._getParser(self)
324 op.add_option("--bytes-per-inode", deprecated=1)
325 op.add_option("--fsprofile")
326 op.add_option("--encrypted", action="store_true", default=False)
327 op.add_option("--passphrase")
328 return op
329
330class F12_Raid(F9_Raid):
331 removedKeywords = F9_Raid.removedKeywords
332 removedAttrs = F9_Raid.removedAttrs
333
334 def _getParser(self):
335 op = F9_Raid._getParser(self)
336 op.add_option("--escrowcert")
337 op.add_option("--backuppassphrase", action="store_true", default=False)
338 return op
339
340class F13_Raid(F12_Raid):
341 removedKeywords = F12_Raid.removedKeywords
342 removedAttrs = F12_Raid.removedAttrs
343
344 def __init__(self, writePriority=131, *args, **kwargs):
345 F12_Raid.__init__(self, writePriority, *args, **kwargs)
346
347 self.levelMap.update({"RAID4": "RAID4", "4": "RAID4"})
348
349class F14_Raid(F13_Raid):
350 removedKeywords = F13_Raid.removedKeywords
351 removedAttrs = F13_Raid.removedAttrs
352
353 def _getParser(self):
354 op = F13_Raid._getParser(self)
355 op.remove_option("--bytes-per-inode")
356 return op
357
358class F15_Raid(F14_Raid):
359 removedKeywords = F14_Raid.removedKeywords
360 removedAttrs = F14_Raid.removedAttrs
361
362 def _getParser(self):
363 op = F14_Raid._getParser(self)
364 op.add_option("--label")
365 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py b/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py
new file mode 100644
index 0000000000..391af14c22
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py
@@ -0,0 +1,79 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25class FC3_Reboot(KickstartCommand):
26 removedKeywords = KickstartCommand.removedKeywords
27 removedAttrs = KickstartCommand.removedAttrs
28
29 def __init__(self, writePriority=0, *args, **kwargs):
30 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
31 self.action = kwargs.get("action", None)
32
33 def __str__(self):
34 retval = KickstartCommand.__str__(self)
35
36 if self.action == KS_REBOOT:
37 retval += "# Reboot after installation\nreboot\n"
38 elif self.action == KS_SHUTDOWN:
39 retval += "# Shutdown after installation\nshutdown\n"
40
41 return retval
42
43 def parse(self, args):
44 if self.currentCmd == "reboot":
45 self.action = KS_REBOOT
46 else:
47 self.action = KS_SHUTDOWN
48
49 return self
50
51class FC6_Reboot(FC3_Reboot):
52 removedKeywords = FC3_Reboot.removedKeywords
53 removedAttrs = FC3_Reboot.removedAttrs
54
55 def __init__(self, writePriority=0, *args, **kwargs):
56 FC3_Reboot.__init__(self, writePriority, *args, **kwargs)
57 self.op = self._getParser()
58
59 self.eject = kwargs.get("eject", False)
60
61 def __str__(self):
62 retval = FC3_Reboot.__str__(self).rstrip()
63
64 if self.eject:
65 retval += " --eject"
66
67 return retval + "\n"
68
69 def _getParser(self):
70 op = KSOptionParser()
71 op.add_option("--eject", dest="eject", action="store_true",
72 default=False)
73 return op
74
75 def parse(self, args):
76 FC3_Reboot.parse(self, args)
77 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
78 self._setToSelf(self.op, opts)
79 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py b/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py
new file mode 100644
index 0000000000..543ef947c1
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py
@@ -0,0 +1,249 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007, 2008, 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26import warnings
27_ = lambda x: gettext.ldgettext("pykickstart", x)
28
29class FC6_RepoData(BaseData):
30 removedKeywords = BaseData.removedKeywords
31 removedAttrs = BaseData.removedAttrs
32
33 def __init__(self, *args, **kwargs):
34 BaseData.__init__(self, *args, **kwargs)
35 self.baseurl = kwargs.get("baseurl", "")
36 self.mirrorlist = kwargs.get("mirrorlist", None)
37 self.name = kwargs.get("name", "")
38
39 def __eq__(self, y):
40 return self.name == y.name
41
42 def _getArgsAsStr(self):
43 retval = ""
44
45 if self.baseurl:
46 retval += "--baseurl=%s" % self.baseurl
47 elif self.mirrorlist:
48 retval += "--mirrorlist=%s" % self.mirrorlist
49
50 return retval
51
52 def __str__(self):
53 retval = BaseData.__str__(self)
54 retval += "repo --name=\"%s\" %s\n" % (self.name, self._getArgsAsStr())
55 return retval
56
57class F8_RepoData(FC6_RepoData):
58 removedKeywords = FC6_RepoData.removedKeywords
59 removedAttrs = FC6_RepoData.removedAttrs
60
61 def __init__(self, *args, **kwargs):
62 FC6_RepoData.__init__(self, *args, **kwargs)
63 self.cost = kwargs.get("cost", None)
64 self.includepkgs = kwargs.get("includepkgs", [])
65 self.excludepkgs = kwargs.get("excludepkgs", [])
66
67 def _getArgsAsStr(self):
68 retval = FC6_RepoData._getArgsAsStr(self)
69
70 if self.cost:
71 retval += " --cost=%s" % self.cost
72 if self.includepkgs:
73 retval += " --includepkgs=\"%s\"" % ",".join(self.includepkgs)
74 if self.excludepkgs:
75 retval += " --excludepkgs=\"%s\"" % ",".join(self.excludepkgs)
76
77 return retval
78
79class F11_RepoData(F8_RepoData):
80 removedKeywords = F8_RepoData.removedKeywords
81 removedAttrs = F8_RepoData.removedAttrs
82
83 def __init__(self, *args, **kwargs):
84 F8_RepoData.__init__(self, *args, **kwargs)
85 self.ignoregroups = kwargs.get("ignoregroups", None)
86
87 def _getArgsAsStr(self):
88 retval = F8_RepoData._getArgsAsStr(self)
89
90 if self.ignoregroups:
91 retval += " --ignoregroups=true"
92 return retval
93
94class F13_RepoData(F11_RepoData):
95 removedKeywords = F11_RepoData.removedKeywords
96 removedAttrs = F11_RepoData.removedAttrs
97
98 def __init__(self, *args, **kwargs):
99 F11_RepoData.__init__(self, *args, **kwargs)
100 self.proxy = kwargs.get("proxy", "")
101
102 def _getArgsAsStr(self):
103 retval = F11_RepoData._getArgsAsStr(self)
104
105 if self.proxy:
106 retval += " --proxy=\"%s\"" % self.proxy
107
108 return retval
109
110class F14_RepoData(F13_RepoData):
111 removedKeywords = F13_RepoData.removedKeywords
112 removedAttrs = F13_RepoData.removedAttrs
113
114 def __init__(self, *args, **kwargs):
115 F13_RepoData.__init__(self, *args, **kwargs)
116 self.noverifyssl = kwargs.get("noverifyssl", False)
117
118 def _getArgsAsStr(self):
119 retval = F13_RepoData._getArgsAsStr(self)
120
121 if self.noverifyssl:
122 retval += " --noverifyssl"
123
124 return retval
125
126RHEL6_RepoData = F14_RepoData
127
128F15_RepoData = F14_RepoData
129
130class FC6_Repo(KickstartCommand):
131 removedKeywords = KickstartCommand.removedKeywords
132 removedAttrs = KickstartCommand.removedAttrs
133
134 urlRequired = True
135
136 def __init__(self, writePriority=0, *args, **kwargs):
137 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
138 self.op = self._getParser()
139
140 self.repoList = kwargs.get("repoList", [])
141
142 def __str__(self):
143 retval = ""
144 for repo in self.repoList:
145 retval += repo.__str__()
146
147 return retval
148
149 def _getParser(self):
150 op = KSOptionParser()
151 op.add_option("--name", dest="name", required=1)
152 op.add_option("--baseurl")
153 op.add_option("--mirrorlist")
154 return op
155
156 def parse(self, args):
157 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
158
159 if len(extra) != 0:
160 mapping = {"command": "repo", "options": extra}
161 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
162
163 # This is lame, but I can't think of a better way to make sure only
164 # one of these two is specified.
165 if opts.baseurl and opts.mirrorlist:
166 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --baseurl and --mirrorlist may be specified for repo command."))
167
168 if self.urlRequired and not opts.baseurl and not opts.mirrorlist:
169 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --baseurl or --mirrorlist must be specified for repo command."))
170
171 rd = self.handler.RepoData()
172 self._setToObj(self.op, opts, rd)
173 rd.lineno = self.lineno
174
175 # Check for duplicates in the data list.
176 if rd in self.dataList():
177 warnings.warn(_("A repo with the name %s has already been defined.") % rd.name)
178
179 return rd
180
181 def dataList(self):
182 return self.repoList
183
184class F8_Repo(FC6_Repo):
185 removedKeywords = FC6_Repo.removedKeywords
186 removedAttrs = FC6_Repo.removedAttrs
187
188 def __str__(self):
189 retval = ""
190 for repo in self.repoList:
191 retval += repo.__str__()
192
193 return retval
194
195 def _getParser(self):
196 def list_cb (option, opt_str, value, parser):
197 for d in value.split(','):
198 parser.values.ensure_value(option.dest, []).append(d)
199
200 op = FC6_Repo._getParser(self)
201 op.add_option("--cost", action="store", type="int")
202 op.add_option("--excludepkgs", action="callback", callback=list_cb,
203 nargs=1, type="string")
204 op.add_option("--includepkgs", action="callback", callback=list_cb,
205 nargs=1, type="string")
206 return op
207
208 def methodToRepo(self):
209 if not self.handler.method.url:
210 raise KickstartError, formatErrorMsg(self.handler.method.lineno, msg=_("Method must be a url to be added to the repo list."))
211 reponame = "ks-method-url"
212 repourl = self.handler.method.url
213 rd = self.handler.RepoData(name=reponame, baseurl=repourl)
214 return rd
215
216class F11_Repo(F8_Repo):
217 removedKeywords = F8_Repo.removedKeywords
218 removedAttrs = F8_Repo.removedAttrs
219
220 def _getParser(self):
221 op = F8_Repo._getParser(self)
222 op.add_option("--ignoregroups", action="store", type="ksboolean")
223 return op
224
225class F13_Repo(F11_Repo):
226 removedKeywords = F11_Repo.removedKeywords
227 removedAttrs = F11_Repo.removedAttrs
228
229 def _getParser(self):
230 op = F11_Repo._getParser(self)
231 op.add_option("--proxy")
232 return op
233
234class F14_Repo(F13_Repo):
235 removedKeywords = F13_Repo.removedKeywords
236 removedAttrs = F13_Repo.removedAttrs
237
238 def _getParser(self):
239 op = F13_Repo._getParser(self)
240 op.add_option("--noverifyssl", action="store_true", default=False)
241 return op
242
243RHEL6_Repo = F14_Repo
244
245class F15_Repo(F14_Repo):
246 removedKeywords = F14_Repo.removedKeywords
247 removedAttrs = F14_Repo.removedAttrs
248
249 urlRequired = False
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py b/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py
new file mode 100644
index 0000000000..1893d4ea49
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py
@@ -0,0 +1,68 @@
1#
2# Alexander Todorov <atodorov@redhat.com>
3#
4# Copyright 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class F10_Rescue(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.rescue = False
36 self.nomount = kwargs.get("nomount", False)
37 self.romount = kwargs.get("romount", False)
38
39 def __str__(self):
40 retval = KickstartCommand.__str__(self)
41
42 if self.rescue:
43 retval += "rescue"
44
45 if self.nomount:
46 retval += " --nomount"
47 if self.romount:
48 retval += " --romount"
49
50 retval = "# Start rescue mode\n%s\n" % retval
51
52 return retval
53
54 def _getParser(self):
55 op = KSOptionParser()
56 op.add_option("--nomount", dest="nomount", action="store_true", default=False)
57 op.add_option("--romount", dest="romount", action="store_true", default=False)
58 return op
59
60 def parse(self, args):
61 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
62
63 if opts.nomount and opts.romount:
64 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --nomount and --romount may be specified for rescue command."))
65
66 self._setToSelf(self.op, opts)
67 self.rescue = True
68 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py b/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py
new file mode 100644
index 0000000000..e038b4525d
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py
@@ -0,0 +1,93 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_RootPw(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.isCrypted = kwargs.get("isCrypted", False)
36 self.password = kwargs.get("password", "")
37
38 def _getArgsAsStr(self):
39 retval = ""
40
41 if self.isCrypted:
42 retval += " --iscrypted"
43
44 return retval
45
46 def __str__(self):
47 retval = KickstartCommand.__str__(self)
48
49 if self.password != "":
50 retval += "# Root password\nrootpw%s %s\n" % (self._getArgsAsStr(), self.password)
51
52 return retval
53
54 def _getParser(self):
55 op = KSOptionParser()
56 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
57 default=False)
58 return op
59
60 def parse(self, args):
61 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
62 self._setToSelf(self.op, opts)
63
64 if len(extra) != 1:
65 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "rootpw")
66
67 self.password = extra[0]
68 return self
69
70class F8_RootPw(FC3_RootPw):
71 removedKeywords = FC3_RootPw.removedKeywords
72 removedAttrs = FC3_RootPw.removedAttrs
73
74 def __init__(self, writePriority=0, *args, **kwargs):
75 FC3_RootPw.__init__(self, writePriority, *args, **kwargs)
76 self.lock = kwargs.get("lock", False)
77
78 def _getArgsAsStr(self):
79 retval = FC3_RootPw._getArgsAsStr(self)
80
81 if self.lock:
82 retval += " --lock"
83
84 if not self.isCrypted:
85 retval += " --plaintext"
86
87 return retval
88
89 def _getParser(self):
90 op = FC3_RootPw._getParser(self)
91 op.add_option("--lock", dest="lock", action="store_true", default=False)
92 op.add_option("--plaintext", dest="isCrypted", action="store_false")
93 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py b/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py
new file mode 100644
index 0000000000..9f8059c76b
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py
@@ -0,0 +1,64 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.options import *
23
24class FC3_SELinux(KickstartCommand):
25 removedKeywords = KickstartCommand.removedKeywords
26 removedAttrs = KickstartCommand.removedAttrs
27
28 def __init__(self, writePriority=0, *args, **kwargs):
29 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
30 self.op = self._getParser()
31
32 self.selinux = kwargs.get("selinux", None)
33
34 def __str__(self):
35 retval = KickstartCommand.__str__(self)
36
37 if not retval and self.selinux is None:
38 return ""
39
40 retval += "# SELinux configuration\n"
41
42 if self.selinux == SELINUX_DISABLED:
43 retval += "selinux --disabled\n"
44 elif self.selinux == SELINUX_ENFORCING:
45 retval += "selinux --enforcing\n"
46 elif self.selinux == SELINUX_PERMISSIVE:
47 retval += "selinux --permissive\n"
48
49 return retval
50
51 def _getParser(self):
52 op = KSOptionParser()
53 op.add_option("--disabled", dest="selinux", action="store_const",
54 const=SELINUX_DISABLED)
55 op.add_option("--enforcing", dest="selinux", action="store_const",
56 const=SELINUX_ENFORCING)
57 op.add_option("--permissive", dest="selinux", action="store_const",
58 const=SELINUX_PERMISSIVE)
59 return op
60
61 def parse(self, args):
62 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
63 self._setToSelf(self.op, opts)
64 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/services.py b/scripts/lib/mic/3rdparty/pykickstart/commands/services.py
new file mode 100644
index 0000000000..2e0eab8007
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/services.py
@@ -0,0 +1,71 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC6_Services(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.disabled = kwargs.get("disabled", [])
36 self.enabled = kwargs.get("enabled", [])
37
38 def __str__(self):
39 retval = KickstartCommand.__str__(self)
40 args = ""
41
42 if len(self.disabled) > 0:
43 args += " --disabled=\"%s\"" % ",".join(self.disabled)
44 if len(self.enabled) > 0:
45 args += " --enabled=\"%s\"" % ",".join(self.enabled)
46
47 if args != "":
48 retval += "# System services\nservices%s\n" % args
49
50 return retval
51
52 def _getParser(self):
53 def services_cb (option, opt_str, value, parser):
54 for d in value.split(','):
55 parser.values.ensure_value(option.dest, []).append(d.strip())
56
57 op = KSOptionParser()
58 op.add_option("--disabled", dest="disabled", action="callback",
59 callback=services_cb, nargs=1, type="string")
60 op.add_option("--enabled", dest="enabled", action="callback",
61 callback=services_cb, nargs=1, type="string")
62 return op
63
64 def parse(self, args):
65 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
66 self._setToSelf(self.op, opts)
67
68 if len(self.disabled) == 0 and len(self.enabled) == 0:
69 raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("One of --disabled or --enabled must be provided."))
70
71 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py b/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py
new file mode 100644
index 0000000000..36d1a8d5ba
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py
@@ -0,0 +1,54 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_SkipX(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34 self.skipx = kwargs.get("skipx", False)
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if self.skipx:
40 retval += "# Do not configure the X Window System\nskipx\n"
41
42 return retval
43
44 def _getParser(self):
45 op = KSOptionParser()
46 return op
47
48 def parse(self, args):
49 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
50 if len(extra) > 0:
51 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "skipx")
52
53 self.skipx = True
54 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py b/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py
new file mode 100644
index 0000000000..e7867ebfb2
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py
@@ -0,0 +1,105 @@
1#
2# Peter Jones <pjones@redhat.com>
3#
4# Copyright 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class F13_SshPwData(BaseData):
28 removedKeywords = BaseData.removedKeywords
29 removedAttrs = BaseData.removedAttrs
30
31 def __init__(self, *args, **kwargs):
32 BaseData.__init__(self, *args, **kwargs)
33 self.username = kwargs.get("username", None)
34 self.isCrypted = kwargs.get("isCrypted", False)
35 self.password = kwargs.get("password", "")
36 self.lock = kwargs.get("lock", False)
37
38 def __eq__(self, y):
39 return self.username == y.username
40
41 def __str__(self):
42 retval = BaseData.__str__(self)
43
44 retval += "sshpw"
45 retval += self._getArgsAsStr() + '\n'
46
47 return retval
48
49 def _getArgsAsStr(self):
50 retval = ""
51
52 retval += " --username=%s" % self.username
53 if self.lock:
54 retval += " --lock"
55 if self.isCrypted:
56 retval += " --iscrypted"
57 else:
58 retval += " --plaintext"
59
60 retval += " %s" % self.password
61 return retval
62
63class F13_SshPw(KickstartCommand):
64 removedKeywords = KickstartCommand.removedKeywords
65 removedAttrs = KickstartCommand.removedAttrs
66
67 def __init__(self, writePriority=0, *args, **kwargs):
68 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
69 self.op = self._getParser()
70
71 self.sshUserList = kwargs.get("sshUserList", [])
72
73 def __str__(self):
74 retval = ""
75 for user in self.sshUserList:
76 retval += user.__str__()
77
78 return retval
79
80 def _getParser(self):
81 op = KSOptionParser()
82 op.add_option("--username", dest="username", required=True)
83 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
84 default=False)
85 op.add_option("--plaintext", dest="isCrypted", action="store_false")
86 op.add_option("--lock", dest="lock", action="store_true", default=False)
87 return op
88
89 def parse(self, args):
90 ud = self.handler.SshPwData()
91 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
92 self._setToObj(self.op, opts, ud)
93 ud.lineno = self.lineno
94
95 if len(extra) != 1:
96 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "sshpw")
97 ud.password = extra[0]
98
99 if ud in self.dataList():
100 warnings.warn(_("An ssh user with the name %s has already been defined.") % ud.name)
101
102 return ud
103
104 def dataList(self):
105 return self.sshUserList
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py b/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py
new file mode 100644
index 0000000000..f5441de593
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py
@@ -0,0 +1,86 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_Timezone(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.isUtc = kwargs.get("isUtc", False)
36 self.timezone = kwargs.get("timezone", "")
37
38 def __str__(self):
39 retval = KickstartCommand.__str__(self)
40
41 if self.timezone != "":
42 if self.isUtc:
43 utc = "--utc"
44 else:
45 utc = ""
46
47 retval += "# System timezone\ntimezone %s %s\n" %(utc, self.timezone)
48
49 return retval
50
51 def _getParser(self):
52 op = KSOptionParser()
53 op.add_option("--utc", dest="isUtc", action="store_true", default=False)
54 return op
55
56 def parse(self, args):
57 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
58 self._setToSelf(self.op, opts)
59
60 if len(extra) != 1:
61 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "timezone")
62
63 self.timezone = extra[0]
64 return self
65
66class FC6_Timezone(FC3_Timezone):
67 removedKeywords = FC3_Timezone.removedKeywords
68 removedAttrs = FC3_Timezone.removedAttrs
69
70 def __str__(self):
71 retval = KickstartCommand.__str__(self)
72
73 if self.timezone != "":
74 if self.isUtc:
75 utc = "--isUtc"
76 else:
77 utc = ""
78
79 retval += "# System timezone\ntimezone %s %s\n" %(utc, self.timezone)
80
81 return retval
82
83 def _getParser(self):
84 op = FC3_Timezone._getParser(self)
85 op.add_option("--utc", "--isUtc", dest="isUtc", action="store_true", default=False)
86 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py b/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py
new file mode 100644
index 0000000000..53ec49f7b8
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py
@@ -0,0 +1,60 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class F7_Updates(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34 self.url = kwargs.get("url", "")
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if self.url == "floppy":
40 retval += "updates\n"
41 elif self.url != "":
42 retval += "updates %s\n" % self.url
43
44 return retval
45
46 def _getParser(self):
47 op = KSOptionParser()
48 return op
49
50 def parse(self, args):
51 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
52
53 if len(extra) > 1:
54 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s only takes one argument") % "updates")
55 elif len(extra) == 0:
56 self.url = "floppy"
57 else:
58 self.url = extra[0]
59
60 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py b/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py
new file mode 100644
index 0000000000..a68a82d378
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py
@@ -0,0 +1,106 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_Upgrade(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.upgrade = kwargs.get("upgrade", None)
34 self.op = self._getParser()
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if self.upgrade is None:
40 return retval
41
42 if self.upgrade:
43 retval += "# Upgrade existing installation\nupgrade\n"
44 else:
45 retval += "# Install OS instead of upgrade\ninstall\n"
46
47 return retval
48
49 def _getParser(self):
50 op = KSOptionParser()
51 return op
52
53 def parse(self, args):
54 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
55
56 if len(extra) > 0:
57 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "upgrade")
58
59 if self.currentCmd == "upgrade":
60 self.upgrade = True
61 else:
62 self.upgrade = False
63
64 return self
65
66class F11_Upgrade(FC3_Upgrade):
67 removedKeywords = FC3_Upgrade.removedKeywords
68 removedAttrs = FC3_Upgrade.removedAttrs
69
70 def __init__(self, writePriority=0, *args, **kwargs):
71 FC3_Upgrade.__init__(self, writePriority, *args, **kwargs)
72
73 self.op = self._getParser()
74 self.root_device = kwargs.get("root_device", None)
75
76 def __str__(self):
77 if self.upgrade and (self.root_device is not None):
78 retval = KickstartCommand.__str__(self)
79 retval += "# Upgrade existing installation\nupgrade --root-device=%s\n" % self.root_device
80 else:
81 retval = FC3_Upgrade.__str__(self)
82
83 return retval
84
85 def _getParser(self):
86 op = KSOptionParser()
87 op.add_option("--root-device", dest="root_device")
88 return op
89
90 def parse(self, args):
91 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
92
93 if len(extra) > 0:
94 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "upgrade")
95
96 if (opts.root_device is not None) and (opts.root_device == ""):
97 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not accept empty parameter %s") % ("upgrade", "--root-device"))
98 else:
99 self.root_device = opts.root_device
100
101 if self.currentCmd == "upgrade":
102 self.upgrade = True
103 else:
104 self.upgrade = False
105
106 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/user.py b/scripts/lib/mic/3rdparty/pykickstart/commands/user.py
new file mode 100644
index 0000000000..189dc7585f
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/user.py
@@ -0,0 +1,173 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.constants import *
22from pykickstart.errors import *
23from pykickstart.options import *
24
25import gettext
26import warnings
27_ = lambda x: gettext.ldgettext("pykickstart", x)
28
29class FC6_UserData(BaseData):
30 removedKeywords = BaseData.removedKeywords
31 removedAttrs = BaseData.removedAttrs
32
33 def __init__(self, *args, **kwargs):
34 BaseData.__init__(self, *args, **kwargs)
35 self.groups = kwargs.get("groups", [])
36 self.homedir = kwargs.get("homedir", "")
37 self.isCrypted = kwargs.get("isCrypted", False)
38 self.name = kwargs.get("name", "")
39 self.password = kwargs.get("password", "")
40 self.shell = kwargs.get("shell", "")
41 self.uid = kwargs.get("uid", None)
42
43 def __eq__(self, y):
44 return self.name == y.name
45
46 def __str__(self):
47 retval = BaseData.__str__(self)
48
49 if self.uid != "":
50 retval += "user"
51 retval += self._getArgsAsStr() + "\n"
52
53 return retval
54
55 def _getArgsAsStr(self):
56 retval = ""
57
58 if len(self.groups) > 0:
59 retval += " --groups=%s" % ",".join(self.groups)
60 if self.homedir:
61 retval += " --homedir=%s" % self.homedir
62 if self.name:
63 retval += " --name=%s" % self.name
64 if self.password:
65 retval += " --password=%s" % self.password
66 if self.isCrypted:
67 retval += " --iscrypted"
68 if self.shell:
69 retval += " --shell=%s" % self.shell
70 if self.uid:
71 retval += " --uid=%s" % self.uid
72
73 return retval
74
75class F8_UserData(FC6_UserData):
76 removedKeywords = FC6_UserData.removedKeywords
77 removedAttrs = FC6_UserData.removedAttrs
78
79 def __init__(self, *args, **kwargs):
80 FC6_UserData.__init__(self, *args, **kwargs)
81 self.lock = kwargs.get("lock", False)
82
83 def _getArgsAsStr(self):
84 retval = FC6_UserData._getArgsAsStr(self)
85
86 if self.lock:
87 retval += " --lock"
88
89 return retval
90
91class F12_UserData(F8_UserData):
92 removedKeywords = F8_UserData.removedKeywords
93 removedAttrs = F8_UserData.removedAttrs
94
95 def __init__(self, *args, **kwargs):
96 F8_UserData.__init__(self, *args, **kwargs)
97 self.gecos = kwargs.get("gecos", "")
98
99 def _getArgsAsStr(self):
100 retval = F8_UserData._getArgsAsStr(self)
101
102 if self.gecos:
103 retval += " --gecos=\"%s\"" % (self.gecos,)
104
105 return retval
106
107class FC6_User(KickstartCommand):
108 removedKeywords = KickstartCommand.removedKeywords
109 removedAttrs = KickstartCommand.removedAttrs
110
111 def __init__(self, writePriority=0, *args, **kwargs):
112 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
113 self.op = self._getParser()
114
115 self.userList = kwargs.get("userList", [])
116
117 def __str__(self):
118 retval = ""
119 for user in self.userList:
120 retval += user.__str__()
121
122 return retval
123
124 def _getParser(self):
125 def groups_cb (option, opt_str, value, parser):
126 for d in value.split(','):
127 parser.values.ensure_value(option.dest, []).append(d)
128
129 op = KSOptionParser()
130 op.add_option("--groups", dest="groups", action="callback",
131 callback=groups_cb, nargs=1, type="string")
132 op.add_option("--homedir")
133 op.add_option("--iscrypted", dest="isCrypted", action="store_true",
134 default=False)
135 op.add_option("--name", required=1)
136 op.add_option("--password")
137 op.add_option("--shell")
138 op.add_option("--uid", type="int")
139 return op
140
141 def parse(self, args):
142 ud = self.handler.UserData()
143 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
144 self._setToObj(self.op, opts, ud)
145 ud.lineno = self.lineno
146
147 # Check for duplicates in the data list.
148 if ud in self.dataList():
149 warnings.warn(_("A user with the name %s has already been defined.") % ud.name)
150
151 return ud
152
153 def dataList(self):
154 return self.userList
155
156class F8_User(FC6_User):
157 removedKeywords = FC6_User.removedKeywords
158 removedAttrs = FC6_User.removedAttrs
159
160 def _getParser(self):
161 op = FC6_User._getParser(self)
162 op.add_option("--lock", action="store_true", default=False)
163 op.add_option("--plaintext", dest="isCrypted", action="store_false")
164 return op
165
166class F12_User(F8_User):
167 removedKeywords = F8_User.removedKeywords
168 removedAttrs = F8_User.removedAttrs
169
170 def _getParser(self):
171 op = F8_User._getParser(self)
172 op.add_option("--gecos", type="string")
173 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py b/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py
new file mode 100644
index 0000000000..200ccfba2e
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py
@@ -0,0 +1,114 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24class FC3_Vnc(KickstartCommand):
25 removedKeywords = KickstartCommand.removedKeywords
26 removedAttrs = KickstartCommand.removedAttrs
27
28 def __init__(self, writePriority=0, *args, **kwargs):
29 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
30 self.op = self._getParser()
31
32 self.enabled = kwargs.get("enabled", False)
33 self.password = kwargs.get("password", "")
34 self.connect = kwargs.get("connect", "")
35
36 def __str__(self):
37 retval = KickstartCommand.__str__(self)
38
39 if not self.enabled:
40 return retval
41
42 retval += "vnc"
43
44 if self.connect != "":
45 retval += " --connect=%s" % self.connect
46 if self.password != "":
47 retval += " --password=%s" % self.password
48
49 return retval + "\n"
50
51 def _getParser(self):
52 op = KSOptionParser()
53 op.add_option("--connect")
54 op.add_option("--password", dest="password")
55 return op
56
57 def parse(self, args):
58 self.enabled = True
59 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
60 self._setToSelf(self.op, opts)
61 return self
62
63class FC6_Vnc(FC3_Vnc):
64 removedKeywords = FC3_Vnc.removedKeywords + ["connect"]
65 removedAttrs = FC3_Vnc.removedAttrs + ["connect"]
66
67 def __init__(self, writePriority=0, host="", port="", *args, **kwargs):
68 FC3_Vnc.__init__(self, writePriority, *args, **kwargs)
69 self.deleteRemovedAttrs()
70
71 self.host = kwargs.get("host", "")
72 self.port = kwargs.get("port", "")
73
74 def __str__(self):
75 retval = KickstartCommand.__str__(self)
76
77 if not self.enabled:
78 return retval
79
80 retval += "vnc"
81
82 if self.host != "":
83 retval += " --host=%s" % self.host
84
85 if self.port != "":
86 retval += " --port=%s" % self.port
87 if self.password != "":
88 retval += " --password=%s" % self.password
89
90 return retval + "\n"
91
92 def _getParser(self):
93 def connect_cb (option, opt_str, value, parser):
94 cargs = value.split(":")
95 parser.values.ensure_value("host", cargs[0])
96
97 if len(cargs) > 1:
98 parser.values.ensure_value("port", cargs[1])
99
100 op = FC3_Vnc._getParser(self)
101 op.add_option("--connect", action="callback", callback=connect_cb,
102 nargs=1, type="string")
103 op.add_option("--host", dest="host")
104 op.add_option("--port", dest="port")
105 return op
106
107class F9_Vnc(FC6_Vnc):
108 removedKeywords = FC6_Vnc.removedKeywords
109 removedAttrs = FC6_Vnc.removedAttrs
110
111 def _getParser(self):
112 op = FC6_Vnc._getParser(self)
113 op.remove_option("--connect")
114 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py b/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py
new file mode 100644
index 0000000000..255c47f0ae
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py
@@ -0,0 +1,102 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23import gettext
24import warnings
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_VolGroupData(BaseData):
28 removedKeywords = BaseData.removedKeywords
29 removedAttrs = BaseData.removedAttrs
30
31 def __init__(self, *args, **kwargs):
32 BaseData.__init__(self, *args, **kwargs)
33 self.format = kwargs.get("format", True)
34 self.pesize = kwargs.get("pesize", 32768)
35 self.preexist = kwargs.get("preexist", False)
36 self.vgname = kwargs.get("vgname", "")
37 self.physvols = kwargs.get("physvols", [])
38
39 def __eq__(self, y):
40 return self.vgname == y.vgname
41
42 def __str__(self):
43 retval = BaseData.__str__(self)
44 retval += "volgroup %s" % self.vgname
45
46 if not self.format:
47 retval += " --noformat"
48 if self.pesize != 0:
49 retval += " --pesize=%d" % self.pesize
50 if self.preexist:
51 retval += " --useexisting"
52
53 return retval + " " + " ".join(self.physvols) + "\n"
54
55class FC3_VolGroup(KickstartCommand):
56 removedKeywords = KickstartCommand.removedKeywords
57 removedAttrs = KickstartCommand.removedAttrs
58
59 def __init__(self, writePriority=132, *args, **kwargs):
60 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
61 self.op = self._getParser()
62
63 self.vgList = kwargs.get("vgList", [])
64
65 def __str__(self):
66 retval = ""
67 for vg in self.vgList:
68 retval += vg.__str__()
69
70 return retval
71
72 def _getParser(self):
73 # Have to be a little more complicated to set two values.
74 def vg_cb (option, opt_str, value, parser):
75 parser.values.format = False
76 parser.values.preexist = True
77
78 op = KSOptionParser()
79 op.add_option("--noformat", action="callback", callback=vg_cb,
80 dest="format", default=True, nargs=0)
81 op.add_option("--pesize", dest="pesize", type="int", nargs=1,
82 default=32768)
83 op.add_option("--useexisting", dest="preexist", action="store_true",
84 default=False)
85 return op
86
87 def parse(self, args):
88 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
89 vg = self.handler.VolGroupData()
90 self._setToObj(self.op, opts, vg)
91 vg.lineno = self.lineno
92 vg.vgname = extra[0]
93 vg.physvols = extra[1:]
94
95 # Check for duplicates in the data list.
96 if vg in self.dataList():
97 warnings.warn(_("A volgroup with the name %s has already been defined.") % vg.vgname)
98
99 return vg
100
101 def dataList(self):
102 return self.vgList
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py b/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py
new file mode 100644
index 0000000000..644ee86743
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py
@@ -0,0 +1,184 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24import gettext
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_XConfig(KickstartCommand):
28 removedKeywords = KickstartCommand.removedKeywords
29 removedAttrs = KickstartCommand.removedAttrs
30
31 def __init__(self, writePriority=0, *args, **kwargs):
32 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
33 self.op = self._getParser()
34
35 self.card = kwargs.get("card", "")
36 self.defaultdesktop = kwargs.get("defaultdesktop", "")
37 self.depth = kwargs.get("depth", 0)
38 self.hsync = kwargs.get("hsync", "")
39 self.monitor = kwargs.get("monitor", "")
40 self.noProbe = kwargs.get("noProbe", False)
41 self.resolution = kwargs.get("resolution", "")
42 self.server = kwargs.get("server", "")
43 self.startX = kwargs.get("startX", False)
44 self.videoRam = kwargs.get("videoRam", "")
45 self.vsync = kwargs.get("vsync", "")
46
47 def __str__(self):
48 retval = KickstartCommand.__str__(self)
49
50 if self.card != "":
51 retval += " --card=%s" % self.card
52 if self.defaultdesktop != "":
53 retval += " --defaultdesktop=%s" % self.defaultdesktop
54 if self.depth != 0:
55 retval += " --depth=%d" % self.depth
56 if self.hsync != "":
57 retval += " --hsync=%s" % self.hsync
58 if self.monitor != "":
59 retval += " --monitor=%s" % self.monitor
60 if self.noProbe:
61 retval += " --noprobe"
62 if self.resolution != "":
63 retval += " --resolution=%s" % self.resolution
64 if self.server != "":
65 retval += " --server=%s" % self.server
66 if self.startX:
67 retval += " --startxonboot"
68 if self.videoRam != "":
69 retval += " --videoram=%s" % self.videoRam
70 if self.vsync != "":
71 retval += " --vsync=%s" % self.vsync
72
73 if retval != "":
74 retval = "# X Window System configuration information\nxconfig %s\n" % retval
75
76 return retval
77
78 def _getParser(self):
79 op = KSOptionParser()
80 op.add_option("--card")
81 op.add_option("--defaultdesktop")
82 op.add_option("--depth", action="store", type="int", nargs=1)
83 op.add_option("--hsync")
84 op.add_option("--monitor")
85 op.add_option("--noprobe", dest="noProbe", action="store_true",
86 default=False)
87 op.add_option("--resolution")
88 op.add_option("--server")
89 op.add_option("--startxonboot", dest="startX", action="store_true",
90 default=False)
91 op.add_option("--videoram", dest="videoRam")
92 op.add_option("--vsync")
93 return op
94
95 def parse(self, args):
96 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
97 if extra:
98 mapping = {"command": "xconfig", "options": extra}
99 raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping)
100
101 self._setToSelf(self.op, opts)
102 return self
103
104class FC6_XConfig(FC3_XConfig):
105 removedKeywords = FC3_XConfig.removedKeywords + ["card", "hsync", "monitor", "noProbe", "vsync"]
106 removedAttrs = FC3_XConfig.removedAttrs + ["card", "hsync", "monitor", "noProbe", "vsync"]
107
108 def __init__(self, writePriority=0, *args, **kwargs):
109 FC3_XConfig.__init__(self, writePriority, *args, **kwargs)
110 self.deleteRemovedAttrs()
111
112 self.driver = kwargs.get("driver", "")
113
114 def __str__(self):
115 retval = KickstartCommand.__str__(self)
116
117 if hasattr(self, "driver") and self.driver != "":
118 retval += " --driver=%s" % self.driver
119 if self.defaultdesktop != "":
120 retval += " --defaultdesktop=%s" % self.defaultdesktop
121 if self.depth != 0:
122 retval += " --depth=%d" % self.depth
123 if hasattr(self, "resolution") and self.resolution != "":
124 retval += " --resolution=%s" % self.resolution
125 if self.startX:
126 retval += " --startxonboot"
127 if hasattr(self, "videoRam") and self.videoRam != "":
128 retval += " --videoram=%s" % self.videoRam
129
130 if retval != "":
131 retval = "# X Window System configuration information\nxconfig %s\n" % retval
132
133 return retval
134
135 def _getParser(self):
136 op = FC3_XConfig._getParser(self)
137 op.add_option("--card", deprecated=1)
138 op.add_option("--driver", dest="driver")
139 op.add_option("--hsync", deprecated=1)
140 op.add_option("--monitor", deprecated=1)
141 op.add_option("--noprobe", deprecated=1)
142 op.add_option("--vsync", deprecated=1)
143 return op
144
145class F9_XConfig(FC6_XConfig):
146 removedKeywords = FC6_XConfig.removedKeywords
147 removedAttrs = FC6_XConfig.removedAttrs
148
149 def _getParser(self):
150 op = FC6_XConfig._getParser(self)
151 op.remove_option("--card")
152 op.remove_option("--hsync")
153 op.remove_option("--monitor")
154 op.remove_option("--noprobe")
155 op.remove_option("--vsync")
156 return op
157
158class F10_XConfig(F9_XConfig):
159 removedKeywords = F9_XConfig.removedKeywords + ["driver", "resolution", "videoRam"]
160 removedAttrs = F9_XConfig.removedAttrs + ["driver", "resolution", "videoRam"]
161
162 def __init__(self, writePriority=0, *args, **kwargs):
163 F9_XConfig.__init__(self, writePriority, *args, **kwargs)
164 self.deleteRemovedAttrs()
165
166 def _getParser(self):
167 op = F9_XConfig._getParser(self)
168 op.add_option("--driver", deprecated=1)
169 op.add_option("--depth", deprecated=1)
170 op.add_option("--resolution", deprecated=1)
171 op.add_option("--videoram", deprecated=1)
172 return op
173
174class F14_XConfig(F10_XConfig):
175 removedKeywords = F10_XConfig.removedKeywords
176 removedAttrs = F10_XConfig.removedAttrs
177
178 def _getParser(self):
179 op = F10_XConfig._getParser(self)
180 op.remove_option("--driver")
181 op.remove_option("--depth")
182 op.remove_option("--resolution")
183 op.remove_option("--videoram")
184 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py b/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py
new file mode 100644
index 0000000000..79555a9b27
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py
@@ -0,0 +1,69 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20import warnings
21
22from pykickstart.base import *
23from pykickstart.options import *
24
25import gettext
26_ = lambda x: gettext.ldgettext("pykickstart", x)
27
28class FC3_ZeroMbr(KickstartCommand):
29 removedKeywords = KickstartCommand.removedKeywords
30 removedAttrs = KickstartCommand.removedAttrs
31
32 def __init__(self, writePriority=110, *args, **kwargs):
33 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
34 self.op = self._getParser()
35 self.zerombr = kwargs.get("zerombr", False)
36
37 def __str__(self):
38 retval = KickstartCommand.__str__(self)
39
40 if self.zerombr:
41 retval += "# Clear the Master Boot Record\nzerombr\n"
42
43 return retval
44
45 def _getParser(self):
46 op = KSOptionParser()
47 return op
48
49 def parse(self, args):
50 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
51
52 if len(extra) > 0:
53 warnings.warn(_("Ignoring deprecated option on line %s: The zerombr command no longer takes any options. In future releases, this will result in a fatal error from kickstart. Please modify your kickstart file to remove any options.") % self.lineno, DeprecationWarning)
54
55 self.zerombr = True
56 return self
57
58class F9_ZeroMbr(FC3_ZeroMbr):
59 removedKeywords = FC3_ZeroMbr.removedKeywords
60 removedAttrs = FC3_ZeroMbr.removedAttrs
61
62 def parse(self, args):
63 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
64
65 if len(extra) > 0:
66 raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "zerombr")
67
68 self.zerombr = True
69 return self
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py b/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py
new file mode 100644
index 0000000000..1ed2694c89
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py
@@ -0,0 +1,134 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.options import *
22
23import gettext
24import warnings
25_ = lambda x: gettext.ldgettext("pykickstart", x)
26
27class FC3_ZFCPData(BaseData):
28 removedKeywords = BaseData.removedKeywords
29 removedAttrs = BaseData.removedAttrs
30
31 def __init__(self, *args, **kwargs):
32 BaseData.__init__(self, *args, **kwargs)
33 self.devnum = kwargs.get("devnum", "")
34 self.wwpn = kwargs.get("wwpn", "")
35 self.fcplun = kwargs.get("fcplun", "")
36 self.scsiid = kwargs.get("scsiid", "")
37 self.scsilun = kwargs.get("scsilun", "")
38
39 def __eq__(self, y):
40 return self.devnum == y.devnum and self.wwpn == y.wwpn and \
41 self.fcplun == y.fcplun and self.scsiid == y.scsiid and \
42 self.scsilun == y.scsilun
43
44 def __str__(self):
45 retval = BaseData.__str__(self)
46 retval += "zfcp"
47
48 if self.devnum != "":
49 retval += " --devnum=%s" % self.devnum
50 if self.wwpn != "":
51 retval += " --wwpn=%s" % self.wwpn
52 if self.fcplun != "":
53 retval += " --fcplun=%s" % self.fcplun
54 if hasattr(self, "scsiid") and self.scsiid != "":
55 retval += " --scsiid=%s" % self.scsiid
56 if hasattr(self, "scsilun") and self.scsilun != "":
57 retval += " --scsilun=%s" % self.scsilun
58
59 return retval + "\n"
60
61class F12_ZFCPData(FC3_ZFCPData):
62 removedKeywords = FC3_ZFCPData.removedKeywords + ["scsiid", "scsilun"]
63 removedAttrs = FC3_ZFCPData.removedAttrs + ["scsiid", "scsilun"]
64
65 def __init__(self, *args, **kwargs):
66 FC3_ZFCPData.__init__(self, *args, **kwargs)
67 self.deleteRemovedAttrs()
68
69F14_ZFCPData = F12_ZFCPData
70
71class FC3_ZFCP(KickstartCommand):
72 removedKeywords = KickstartCommand.removedKeywords
73 removedAttrs = KickstartCommand.removedAttrs
74
75 def __init__(self, writePriority=71, *args, **kwargs):
76 KickstartCommand.__init__(self, writePriority, *args, **kwargs)
77 self.op = self._getParser()
78
79 self.zfcp = kwargs.get("zfcp", [])
80
81 def __str__(self):
82 retval = ""
83 for zfcp in self.zfcp:
84 retval += zfcp.__str__()
85
86 return retval
87
88 def _getParser(self):
89 op = KSOptionParser()
90 op.add_option("--devnum", dest="devnum", required=1)
91 op.add_option("--fcplun", dest="fcplun", required=1)
92 op.add_option("--scsiid", dest="scsiid", required=1)
93 op.add_option("--scsilun", dest="scsilun", required=1)
94 op.add_option("--wwpn", dest="wwpn", required=1)
95 return op
96
97 def parse(self, args):
98 zd = self.handler.ZFCPData()
99 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
100 self._setToObj(self.op, opts, zd)
101 zd.lineno = self.lineno
102
103 # Check for duplicates in the data list.
104 if zd in self.dataList():
105 warnings.warn(_("A zfcp with this information has already been defined."))
106
107 return zd
108
109 def dataList(self):
110 return self.zfcp
111
112class F12_ZFCP(FC3_ZFCP):
113 removedKeywords = FC3_ZFCP.removedKeywords
114 removedAttrs = FC3_ZFCP.removedAttrs + ["scsiid", "scsilun"]
115
116 def __init__(self, *args, **kwargs):
117 FC3_ZFCP.__init__(self, *args, **kwargs)
118 self.deleteRemovedAttrs()
119
120 def _getParser(self):
121 op = FC3_ZFCP._getParser(self)
122 op.add_option("--scsiid", deprecated=1)
123 op.add_option("--scsilun", deprecated=1)
124 return op
125
126class F14_ZFCP(F12_ZFCP):
127 removedKeywords = F12_ZFCP.removedKeywords
128 removedAttrs = F12_ZFCP.removedAttrs
129
130 def _getParser(self):
131 op = F12_ZFCP._getParser(self)
132 op.remove_option("--scsiid")
133 op.remove_option("--scsilun")
134 return op
diff --git a/scripts/lib/mic/3rdparty/pykickstart/constants.py b/scripts/lib/mic/3rdparty/pykickstart/constants.py
new file mode 100644
index 0000000000..5e12fc80ec
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/constants.py
@@ -0,0 +1,57 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005-2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20CLEARPART_TYPE_LINUX = 0
21CLEARPART_TYPE_ALL = 1
22CLEARPART_TYPE_NONE = 2
23
24DISPLAY_MODE_CMDLINE = 0
25DISPLAY_MODE_GRAPHICAL = 1
26DISPLAY_MODE_TEXT = 2
27
28FIRSTBOOT_DEFAULT = 0
29FIRSTBOOT_SKIP = 1
30FIRSTBOOT_RECONFIG = 2
31
32KS_MISSING_PROMPT = 0
33KS_MISSING_IGNORE = 1
34
35SELINUX_DISABLED = 0
36SELINUX_ENFORCING = 1
37SELINUX_PERMISSIVE = 2
38
39KS_SCRIPT_PRE = 0
40KS_SCRIPT_POST = 1
41KS_SCRIPT_TRACEBACK = 2
42
43KS_WAIT = 0
44KS_REBOOT = 1
45KS_SHUTDOWN = 2
46
47KS_INSTKEY_SKIP = -99
48
49BOOTPROTO_DHCP = "dhcp"
50BOOTPROTO_BOOTP = "bootp"
51BOOTPROTO_STATIC = "static"
52BOOTPROTO_QUERY = "query"
53BOOTPROTO_IBFT = "ibft"
54
55GROUP_REQUIRED = 0
56GROUP_DEFAULT = 1
57GROUP_ALL = 2
diff --git a/scripts/lib/mic/3rdparty/pykickstart/errors.py b/scripts/lib/mic/3rdparty/pykickstart/errors.py
new file mode 100644
index 0000000000..a234d99d43
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/errors.py
@@ -0,0 +1,103 @@
1#
2# errors.py: Kickstart error handling.
3#
4# Chris Lumens <clumens@redhat.com>
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20"""
21Error handling classes and functions.
22
23This module exports a single function:
24
25 formatErrorMsg - Properly formats an error message.
26
27It also exports several exception classes:
28
29 KickstartError - A generic exception class.
30
31 KickstartParseError - An exception for errors relating to parsing.
32
33 KickstartValueError - An exception for errors relating to option
34 processing.
35
36 KickstartVersionError - An exception for errors relating to unsupported
37 syntax versions.
38"""
39import gettext
40_ = lambda x: gettext.ldgettext("pykickstart", x)
41
42def formatErrorMsg(lineno, msg=""):
43 """Properly format the error message msg for inclusion in an exception."""
44 if msg != "":
45 mapping = {"lineno": lineno, "msg": msg}
46 return _("The following problem occurred on line %(lineno)s of the kickstart file:\n\n%(msg)s\n") % mapping
47 else:
48 return _("There was a problem reading from line %s of the kickstart file") % lineno
49
50class KickstartError(Exception):
51 """A generic exception class for unspecific error conditions."""
52 def __init__(self, val = ""):
53 """Create a new KickstartError exception instance with the descriptive
54 message val. val should be the return value of formatErrorMsg.
55 """
56 Exception.__init__(self)
57 self.value = val
58
59 def __str__ (self):
60 return self.value
61
62class KickstartParseError(KickstartError):
63 """An exception class for errors when processing the input file, such as
64 unknown options, commands, or sections.
65 """
66 def __init__(self, msg):
67 """Create a new KickstartParseError exception instance with the
68 descriptive message val. val should be the return value of
69 formatErrorMsg.
70 """
71 KickstartError.__init__(self, msg)
72
73 def __str__(self):
74 return self.value
75
76class KickstartValueError(KickstartError):
77 """An exception class for errors when processing arguments to commands,
78 such as too many arguments, too few arguments, or missing required
79 arguments.
80 """
81 def __init__(self, msg):
82 """Create a new KickstartValueError exception instance with the
83 descriptive message val. val should be the return value of
84 formatErrorMsg.
85 """
86 KickstartError.__init__(self, msg)
87
88 def __str__ (self):
89 return self.value
90
91class KickstartVersionError(KickstartError):
92 """An exception class for errors related to using an incorrect version of
93 kickstart syntax.
94 """
95 def __init__(self, msg):
96 """Create a new KickstartVersionError exception instance with the
97 descriptive message val. val should be the return value of
98 formatErrorMsg.
99 """
100 KickstartError.__init__(self, msg)
101
102 def __str__ (self):
103 return self.value
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py
new file mode 100644
index 0000000000..d8c8f2b899
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py
@@ -0,0 +1,1307 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007, 2008, 2009, 2010 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.version import *
21from pykickstart.commands import *
22
23# This map is keyed on kickstart syntax version as provided by
24# pykickstart.version. Within each sub-dict is a mapping from command name
25# to the class that handles it. This is an onto mapping - that is, multiple
26# command names can map to the same class. However, the Handler will ensure
27# that only one instance of each class ever exists.
28commandMap = {
29 FC3: {
30 "auth": authconfig.FC3_Authconfig,
31 "authconfig": authconfig.FC3_Authconfig,
32 "autopart": autopart.FC3_AutoPart,
33 "autostep": autostep.FC3_AutoStep,
34 "bootloader": bootloader.FC3_Bootloader,
35 "cdrom": method.FC3_Method,
36 "clearpart": clearpart.FC3_ClearPart,
37 "cmdline": displaymode.FC3_DisplayMode,
38 "device": device.FC3_Device,
39 "deviceprobe": deviceprobe.FC3_DeviceProbe,
40 "driverdisk": driverdisk.FC3_DriverDisk,
41 "firewall": firewall.FC3_Firewall,
42 "firstboot": firstboot.FC3_Firstboot,
43 "graphical": displaymode.FC3_DisplayMode,
44 "halt": reboot.FC3_Reboot,
45 "harddrive": method.FC3_Method,
46 "ignoredisk": ignoredisk.FC3_IgnoreDisk,
47 "install": upgrade.FC3_Upgrade,
48 "interactive": interactive.FC3_Interactive,
49 "keyboard": keyboard.FC3_Keyboard,
50 "lang": lang.FC3_Lang,
51 "langsupport": langsupport.FC3_LangSupport,
52 "lilo": bootloader.FC3_Bootloader,
53 "lilocheck": lilocheck.FC3_LiloCheck,
54 "logvol": logvol.FC3_LogVol,
55 "monitor": monitor.FC3_Monitor,
56 "mouse": mouse.FC3_Mouse,
57 "network": network.FC3_Network,
58 "nfs": method.FC3_Method,
59 "part": partition.FC3_Partition,
60 "partition": partition.FC3_Partition,
61 "poweroff": reboot.FC3_Reboot,
62 "raid": raid.FC3_Raid,
63 "reboot": reboot.FC3_Reboot,
64 "rootpw": rootpw.FC3_RootPw,
65 "selinux": selinux.FC3_SELinux,
66 "shutdown": reboot.FC3_Reboot,
67 "skipx": skipx.FC3_SkipX,
68 "text": displaymode.FC3_DisplayMode,
69 "timezone": timezone.FC3_Timezone,
70 "upgrade": upgrade.FC3_Upgrade,
71 "url": method.FC3_Method,
72 "vnc": vnc.FC3_Vnc,
73 "volgroup": volgroup.FC3_VolGroup,
74 "xconfig": xconfig.FC3_XConfig,
75 "zerombr": zerombr.FC3_ZeroMbr,
76 "zfcp": zfcp.FC3_ZFCP,
77 },
78
79 # based on fc3
80 FC4: {
81 "auth": authconfig.FC3_Authconfig,
82 "authconfig": authconfig.FC3_Authconfig,
83 "autopart": autopart.FC3_AutoPart,
84 "autostep": autostep.FC3_AutoStep,
85 "bootloader": bootloader.FC4_Bootloader,
86 "cdrom": method.FC3_Method,
87 "clearpart": clearpart.FC3_ClearPart,
88 "cmdline": displaymode.FC3_DisplayMode,
89 "device": device.FC3_Device,
90 "deviceprobe": deviceprobe.FC3_DeviceProbe,
91 "driverdisk": driverdisk.FC4_DriverDisk,
92 "firewall": firewall.FC3_Firewall,
93 "firstboot": firstboot.FC3_Firstboot,
94 "graphical": displaymode.FC3_DisplayMode,
95 "halt": reboot.FC3_Reboot,
96 "harddrive": method.FC3_Method,
97 "ignoredisk": ignoredisk.FC3_IgnoreDisk,
98 "install": upgrade.FC3_Upgrade,
99 "interactive": interactive.FC3_Interactive,
100 "keyboard": keyboard.FC3_Keyboard,
101 "lang": lang.FC3_Lang,
102 "langsupport": langsupport.FC3_LangSupport,
103 "logvol": logvol.FC4_LogVol,
104 "mediacheck": mediacheck.FC4_MediaCheck,
105 "monitor": monitor.FC3_Monitor,
106 "mouse": mouse.FC3_Mouse,
107 "network": network.FC4_Network,
108 "nfs": method.FC3_Method,
109 "part": partition.FC4_Partition,
110 "partition": partition.FC4_Partition,
111 "poweroff": reboot.FC3_Reboot,
112 "raid": raid.FC4_Raid,
113 "reboot": reboot.FC3_Reboot,
114 "rootpw": rootpw.FC3_RootPw,
115 "selinux": selinux.FC3_SELinux,
116 "shutdown": reboot.FC3_Reboot,
117 "skipx": skipx.FC3_SkipX,
118 "text": displaymode.FC3_DisplayMode,
119 "timezone": timezone.FC3_Timezone,
120 "upgrade": upgrade.FC3_Upgrade,
121 "url": method.FC3_Method,
122 "vnc": vnc.FC3_Vnc,
123 "volgroup": volgroup.FC3_VolGroup,
124 "xconfig": xconfig.FC3_XConfig,
125 "zerombr": zerombr.FC3_ZeroMbr,
126 "zfcp": zfcp.FC3_ZFCP,
127 },
128
129 # based on fc4
130 FC5: {
131 "auth": authconfig.FC3_Authconfig,
132 "authconfig": authconfig.FC3_Authconfig,
133 "autopart": autopart.FC3_AutoPart,
134 "autostep": autostep.FC3_AutoStep,
135 "bootloader": bootloader.FC4_Bootloader,
136 "cdrom": method.FC3_Method,
137 "clearpart": clearpart.FC3_ClearPart,
138 "cmdline": displaymode.FC3_DisplayMode,
139 "device": device.FC3_Device,
140 "deviceprobe": deviceprobe.FC3_DeviceProbe,
141 "driverdisk": driverdisk.FC4_DriverDisk,
142 "firewall": firewall.FC3_Firewall,
143 "firstboot": firstboot.FC3_Firstboot,
144 "graphical": displaymode.FC3_DisplayMode,
145 "halt": reboot.FC3_Reboot,
146 "harddrive": method.FC3_Method,
147 "ignoredisk": ignoredisk.FC3_IgnoreDisk,
148 "install": upgrade.FC3_Upgrade,
149 "interactive": interactive.FC3_Interactive,
150 "keyboard": keyboard.FC3_Keyboard,
151 "lang": lang.FC3_Lang,
152 "langsupport": langsupport.FC5_LangSupport,
153 "logvol": logvol.FC4_LogVol,
154 "mediacheck": mediacheck.FC4_MediaCheck,
155 "monitor": monitor.FC3_Monitor,
156 "mouse": mouse.FC3_Mouse,
157 "network": network.FC4_Network,
158 "nfs": method.FC3_Method,
159 "part": partition.FC4_Partition,
160 "partition": partition.FC4_Partition,
161 "poweroff": reboot.FC3_Reboot,
162 "raid": raid.FC5_Raid,
163 "reboot": reboot.FC3_Reboot,
164 "rootpw": rootpw.FC3_RootPw,
165 "selinux": selinux.FC3_SELinux,
166 "shutdown": reboot.FC3_Reboot,
167 "skipx": skipx.FC3_SkipX,
168 "text": displaymode.FC3_DisplayMode,
169 "timezone": timezone.FC3_Timezone,
170 "upgrade": upgrade.FC3_Upgrade,
171 "url": method.FC3_Method,
172 "vnc": vnc.FC3_Vnc,
173 "volgroup": volgroup.FC3_VolGroup,
174 "xconfig": xconfig.FC3_XConfig,
175 "zerombr": zerombr.FC3_ZeroMbr,
176 "zfcp": zfcp.FC3_ZFCP,
177 },
178
179 # based on fc5
180 FC6: {
181 "auth": authconfig.FC3_Authconfig,
182 "authconfig": authconfig.FC3_Authconfig,
183 "autopart": autopart.FC3_AutoPart,
184 "autostep": autostep.FC3_AutoStep,
185 "bootloader": bootloader.FC4_Bootloader,
186 "cdrom": method.FC6_Method,
187 "clearpart": clearpart.FC3_ClearPart,
188 "cmdline": displaymode.FC3_DisplayMode,
189 "device": device.FC3_Device,
190 "deviceprobe": deviceprobe.FC3_DeviceProbe,
191 "dmraid": dmraid.FC6_DmRaid,
192 "driverdisk": driverdisk.FC4_DriverDisk,
193 "firewall": firewall.FC3_Firewall,
194 "firstboot": firstboot.FC3_Firstboot,
195 "graphical": displaymode.FC3_DisplayMode,
196 "halt": reboot.FC6_Reboot,
197 "harddrive": method.FC6_Method,
198 "ignoredisk": ignoredisk.FC3_IgnoreDisk,
199 "install": upgrade.FC3_Upgrade,
200 "interactive": interactive.FC3_Interactive,
201 "iscsi": iscsi.FC6_Iscsi,
202 "iscsiname": iscsiname.FC6_IscsiName,
203 "keyboard": keyboard.FC3_Keyboard,
204 "lang": lang.FC3_Lang,
205 "langsupport": langsupport.FC5_LangSupport,
206 "logging": logging.FC6_Logging,
207 "logvol": logvol.FC4_LogVol,
208 "mediacheck": mediacheck.FC4_MediaCheck,
209 "monitor": monitor.FC6_Monitor,
210 "mouse": mouse.FC3_Mouse,
211 "multipath": multipath.FC6_MultiPath,
212 "network": network.FC6_Network,
213 "nfs": method.FC6_Method,
214 "part": partition.FC4_Partition,
215 "partition": partition.FC4_Partition,
216 "poweroff": reboot.FC6_Reboot,
217 "raid": raid.FC5_Raid,
218 "reboot": reboot.FC6_Reboot,
219 "repo": repo.FC6_Repo,
220 "rootpw": rootpw.FC3_RootPw,
221 "selinux": selinux.FC3_SELinux,
222 "services": services.FC6_Services,
223 "shutdown": reboot.FC6_Reboot,
224 "skipx": skipx.FC3_SkipX,
225 "text": displaymode.FC3_DisplayMode,
226 "timezone": timezone.FC6_Timezone,
227 "upgrade": upgrade.FC3_Upgrade,
228 "user": user.FC6_User,
229 "url": method.FC6_Method,
230 "vnc": vnc.FC6_Vnc,
231 "volgroup": volgroup.FC3_VolGroup,
232 "xconfig": xconfig.FC6_XConfig,
233 "zerombr": zerombr.FC3_ZeroMbr,
234 "zfcp": zfcp.FC3_ZFCP,
235 },
236
237 # based on fc6
238 F7: {
239 "auth": authconfig.FC3_Authconfig,
240 "authconfig": authconfig.FC3_Authconfig,
241 "autopart": autopart.FC3_AutoPart,
242 "autostep": autostep.FC3_AutoStep,
243 "bootloader": bootloader.FC4_Bootloader,
244 "cdrom": method.FC6_Method,
245 "clearpart": clearpart.FC3_ClearPart,
246 "cmdline": displaymode.FC3_DisplayMode,
247 "device": device.FC3_Device,
248 "deviceprobe": deviceprobe.FC3_DeviceProbe,
249 "dmraid": dmraid.FC6_DmRaid,
250 "driverdisk": driverdisk.FC4_DriverDisk,
251 "firewall": firewall.FC3_Firewall,
252 "firstboot": firstboot.FC3_Firstboot,
253 "graphical": displaymode.FC3_DisplayMode,
254 "halt": reboot.FC6_Reboot,
255 "harddrive": method.FC6_Method,
256 "ignoredisk": ignoredisk.FC3_IgnoreDisk,
257 "install": upgrade.FC3_Upgrade,
258 "interactive": interactive.FC3_Interactive,
259 "iscsi": iscsi.FC6_Iscsi,
260 "iscsiname": iscsiname.FC6_IscsiName,
261 "keyboard": keyboard.FC3_Keyboard,
262 "lang": lang.FC3_Lang,
263 "logging": logging.FC6_Logging,
264 "logvol": logvol.FC4_LogVol,
265 "mediacheck": mediacheck.FC4_MediaCheck,
266 "monitor": monitor.FC6_Monitor,
267 "multipath": multipath.FC6_MultiPath,
268 "network": network.FC6_Network,
269 "nfs": method.FC6_Method,
270 "part": partition.FC4_Partition,
271 "partition": partition.FC4_Partition,
272 "poweroff": reboot.FC6_Reboot,
273 "raid": raid.F7_Raid,
274 "reboot": reboot.FC6_Reboot,
275 "repo": repo.FC6_Repo,
276 "rootpw": rootpw.FC3_RootPw,
277 "selinux": selinux.FC3_SELinux,
278 "services": services.FC6_Services,
279 "shutdown": reboot.FC6_Reboot,
280 "skipx": skipx.FC3_SkipX,
281 "text": displaymode.FC3_DisplayMode,
282 "timezone": timezone.FC6_Timezone,
283 "updates": updates.F7_Updates,
284 "upgrade": upgrade.FC3_Upgrade,
285 "url": method.FC6_Method,
286 "user": user.FC6_User,
287 "vnc": vnc.FC6_Vnc,
288 "volgroup": volgroup.FC3_VolGroup,
289 "xconfig": xconfig.FC6_XConfig,
290 "zerombr": zerombr.FC3_ZeroMbr,
291 "zfcp": zfcp.FC3_ZFCP,
292 },
293
294 # based on f7
295 F8: {
296 "auth": authconfig.FC3_Authconfig,
297 "authconfig": authconfig.FC3_Authconfig,
298 "autopart": autopart.FC3_AutoPart,
299 "autostep": autostep.FC3_AutoStep,
300 "bootloader": bootloader.F8_Bootloader,
301 "cdrom": method.FC6_Method,
302 "clearpart": clearpart.FC3_ClearPart,
303 "cmdline": displaymode.FC3_DisplayMode,
304 "device": device.F8_Device,
305 "deviceprobe": deviceprobe.FC3_DeviceProbe,
306 "dmraid": dmraid.FC6_DmRaid,
307 "driverdisk": driverdisk.FC4_DriverDisk,
308 "firewall": firewall.FC3_Firewall,
309 "firstboot": firstboot.FC3_Firstboot,
310 "graphical": displaymode.FC3_DisplayMode,
311 "halt": reboot.FC6_Reboot,
312 "harddrive": method.FC6_Method,
313 "ignoredisk": ignoredisk.F8_IgnoreDisk,
314 "install": upgrade.FC3_Upgrade,
315 "interactive": interactive.FC3_Interactive,
316 "iscsi": iscsi.FC6_Iscsi,
317 "iscsiname": iscsiname.FC6_IscsiName,
318 "keyboard": keyboard.FC3_Keyboard,
319 "lang": lang.FC3_Lang,
320 "logging": logging.FC6_Logging,
321 "logvol": logvol.FC4_LogVol,
322 "mediacheck": mediacheck.FC4_MediaCheck,
323 "monitor": monitor.FC6_Monitor,
324 "multipath": multipath.FC6_MultiPath,
325 "network": network.F8_Network,
326 "nfs": method.FC6_Method,
327 "part": partition.FC4_Partition,
328 "partition": partition.FC4_Partition,
329 "poweroff": reboot.FC6_Reboot,
330 "raid": raid.F7_Raid,
331 "reboot": reboot.FC6_Reboot,
332 "repo": repo.F8_Repo,
333 "rootpw": rootpw.F8_RootPw,
334 "selinux": selinux.FC3_SELinux,
335 "services": services.FC6_Services,
336 "shutdown": reboot.FC6_Reboot,
337 "skipx": skipx.FC3_SkipX,
338 "text": displaymode.FC3_DisplayMode,
339 "timezone": timezone.FC6_Timezone,
340 "updates": updates.F7_Updates,
341 "upgrade": upgrade.FC3_Upgrade,
342 "url": method.FC6_Method,
343 "user": user.F8_User,
344 "vnc": vnc.FC6_Vnc,
345 "volgroup": volgroup.FC3_VolGroup,
346 "xconfig": xconfig.FC6_XConfig,
347 "zerombr": zerombr.FC3_ZeroMbr,
348 "zfcp": zfcp.FC3_ZFCP,
349 },
350
351 # based on f8
352 F9: {
353 "auth": authconfig.FC3_Authconfig,
354 "authconfig": authconfig.FC3_Authconfig,
355 "autopart": autopart.F9_AutoPart,
356 "autostep": autostep.FC3_AutoStep,
357 "bootloader": bootloader.F8_Bootloader,
358 "cdrom": method.FC6_Method,
359 "clearpart": clearpart.FC3_ClearPart,
360 "cmdline": displaymode.FC3_DisplayMode,
361 "device": device.F8_Device,
362 "deviceprobe": deviceprobe.FC3_DeviceProbe,
363 "dmraid": dmraid.FC6_DmRaid,
364 "driverdisk": driverdisk.FC4_DriverDisk,
365 "firewall": firewall.F9_Firewall,
366 "firstboot": firstboot.FC3_Firstboot,
367 "graphical": displaymode.FC3_DisplayMode,
368 "halt": reboot.FC6_Reboot,
369 "harddrive": method.FC6_Method,
370 "ignoredisk": ignoredisk.F8_IgnoreDisk,
371 "install": upgrade.FC3_Upgrade,
372 "interactive": interactive.FC3_Interactive,
373 "iscsi": iscsi.FC6_Iscsi,
374 "iscsiname": iscsiname.FC6_IscsiName,
375 "keyboard": keyboard.FC3_Keyboard,
376 "lang": lang.FC3_Lang,
377 "logging": logging.FC6_Logging,
378 "logvol": logvol.F9_LogVol,
379 "mediacheck": mediacheck.FC4_MediaCheck,
380 "monitor": monitor.FC6_Monitor,
381 "multipath": multipath.FC6_MultiPath,
382 "network": network.F9_Network,
383 "nfs": method.FC6_Method,
384 "part": partition.F9_Partition,
385 "partition": partition.F9_Partition,
386 "poweroff": reboot.FC6_Reboot,
387 "raid": raid.F9_Raid,
388 "reboot": reboot.FC6_Reboot,
389 "repo": repo.F8_Repo,
390 "rootpw": rootpw.F8_RootPw,
391 "selinux": selinux.FC3_SELinux,
392 "services": services.FC6_Services,
393 "shutdown": reboot.FC6_Reboot,
394 "skipx": skipx.FC3_SkipX,
395 "text": displaymode.FC3_DisplayMode,
396 "timezone": timezone.FC6_Timezone,
397 "updates": updates.F7_Updates,
398 "upgrade": upgrade.FC3_Upgrade,
399 "url": method.FC6_Method,
400 "user": user.F8_User,
401 "vnc": vnc.F9_Vnc,
402 "volgroup": volgroup.FC3_VolGroup,
403 "xconfig": xconfig.F9_XConfig,
404 "zerombr": zerombr.F9_ZeroMbr,
405 "zfcp": zfcp.FC3_ZFCP,
406 },
407
408 # based on f9
409 F10: {
410 "auth": authconfig.FC3_Authconfig,
411 "authconfig": authconfig.FC3_Authconfig,
412 "autopart": autopart.F9_AutoPart,
413 "autostep": autostep.FC3_AutoStep,
414 "bootloader": bootloader.F8_Bootloader,
415 "cdrom": method.FC6_Method,
416 "clearpart": clearpart.FC3_ClearPart,
417 "cmdline": displaymode.FC3_DisplayMode,
418 "device": device.F8_Device,
419 "deviceprobe": deviceprobe.FC3_DeviceProbe,
420 "dmraid": dmraid.FC6_DmRaid,
421 "driverdisk": driverdisk.FC4_DriverDisk,
422 "firewall": firewall.F10_Firewall,
423 "firstboot": firstboot.FC3_Firstboot,
424 "graphical": displaymode.FC3_DisplayMode,
425 "halt": reboot.FC6_Reboot,
426 "harddrive": method.FC6_Method,
427 "ignoredisk": ignoredisk.F8_IgnoreDisk,
428 "install": upgrade.FC3_Upgrade,
429 "interactive": interactive.FC3_Interactive,
430 "iscsi": iscsi.F10_Iscsi,
431 "iscsiname": iscsiname.FC6_IscsiName,
432 "keyboard": keyboard.FC3_Keyboard,
433 "lang": lang.FC3_Lang,
434 "logging": logging.FC6_Logging,
435 "logvol": logvol.F9_LogVol,
436 "mediacheck": mediacheck.FC4_MediaCheck,
437 "monitor": monitor.F10_Monitor,
438 "multipath": multipath.FC6_MultiPath,
439 "network": network.F9_Network,
440 "nfs": method.FC6_Method,
441 "part": partition.F9_Partition,
442 "partition": partition.F9_Partition,
443 "poweroff": reboot.FC6_Reboot,
444 "raid": raid.F9_Raid,
445 "reboot": reboot.FC6_Reboot,
446 "repo": repo.F8_Repo,
447 "rescue": rescue.F10_Rescue,
448 "rootpw": rootpw.F8_RootPw,
449 "selinux": selinux.FC3_SELinux,
450 "services": services.FC6_Services,
451 "shutdown": reboot.FC6_Reboot,
452 "skipx": skipx.FC3_SkipX,
453 "text": displaymode.FC3_DisplayMode,
454 "timezone": timezone.FC6_Timezone,
455 "updates": updates.F7_Updates,
456 "upgrade": upgrade.FC3_Upgrade,
457 "url": method.FC6_Method,
458 "user": user.F8_User,
459 "vnc": vnc.F9_Vnc,
460 "volgroup": volgroup.FC3_VolGroup,
461 "xconfig": xconfig.F10_XConfig,
462 "zerombr": zerombr.F9_ZeroMbr,
463 "zfcp": zfcp.FC3_ZFCP,
464 },
465
466 # based on f10
467 F11: {
468 "auth": authconfig.FC3_Authconfig,
469 "authconfig": authconfig.FC3_Authconfig,
470 "autopart": autopart.F9_AutoPart,
471 "autostep": autostep.FC3_AutoStep,
472 "bootloader": bootloader.F8_Bootloader,
473 "cdrom": method.FC6_Method,
474 "clearpart": clearpart.FC3_ClearPart,
475 "cmdline": displaymode.FC3_DisplayMode,
476 "device": device.F8_Device,
477 "deviceprobe": deviceprobe.FC3_DeviceProbe,
478 "dmraid": dmraid.FC6_DmRaid,
479 "driverdisk": driverdisk.FC4_DriverDisk,
480 "firewall": firewall.F10_Firewall,
481 "firstboot": firstboot.FC3_Firstboot,
482 "graphical": displaymode.FC3_DisplayMode,
483 "halt": reboot.FC6_Reboot,
484 "harddrive": method.FC6_Method,
485 "ignoredisk": ignoredisk.F8_IgnoreDisk,
486 "install": upgrade.F11_Upgrade,
487 "interactive": interactive.FC3_Interactive,
488 "iscsi": iscsi.F10_Iscsi,
489 "iscsiname": iscsiname.FC6_IscsiName,
490 "keyboard": keyboard.FC3_Keyboard,
491 "lang": lang.FC3_Lang,
492 "logging": logging.FC6_Logging,
493 "logvol": logvol.F9_LogVol,
494 "mediacheck": mediacheck.FC4_MediaCheck,
495 "monitor": monitor.F10_Monitor,
496 "multipath": multipath.FC6_MultiPath,
497 "network": network.F9_Network,
498 "nfs": method.FC6_Method,
499 "part": partition.F11_Partition,
500 "partition": partition.F11_Partition,
501 "poweroff": reboot.FC6_Reboot,
502 "raid": raid.F9_Raid,
503 "reboot": reboot.FC6_Reboot,
504 "repo": repo.F11_Repo,
505 "rescue": rescue.F10_Rescue,
506 "rootpw": rootpw.F8_RootPw,
507 "selinux": selinux.FC3_SELinux,
508 "services": services.FC6_Services,
509 "shutdown": reboot.FC6_Reboot,
510 "skipx": skipx.FC3_SkipX,
511 "text": displaymode.FC3_DisplayMode,
512 "timezone": timezone.FC6_Timezone,
513 "updates": updates.F7_Updates,
514 "upgrade": upgrade.F11_Upgrade,
515 "url": method.FC6_Method,
516 "user": user.F8_User,
517 "vnc": vnc.F9_Vnc,
518 "volgroup": volgroup.FC3_VolGroup,
519 "xconfig": xconfig.F10_XConfig,
520 "zerombr": zerombr.F9_ZeroMbr,
521 "zfcp": zfcp.FC3_ZFCP,
522 },
523
524 # based on f11
525 F12: {
526 "auth": authconfig.FC3_Authconfig,
527 "authconfig": authconfig.FC3_Authconfig,
528 "autopart": autopart.F12_AutoPart,
529 "autostep": autostep.FC3_AutoStep,
530 "bootloader": bootloader.F12_Bootloader,
531 "cdrom": method.FC6_Method,
532 "clearpart": clearpart.FC3_ClearPart,
533 "cmdline": displaymode.FC3_DisplayMode,
534 "device": device.F8_Device,
535 "deviceprobe": deviceprobe.FC3_DeviceProbe,
536 "dmraid": dmraid.FC6_DmRaid,
537 "driverdisk": driverdisk.F12_DriverDisk,
538 "fcoe": fcoe.F12_Fcoe,
539 "firewall": firewall.F10_Firewall,
540 "firstboot": firstboot.FC3_Firstboot,
541 "graphical": displaymode.FC3_DisplayMode,
542 "group": group.F12_Group,
543 "halt": reboot.FC6_Reboot,
544 "harddrive": method.FC6_Method,
545 "ignoredisk": ignoredisk.F8_IgnoreDisk,
546 "install": upgrade.F11_Upgrade,
547 "interactive": interactive.FC3_Interactive,
548 "iscsi": iscsi.F10_Iscsi,
549 "iscsiname": iscsiname.FC6_IscsiName,
550 "keyboard": keyboard.FC3_Keyboard,
551 "lang": lang.FC3_Lang,
552 "logging": logging.FC6_Logging,
553 "logvol": logvol.F12_LogVol,
554 "mediacheck": mediacheck.FC4_MediaCheck,
555 "monitor": monitor.F10_Monitor,
556 "multipath": multipath.FC6_MultiPath,
557 "network": network.F9_Network,
558 "nfs": method.FC6_Method,
559 "part": partition.F12_Partition,
560 "partition": partition.F12_Partition,
561 "poweroff": reboot.FC6_Reboot,
562 "raid": raid.F12_Raid,
563 "reboot": reboot.FC6_Reboot,
564 "repo": repo.F11_Repo,
565 "rescue": rescue.F10_Rescue,
566 "rootpw": rootpw.F8_RootPw,
567 "selinux": selinux.FC3_SELinux,
568 "services": services.FC6_Services,
569 "shutdown": reboot.FC6_Reboot,
570 "skipx": skipx.FC3_SkipX,
571 "text": displaymode.FC3_DisplayMode,
572 "timezone": timezone.FC6_Timezone,
573 "updates": updates.F7_Updates,
574 "upgrade": upgrade.F11_Upgrade,
575 "url": method.FC6_Method,
576 "user": user.F12_User,
577 "vnc": vnc.F9_Vnc,
578 "volgroup": volgroup.FC3_VolGroup,
579 "xconfig": xconfig.F10_XConfig,
580 "zerombr": zerombr.F9_ZeroMbr,
581 "zfcp": zfcp.F12_ZFCP,
582 },
583
584 # based on f12
585 F13: {
586 "auth": authconfig.FC3_Authconfig,
587 "authconfig": authconfig.FC3_Authconfig,
588 "autopart": autopart.F12_AutoPart,
589 "autostep": autostep.FC3_AutoStep,
590 "bootloader": bootloader.F12_Bootloader,
591 "cdrom": method.F13_Method,
592 "clearpart": clearpart.FC3_ClearPart,
593 "cmdline": displaymode.FC3_DisplayMode,
594 "device": device.F8_Device,
595 "deviceprobe": deviceprobe.FC3_DeviceProbe,
596 "dmraid": dmraid.FC6_DmRaid,
597 "driverdisk": driverdisk.F12_DriverDisk,
598 "fcoe": fcoe.F13_Fcoe,
599 "firewall": firewall.F10_Firewall,
600 "firstboot": firstboot.FC3_Firstboot,
601 "graphical": displaymode.FC3_DisplayMode,
602 "group": group.F12_Group,
603 "halt": reboot.FC6_Reboot,
604 "harddrive": method.F13_Method,
605 "ignoredisk": ignoredisk.F8_IgnoreDisk,
606 "install": upgrade.F11_Upgrade,
607 "interactive": interactive.FC3_Interactive,
608 "iscsi": iscsi.F10_Iscsi,
609 "iscsiname": iscsiname.FC6_IscsiName,
610 "keyboard": keyboard.FC3_Keyboard,
611 "lang": lang.FC3_Lang,
612 "logging": logging.FC6_Logging,
613 "logvol": logvol.F12_LogVol,
614 "mediacheck": mediacheck.FC4_MediaCheck,
615 "monitor": monitor.F10_Monitor,
616 "multipath": multipath.FC6_MultiPath,
617 "network": network.F9_Network,
618 "nfs": method.F13_Method,
619 "part": partition.F12_Partition,
620 "partition": partition.F12_Partition,
621 "poweroff": reboot.FC6_Reboot,
622 "raid": raid.F13_Raid,
623 "reboot": reboot.FC6_Reboot,
624 "repo": repo.F13_Repo,
625 "rescue": rescue.F10_Rescue,
626 "rootpw": rootpw.F8_RootPw,
627 "selinux": selinux.FC3_SELinux,
628 "services": services.FC6_Services,
629 "shutdown": reboot.FC6_Reboot,
630 "skipx": skipx.FC3_SkipX,
631 "sshpw": sshpw.F13_SshPw,
632 "text": displaymode.FC3_DisplayMode,
633 "timezone": timezone.FC6_Timezone,
634 "updates": updates.F7_Updates,
635 "upgrade": upgrade.F11_Upgrade,
636 "url": method.F13_Method,
637 "user": user.F12_User,
638 "vnc": vnc.F9_Vnc,
639 "volgroup": volgroup.FC3_VolGroup,
640 "xconfig": xconfig.F10_XConfig,
641 "zerombr": zerombr.F9_ZeroMbr,
642 "zfcp": zfcp.F12_ZFCP,
643 },
644
645 # based on f13
646 F14: {
647 "auth": authconfig.FC3_Authconfig,
648 "authconfig": authconfig.FC3_Authconfig,
649 "autopart": autopart.F12_AutoPart,
650 "autostep": autostep.FC3_AutoStep,
651 "bootloader": bootloader.F14_Bootloader,
652 "cdrom": method.F14_Method,
653 "clearpart": clearpart.FC3_ClearPart,
654 "cmdline": displaymode.FC3_DisplayMode,
655 "device": device.F8_Device,
656 "deviceprobe": deviceprobe.FC3_DeviceProbe,
657 "dmraid": dmraid.FC6_DmRaid,
658 "driverdisk": driverdisk.F14_DriverDisk,
659 "fcoe": fcoe.F13_Fcoe,
660 "firewall": firewall.F14_Firewall,
661 "firstboot": firstboot.FC3_Firstboot,
662 "graphical": displaymode.FC3_DisplayMode,
663 "group": group.F12_Group,
664 "halt": reboot.FC6_Reboot,
665 "harddrive": method.F14_Method,
666 "ignoredisk": ignoredisk.F14_IgnoreDisk,
667 "install": upgrade.F11_Upgrade,
668 "interactive": interactive.F14_Interactive,
669 "iscsi": iscsi.F10_Iscsi,
670 "iscsiname": iscsiname.FC6_IscsiName,
671 "keyboard": keyboard.FC3_Keyboard,
672 "lang": lang.FC3_Lang,
673 "logging": logging.FC6_Logging,
674 "logvol": logvol.F14_LogVol,
675 "mediacheck": mediacheck.FC4_MediaCheck,
676 "monitor": monitor.F10_Monitor,
677 "multipath": multipath.FC6_MultiPath,
678 "network": network.F9_Network,
679 "nfs": method.F14_Method,
680 "part": partition.F14_Partition,
681 "partition": partition.F14_Partition,
682 "poweroff": reboot.FC6_Reboot,
683 "raid": raid.F14_Raid,
684 "reboot": reboot.FC6_Reboot,
685 "repo": repo.F14_Repo,
686 "rescue": rescue.F10_Rescue,
687 "rootpw": rootpw.F8_RootPw,
688 "selinux": selinux.FC3_SELinux,
689 "services": services.FC6_Services,
690 "shutdown": reboot.FC6_Reboot,
691 "skipx": skipx.FC3_SkipX,
692 "sshpw": sshpw.F13_SshPw,
693 "text": displaymode.FC3_DisplayMode,
694 "timezone": timezone.FC6_Timezone,
695 "updates": updates.F7_Updates,
696 "upgrade": upgrade.F11_Upgrade,
697 "url": method.F14_Method,
698 "user": user.F12_User,
699 "vnc": vnc.F9_Vnc,
700 "volgroup": volgroup.FC3_VolGroup,
701 "xconfig": xconfig.F14_XConfig,
702 "zerombr": zerombr.F9_ZeroMbr,
703 "zfcp": zfcp.F14_ZFCP,
704 },
705
706 # based on f14
707 F15: {
708 "auth": authconfig.FC3_Authconfig,
709 "authconfig": authconfig.FC3_Authconfig,
710 "autopart": autopart.F12_AutoPart,
711 "autostep": autostep.FC3_AutoStep,
712 "bootloader": bootloader.F15_Bootloader,
713 "cdrom": method.F14_Method,
714 "clearpart": clearpart.FC3_ClearPart,
715 "cmdline": displaymode.FC3_DisplayMode,
716 "device": device.F8_Device,
717 "deviceprobe": deviceprobe.FC3_DeviceProbe,
718 "dmraid": dmraid.FC6_DmRaid,
719 "driverdisk": driverdisk.F14_DriverDisk,
720 "fcoe": fcoe.F13_Fcoe,
721 "firewall": firewall.F14_Firewall,
722 "firstboot": firstboot.FC3_Firstboot,
723 "graphical": displaymode.FC3_DisplayMode,
724 "group": group.F12_Group,
725 "halt": reboot.FC6_Reboot,
726 "harddrive": method.F14_Method,
727 "ignoredisk": ignoredisk.F14_IgnoreDisk,
728 "install": upgrade.F11_Upgrade,
729 "iscsi": iscsi.F10_Iscsi,
730 "iscsiname": iscsiname.FC6_IscsiName,
731 "keyboard": keyboard.FC3_Keyboard,
732 "lang": lang.FC3_Lang,
733 "logging": logging.FC6_Logging,
734 "logvol": logvol.F15_LogVol,
735 "mediacheck": mediacheck.FC4_MediaCheck,
736 "monitor": monitor.F10_Monitor,
737 "multipath": multipath.FC6_MultiPath,
738 "network": network.F9_Network,
739 "nfs": method.F14_Method,
740 "part": partition.F14_Partition,
741 "partition": partition.F14_Partition,
742 "poweroff": reboot.FC6_Reboot,
743 "raid": raid.F15_Raid,
744 "reboot": reboot.FC6_Reboot,
745 "repo": repo.F15_Repo,
746 "rescue": rescue.F10_Rescue,
747 "rootpw": rootpw.F8_RootPw,
748 "selinux": selinux.FC3_SELinux,
749 "services": services.FC6_Services,
750 "shutdown": reboot.FC6_Reboot,
751 "skipx": skipx.FC3_SkipX,
752 "sshpw": sshpw.F13_SshPw,
753 "text": displaymode.FC3_DisplayMode,
754 "timezone": timezone.FC6_Timezone,
755 "updates": updates.F7_Updates,
756 "upgrade": upgrade.F11_Upgrade,
757 "url": method.F14_Method,
758 "user": user.F12_User,
759 "vnc": vnc.F9_Vnc,
760 "volgroup": volgroup.FC3_VolGroup,
761 "xconfig": xconfig.F14_XConfig,
762 "zerombr": zerombr.F9_ZeroMbr,
763 "zfcp": zfcp.F14_ZFCP,
764 },
765
766 # based on f15
767 F16: {
768 "auth": authconfig.FC3_Authconfig,
769 "authconfig": authconfig.FC3_Authconfig,
770 "autopart": autopart.F12_AutoPart,
771 "autostep": autostep.FC3_AutoStep,
772 "bootloader": bootloader.F15_Bootloader,
773 "cdrom": method.F14_Method,
774 "clearpart": clearpart.FC3_ClearPart,
775 "cmdline": displaymode.FC3_DisplayMode,
776 "device": device.F8_Device,
777 "deviceprobe": deviceprobe.FC3_DeviceProbe,
778 "dmraid": dmraid.FC6_DmRaid,
779 "driverdisk": driverdisk.F14_DriverDisk,
780 "fcoe": fcoe.F13_Fcoe,
781 "firewall": firewall.F14_Firewall,
782 "firstboot": firstboot.FC3_Firstboot,
783 "graphical": displaymode.FC3_DisplayMode,
784 "group": group.F12_Group,
785 "halt": reboot.FC6_Reboot,
786 "harddrive": method.F14_Method,
787 "ignoredisk": ignoredisk.F14_IgnoreDisk,
788 "install": upgrade.F11_Upgrade,
789 "iscsi": iscsi.F10_Iscsi,
790 "iscsiname": iscsiname.FC6_IscsiName,
791 "keyboard": keyboard.FC3_Keyboard,
792 "lang": lang.FC3_Lang,
793 "logging": logging.FC6_Logging,
794 "logvol": logvol.F15_LogVol,
795 "mediacheck": mediacheck.FC4_MediaCheck,
796 "monitor": monitor.F10_Monitor,
797 "multipath": multipath.FC6_MultiPath,
798 "network": network.F16_Network,
799 "nfs": method.F14_Method,
800 "part": partition.F14_Partition,
801 "partition": partition.F14_Partition,
802 "poweroff": reboot.FC6_Reboot,
803 "raid": raid.F15_Raid,
804 "reboot": reboot.FC6_Reboot,
805 "repo": repo.F15_Repo,
806 "rescue": rescue.F10_Rescue,
807 "rootpw": rootpw.F8_RootPw,
808 "selinux": selinux.FC3_SELinux,
809 "services": services.FC6_Services,
810 "shutdown": reboot.FC6_Reboot,
811 "skipx": skipx.FC3_SkipX,
812 "sshpw": sshpw.F13_SshPw,
813 "text": displaymode.FC3_DisplayMode,
814 "timezone": timezone.FC6_Timezone,
815 "updates": updates.F7_Updates,
816 "upgrade": upgrade.F11_Upgrade,
817 "url": method.F14_Method,
818 "user": user.F12_User,
819 "vnc": vnc.F9_Vnc,
820 "volgroup": volgroup.FC3_VolGroup,
821 "xconfig": xconfig.F14_XConfig,
822 "zerombr": zerombr.F9_ZeroMbr,
823 "zfcp": zfcp.F14_ZFCP,
824 },
825
826 # based on fc1
827 RHEL3: {
828 "auth": authconfig.FC3_Authconfig,
829 "authconfig": authconfig.FC3_Authconfig,
830 "autopart": autopart.FC3_AutoPart,
831 "autostep": autostep.FC3_AutoStep,
832 "bootloader": bootloader.FC3_Bootloader,
833 "cdrom": method.FC3_Method,
834 "clearpart": clearpart.FC3_ClearPart,
835 "cmdline": displaymode.FC3_DisplayMode,
836 "device": device.FC3_Device,
837 "deviceprobe": deviceprobe.FC3_DeviceProbe,
838 "driverdisk": driverdisk.FC3_DriverDisk,
839 "firewall": firewall.FC3_Firewall,
840 "firstboot": firstboot.FC3_Firstboot,
841 "graphical": displaymode.FC3_DisplayMode,
842 "halt": reboot.FC3_Reboot,
843 "harddrive": method.FC3_Method,
844 "ignoredisk": ignoredisk.FC3_IgnoreDisk,
845 "install": upgrade.FC3_Upgrade,
846 "interactive": interactive.FC3_Interactive,
847 "keyboard": keyboard.FC3_Keyboard,
848 "lang": lang.FC3_Lang,
849 "langsupport": langsupport.FC3_LangSupport,
850 "lilo": bootloader.FC3_Bootloader,
851 "lilocheck": lilocheck.FC3_LiloCheck,
852 "logvol": logvol.FC3_LogVol,
853 "monitor": monitor.FC3_Monitor,
854 "mouse": mouse.RHEL3_Mouse,
855 "network": network.FC3_Network,
856 "nfs": method.FC3_Method,
857 "part": partition.FC3_Partition,
858 "partition": partition.FC3_Partition,
859 "poweroff": reboot.FC3_Reboot,
860 "raid": raid.FC3_Raid,
861 "reboot": reboot.FC3_Reboot,
862 "rootpw": rootpw.FC3_RootPw,
863 "shutdown": reboot.FC3_Reboot,
864 "skipx": skipx.FC3_SkipX,
865 "text": displaymode.FC3_DisplayMode,
866 "timezone": timezone.FC3_Timezone,
867 "upgrade": upgrade.FC3_Upgrade,
868 "url": method.FC3_Method,
869 "vnc": vnc.FC3_Vnc,
870 "volgroup": volgroup.FC3_VolGroup,
871 "xconfig": xconfig.FC3_XConfig,
872 "zerombr": zerombr.FC3_ZeroMbr,
873 },
874
875 # based on fc3
876 RHEL4: {
877 "auth": authconfig.FC3_Authconfig,
878 "authconfig": authconfig.FC3_Authconfig,
879 "autopart": autopart.FC3_AutoPart,
880 "autostep": autostep.FC3_AutoStep,
881 "bootloader": bootloader.FC3_Bootloader,
882 "cdrom": method.FC3_Method,
883 "clearpart": clearpart.FC3_ClearPart,
884 "cmdline": displaymode.FC3_DisplayMode,
885 "device": device.FC3_Device,
886 "deviceprobe": deviceprobe.FC3_DeviceProbe,
887 "driverdisk": driverdisk.FC4_DriverDisk,
888 "firewall": firewall.FC3_Firewall,
889 "firstboot": firstboot.FC3_Firstboot,
890 "graphical": displaymode.FC3_DisplayMode,
891 "halt": reboot.FC3_Reboot,
892 "harddrive": method.FC3_Method,
893 "ignoredisk": ignoredisk.F8_IgnoreDisk,
894 "install": upgrade.FC3_Upgrade,
895 "interactive": interactive.FC3_Interactive,
896 "keyboard": keyboard.FC3_Keyboard,
897 "lang": lang.FC3_Lang,
898 "langsupport": langsupport.FC3_LangSupport,
899 "lilo": bootloader.FC3_Bootloader,
900 "lilocheck": lilocheck.FC3_LiloCheck,
901 "logvol": logvol.FC3_LogVol,
902 "monitor": monitor.FC3_Monitor,
903 "mouse": mouse.FC3_Mouse,
904 "network": network.RHEL4_Network,
905 "nfs": method.FC3_Method,
906 "part": partition.FC3_Partition,
907 "partition": partition.FC3_Partition,
908 "poweroff": reboot.FC3_Reboot,
909 "raid": raid.FC3_Raid,
910 "reboot": reboot.FC3_Reboot,
911 "rootpw": rootpw.FC3_RootPw,
912 "selinux": selinux.FC3_SELinux,
913 "shutdown": reboot.FC3_Reboot,
914 "skipx": skipx.FC3_SkipX,
915 "text": displaymode.FC3_DisplayMode,
916 "timezone": timezone.FC3_Timezone,
917 "upgrade": upgrade.FC3_Upgrade,
918 "url": method.FC3_Method,
919 "vnc": vnc.FC3_Vnc,
920 "volgroup": volgroup.FC3_VolGroup,
921 "xconfig": xconfig.FC3_XConfig,
922 "zerombr": zerombr.FC3_ZeroMbr,
923 "zfcp": zfcp.FC3_ZFCP,
924 },
925
926 # based on fc6
927 RHEL5: {
928 "auth": authconfig.FC3_Authconfig,
929 "authconfig": authconfig.FC3_Authconfig,
930 "autopart": autopart.F9_AutoPart,
931 "autostep": autostep.FC3_AutoStep,
932 "bootloader": bootloader.RHEL5_Bootloader,
933 "cdrom": method.FC6_Method,
934 "clearpart": clearpart.FC3_ClearPart,
935 "cmdline": displaymode.FC3_DisplayMode,
936 "device": device.FC3_Device,
937 "deviceprobe": deviceprobe.FC3_DeviceProbe,
938 "dmraid": dmraid.FC6_DmRaid,
939 "driverdisk": driverdisk.F12_DriverDisk,
940 "firewall": firewall.FC3_Firewall,
941 "firstboot": firstboot.FC3_Firstboot,
942 "graphical": displaymode.FC3_DisplayMode,
943 "halt": reboot.FC6_Reboot,
944 "harddrive": method.FC6_Method,
945 "ignoredisk": ignoredisk.F8_IgnoreDisk,
946 "install": upgrade.FC3_Upgrade,
947 "interactive": interactive.FC3_Interactive,
948 "iscsi": iscsi.FC6_Iscsi,
949 "iscsiname": iscsiname.FC6_IscsiName,
950 "key": key.RHEL5_Key,
951 "keyboard": keyboard.FC3_Keyboard,
952 "lang": lang.FC3_Lang,
953 "langsupport": langsupport.FC5_LangSupport,
954 "logging": logging.FC6_Logging,
955 "logvol": logvol.RHEL5_LogVol,
956 "mediacheck": mediacheck.FC4_MediaCheck,
957 "monitor": monitor.FC6_Monitor,
958 "mouse": mouse.FC3_Mouse,
959 "multipath": multipath.FC6_MultiPath,
960 "network": network.RHEL5_Network,
961 "nfs": method.FC6_Method,
962 "part": partition.RHEL5_Partition,
963 "partition": partition.RHEL5_Partition,
964 "poweroff": reboot.FC6_Reboot,
965 "raid": raid.RHEL5_Raid,
966 "reboot": reboot.FC6_Reboot,
967 "repo": repo.FC6_Repo,
968 "rootpw": rootpw.FC3_RootPw,
969 "services": services.FC6_Services,
970 "selinux": selinux.FC3_SELinux,
971 "shutdown": reboot.FC6_Reboot,
972 "skipx": skipx.FC3_SkipX,
973 "text": displaymode.FC3_DisplayMode,
974 "timezone": timezone.FC6_Timezone,
975 "upgrade": upgrade.FC3_Upgrade,
976 "user": user.FC6_User,
977 "url": method.FC6_Method,
978 "vnc": vnc.FC6_Vnc,
979 "volgroup": volgroup.FC3_VolGroup,
980 "xconfig": xconfig.FC6_XConfig,
981 "zerombr": zerombr.FC3_ZeroMbr,
982 "zfcp": zfcp.FC3_ZFCP,
983 },
984
985 # based on f13ish
986 RHEL6: {
987 "auth": authconfig.FC3_Authconfig,
988 "authconfig": authconfig.FC3_Authconfig,
989 "autopart": autopart.F12_AutoPart,
990 "autostep": autostep.FC3_AutoStep,
991 "bootloader": bootloader.RHEL6_Bootloader,
992 "cdrom": method.RHEL6_Method,
993 "clearpart": clearpart.FC3_ClearPart,
994 "cmdline": displaymode.FC3_DisplayMode,
995 "device": device.F8_Device,
996 "deviceprobe": deviceprobe.FC3_DeviceProbe,
997 "dmraid": dmraid.FC6_DmRaid,
998 "driverdisk": driverdisk.F12_DriverDisk,
999 "fcoe": fcoe.F13_Fcoe,
1000 "firewall": firewall.F10_Firewall,
1001 "firstboot": firstboot.FC3_Firstboot,
1002 "graphical": displaymode.FC3_DisplayMode,
1003 "group": group.F12_Group,
1004 "halt": reboot.FC6_Reboot,
1005 "harddrive": method.RHEL6_Method,
1006 "ignoredisk": ignoredisk.RHEL6_IgnoreDisk,
1007 "install": upgrade.F11_Upgrade,
1008 "interactive": interactive.FC3_Interactive,
1009 "iscsi": iscsi.F10_Iscsi,
1010 "iscsiname": iscsiname.FC6_IscsiName,
1011 "keyboard": keyboard.FC3_Keyboard,
1012 "lang": lang.FC3_Lang,
1013 "logging": logging.FC6_Logging,
1014 "logvol": logvol.F12_LogVol,
1015 "mediacheck": mediacheck.FC4_MediaCheck,
1016 "monitor": monitor.F10_Monitor,
1017 "multipath": multipath.FC6_MultiPath,
1018 "network": network.RHEL6_Network,
1019 "nfs": method.RHEL6_Method,
1020 "part": partition.F12_Partition,
1021 "partition": partition.F12_Partition,
1022 "poweroff": reboot.FC6_Reboot,
1023 "raid": raid.F13_Raid,
1024 "reboot": reboot.FC6_Reboot,
1025 "repo": repo.RHEL6_Repo,
1026 "rescue": rescue.F10_Rescue,
1027 "rootpw": rootpw.F8_RootPw,
1028 "selinux": selinux.FC3_SELinux,
1029 "services": services.FC6_Services,
1030 "shutdown": reboot.FC6_Reboot,
1031 "skipx": skipx.FC3_SkipX,
1032 "sshpw": sshpw.F13_SshPw,
1033 "text": displaymode.FC3_DisplayMode,
1034 "timezone": timezone.FC6_Timezone,
1035 "updates": updates.F7_Updates,
1036 "upgrade": upgrade.F11_Upgrade,
1037 "url": method.RHEL6_Method,
1038 "user": user.F12_User,
1039 "vnc": vnc.F9_Vnc,
1040 "volgroup": volgroup.FC3_VolGroup,
1041 "xconfig": xconfig.F10_XConfig,
1042 "zerombr": zerombr.F9_ZeroMbr,
1043 "zfcp": zfcp.F12_ZFCP,
1044 }
1045}
1046
1047# This map is keyed on kickstart syntax version as provided by
1048# pykickstart.version. Within each sub-dict is a mapping from a data object
1049# name to the class that provides it. This is a bijective mapping - that is,
1050# each name maps to exactly one data class and all data classes have a name.
1051# More than one instance of each class is allowed to exist, however.
1052dataMap = {
1053 FC3: {
1054 "DriverDiskData": driverdisk.FC3_DriverDiskData,
1055 "LogVolData": logvol.FC3_LogVolData,
1056 "NetworkData": network.FC3_NetworkData,
1057 "PartData": partition.FC3_PartData,
1058 "RaidData": raid.FC3_RaidData,
1059 "VolGroupData": volgroup.FC3_VolGroupData,
1060 "ZFCPData": zfcp.FC3_ZFCPData,
1061 },
1062 FC4: {
1063 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1064 "LogVolData": logvol.FC4_LogVolData,
1065 "NetworkData": network.FC4_NetworkData,
1066 "PartData": partition.FC4_PartData,
1067 "RaidData": raid.FC4_RaidData,
1068 "VolGroupData": volgroup.FC3_VolGroupData,
1069 "ZFCPData": zfcp.FC3_ZFCPData,
1070 },
1071 FC5: {
1072 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1073 "LogVolData": logvol.FC4_LogVolData,
1074 "NetworkData": network.FC4_NetworkData,
1075 "PartData": partition.FC4_PartData,
1076 "RaidData": raid.FC5_RaidData,
1077 "VolGroupData": volgroup.FC3_VolGroupData,
1078 "ZFCPData": zfcp.FC3_ZFCPData,
1079 },
1080 FC6: {
1081 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1082 "DmRaidData": dmraid.FC6_DmRaidData,
1083 "IscsiData": iscsi.FC6_IscsiData,
1084 "LogVolData": logvol.FC4_LogVolData,
1085 "MultiPathData": multipath.FC6_MultiPathData,
1086 "NetworkData": network.FC6_NetworkData,
1087 "PartData": partition.FC4_PartData,
1088 "RaidData": raid.FC5_RaidData,
1089 "RepoData": repo.FC6_RepoData,
1090 "UserData": user.FC6_UserData,
1091 "VolGroupData": volgroup.FC3_VolGroupData,
1092 "ZFCPData": zfcp.FC3_ZFCPData,
1093 },
1094 F7: {
1095 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1096 "DmRaidData": dmraid.FC6_DmRaidData,
1097 "IscsiData": iscsi.FC6_IscsiData,
1098 "LogVolData": logvol.FC4_LogVolData,
1099 "MultiPathData": multipath.FC6_MultiPathData,
1100 "NetworkData": network.FC6_NetworkData,
1101 "PartData": partition.FC4_PartData,
1102 "RaidData": raid.F7_RaidData,
1103 "RepoData": repo.FC6_RepoData,
1104 "UserData": user.FC6_UserData,
1105 "VolGroupData": volgroup.FC3_VolGroupData,
1106 "ZFCPData": zfcp.FC3_ZFCPData,
1107 },
1108 F8: {
1109 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1110 "DeviceData": device.F8_DeviceData,
1111 "DmRaidData": dmraid.FC6_DmRaidData,
1112 "IscsiData": iscsi.FC6_IscsiData,
1113 "LogVolData": logvol.FC4_LogVolData,
1114 "MultiPathData": multipath.FC6_MultiPathData,
1115 "NetworkData": network.F8_NetworkData,
1116 "PartData": partition.FC4_PartData,
1117 "RaidData": raid.F7_RaidData,
1118 "RepoData": repo.F8_RepoData,
1119 "UserData": user.F8_UserData,
1120 "VolGroupData": volgroup.FC3_VolGroupData,
1121 "ZFCPData": zfcp.FC3_ZFCPData,
1122 },
1123 F9: {
1124 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1125 "DeviceData": device.F8_DeviceData,
1126 "DmRaidData": dmraid.FC6_DmRaidData,
1127 "IscsiData": iscsi.FC6_IscsiData,
1128 "LogVolData": logvol.F9_LogVolData,
1129 "MultiPathData": multipath.FC6_MultiPathData,
1130 "NetworkData": network.F8_NetworkData,
1131 "PartData": partition.F9_PartData,
1132 "RaidData": raid.F9_RaidData,
1133 "RepoData": repo.F8_RepoData,
1134 "UserData": user.F8_UserData,
1135 "VolGroupData": volgroup.FC3_VolGroupData,
1136 "ZFCPData": zfcp.FC3_ZFCPData,
1137 },
1138 F10: {
1139 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1140 "DeviceData": device.F8_DeviceData,
1141 "DmRaidData": dmraid.FC6_DmRaidData,
1142 "IscsiData": iscsi.F10_IscsiData,
1143 "LogVolData": logvol.F9_LogVolData,
1144 "MultiPathData": multipath.FC6_MultiPathData,
1145 "NetworkData": network.F8_NetworkData,
1146 "PartData": partition.F9_PartData,
1147 "RaidData": raid.F9_RaidData,
1148 "RepoData": repo.F8_RepoData,
1149 "UserData": user.F8_UserData,
1150 "VolGroupData": volgroup.FC3_VolGroupData,
1151 "ZFCPData": zfcp.FC3_ZFCPData,
1152 },
1153 F11: {
1154 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1155 "DeviceData": device.F8_DeviceData,
1156 "DmRaidData": dmraid.FC6_DmRaidData,
1157 "IscsiData": iscsi.F10_IscsiData,
1158 "LogVolData": logvol.F9_LogVolData,
1159 "MultiPathData": multipath.FC6_MultiPathData,
1160 "NetworkData": network.F8_NetworkData,
1161 "PartData": partition.F11_PartData,
1162 "RaidData": raid.F9_RaidData,
1163 "RepoData": repo.F11_RepoData,
1164 "UserData": user.F8_UserData,
1165 "VolGroupData": volgroup.FC3_VolGroupData,
1166 "ZFCPData": zfcp.FC3_ZFCPData,
1167 },
1168 F12: {
1169 "DriverDiskData": driverdisk.F12_DriverDiskData,
1170 "DeviceData": device.F8_DeviceData,
1171 "DmRaidData": dmraid.FC6_DmRaidData,
1172 "FcoeData": fcoe.F12_FcoeData,
1173 "GroupData": group.F12_GroupData,
1174 "IscsiData": iscsi.F10_IscsiData,
1175 "LogVolData": logvol.F12_LogVolData,
1176 "MultiPathData": multipath.FC6_MultiPathData,
1177 "NetworkData": network.F8_NetworkData,
1178 "PartData": partition.F12_PartData,
1179 "RaidData": raid.F12_RaidData,
1180 "RepoData": repo.F11_RepoData,
1181 "UserData": user.F12_UserData,
1182 "VolGroupData": volgroup.FC3_VolGroupData,
1183 "ZFCPData": zfcp.F12_ZFCPData,
1184 },
1185 F13: {
1186 "DriverDiskData": driverdisk.F12_DriverDiskData,
1187 "DeviceData": device.F8_DeviceData,
1188 "DmRaidData": dmraid.FC6_DmRaidData,
1189 "FcoeData": fcoe.F13_FcoeData,
1190 "GroupData": group.F12_GroupData,
1191 "IscsiData": iscsi.F10_IscsiData,
1192 "LogVolData": logvol.F12_LogVolData,
1193 "MultiPathData": multipath.FC6_MultiPathData,
1194 "NetworkData": network.F8_NetworkData,
1195 "PartData": partition.F12_PartData,
1196 "RaidData": raid.F13_RaidData,
1197 "RepoData": repo.F13_RepoData,
1198 "SshPwData": sshpw.F13_SshPwData,
1199 "UserData": user.F12_UserData,
1200 "VolGroupData": volgroup.FC3_VolGroupData,
1201 "ZFCPData": zfcp.F12_ZFCPData,
1202 },
1203 F14: {
1204 "DriverDiskData": driverdisk.F14_DriverDiskData,
1205 "DeviceData": device.F8_DeviceData,
1206 "DmRaidData": dmraid.FC6_DmRaidData,
1207 "FcoeData": fcoe.F13_FcoeData,
1208 "GroupData": group.F12_GroupData,
1209 "IscsiData": iscsi.F10_IscsiData,
1210 "LogVolData": logvol.F14_LogVolData,
1211 "MultiPathData": multipath.FC6_MultiPathData,
1212 "NetworkData": network.F8_NetworkData,
1213 "PartData": partition.F14_PartData,
1214 "RaidData": raid.F14_RaidData,
1215 "RepoData": repo.F14_RepoData,
1216 "SshPwData": sshpw.F13_SshPwData,
1217 "UserData": user.F12_UserData,
1218 "VolGroupData": volgroup.FC3_VolGroupData,
1219 "ZFCPData": zfcp.F14_ZFCPData,
1220 },
1221 F15: {
1222 "DriverDiskData": driverdisk.F14_DriverDiskData,
1223 "DeviceData": device.F8_DeviceData,
1224 "DmRaidData": dmraid.FC6_DmRaidData,
1225 "FcoeData": fcoe.F13_FcoeData,
1226 "GroupData": group.F12_GroupData,
1227 "IscsiData": iscsi.F10_IscsiData,
1228 "LogVolData": logvol.F15_LogVolData,
1229 "MultiPathData": multipath.FC6_MultiPathData,
1230 "NetworkData": network.F8_NetworkData,
1231 "PartData": partition.F14_PartData,
1232 "RaidData": raid.F15_RaidData,
1233 "RepoData": repo.F15_RepoData,
1234 "SshPwData": sshpw.F13_SshPwData,
1235 "UserData": user.F12_UserData,
1236 "VolGroupData": volgroup.FC3_VolGroupData,
1237 "ZFCPData": zfcp.F14_ZFCPData,
1238 },
1239 F16: {
1240 "DriverDiskData": driverdisk.F14_DriverDiskData,
1241 "DeviceData": device.F8_DeviceData,
1242 "DmRaidData": dmraid.FC6_DmRaidData,
1243 "FcoeData": fcoe.F13_FcoeData,
1244 "GroupData": group.F12_GroupData,
1245 "IscsiData": iscsi.F10_IscsiData,
1246 "LogVolData": logvol.F15_LogVolData,
1247 "MultiPathData": multipath.FC6_MultiPathData,
1248 "NetworkData": network.F16_NetworkData,
1249 "PartData": partition.F14_PartData,
1250 "RaidData": raid.F15_RaidData,
1251 "RepoData": repo.F15_RepoData,
1252 "SshPwData": sshpw.F13_SshPwData,
1253 "UserData": user.F12_UserData,
1254 "VolGroupData": volgroup.FC3_VolGroupData,
1255 "ZFCPData": zfcp.F14_ZFCPData,
1256 },
1257 RHEL3: {
1258 "DriverDiskData": driverdisk.FC3_DriverDiskData,
1259 "LogVolData": logvol.FC3_LogVolData,
1260 "NetworkData": network.RHEL4_NetworkData,
1261 "PartData": partition.FC3_PartData,
1262 "RaidData": raid.FC3_RaidData,
1263 "VolGroupData": volgroup.FC3_VolGroupData,
1264 "ZFCPData": zfcp.FC3_ZFCPData,
1265 },
1266 RHEL4: {
1267 "DriverDiskData": driverdisk.FC4_DriverDiskData,
1268 "LogVolData": logvol.FC3_LogVolData,
1269 "NetworkData": network.RHEL4_NetworkData,
1270 "PartData": partition.FC3_PartData,
1271 "RaidData": raid.FC3_RaidData,
1272 "VolGroupData": volgroup.FC3_VolGroupData,
1273 "ZFCPData": zfcp.FC3_ZFCPData,
1274 },
1275 RHEL5: {
1276 "DriverDiskData": driverdisk.F12_DriverDiskData,
1277 "DmRaidData": dmraid.FC6_DmRaidData,
1278 "IscsiData": iscsi.FC6_IscsiData,
1279 "LogVolData": logvol.RHEL5_LogVolData,
1280 "MultiPathData": multipath.FC6_MultiPathData,
1281 "NetworkData": network.FC6_NetworkData,
1282 "PartData": partition.RHEL5_PartData,
1283 "RaidData": raid.RHEL5_RaidData,
1284 "RepoData": repo.FC6_RepoData,
1285 "UserData": user.FC6_UserData,
1286 "VolGroupData": volgroup.FC3_VolGroupData,
1287 "ZFCPData": zfcp.FC3_ZFCPData,
1288 },
1289 RHEL6: {
1290 "DriverDiskData": driverdisk.F12_DriverDiskData,
1291 "DeviceData": device.F8_DeviceData,
1292 "DmRaidData": dmraid.FC6_DmRaidData,
1293 "FcoeData": fcoe.F13_FcoeData,
1294 "GroupData": group.F12_GroupData,
1295 "IscsiData": iscsi.F10_IscsiData,
1296 "LogVolData": logvol.F12_LogVolData,
1297 "MultiPathData": multipath.FC6_MultiPathData,
1298 "NetworkData": network.RHEL6_NetworkData,
1299 "PartData": partition.F12_PartData,
1300 "RaidData": raid.F13_RaidData,
1301 "RepoData": repo.RHEL6_RepoData,
1302 "SshPwData": sshpw.F13_SshPwData,
1303 "UserData": user.F12_UserData,
1304 "VolGroupData": volgroup.FC3_VolGroupData,
1305 "ZFCPData": zfcp.F12_ZFCPData,
1306 }
1307}
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py
new file mode 100644
index 0000000000..17c8211bbf
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F10Handler(BaseHandler):
24 version = F10
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py
new file mode 100644
index 0000000000..d21aee3e8b
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2008 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F11Handler(BaseHandler):
24 version = F11
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py
new file mode 100644
index 0000000000..cea3ecef6b
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F12Handler(BaseHandler):
24 version = F12
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py
new file mode 100644
index 0000000000..b94c738f79
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F13Handler(BaseHandler):
24 version = F13
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py
new file mode 100644
index 0000000000..478f75d15e
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2010 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F14Handler(BaseHandler):
24 version = F14
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py
new file mode 100644
index 0000000000..12aecb4c1a
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2010 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F15Handler(BaseHandler):
24 version = F15
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py
new file mode 100644
index 0000000000..3c52f8d754
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2011 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F16Handler(BaseHandler):
24 version = F16
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py
new file mode 100644
index 0000000000..5e856ea983
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F7Handler(BaseHandler):
24 version = F7
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py
new file mode 100644
index 0000000000..1a978810f4
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F8Handler(BaseHandler):
24 version = F8
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py
new file mode 100644
index 0000000000..116f1b57c9
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class F9Handler(BaseHandler):
24 version = F9
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py
new file mode 100644
index 0000000000..a115dc2646
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class FC3Handler(BaseHandler):
24 version = FC3
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py
new file mode 100644
index 0000000000..fd47b732ef
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class FC4Handler(BaseHandler):
24 version = FC4
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py
new file mode 100644
index 0000000000..bcdc29d23a
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class FC5Handler(BaseHandler):
24 version = FC5
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py
new file mode 100644
index 0000000000..c83a929f84
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class FC6Handler(BaseHandler):
24 version = FC6
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py
new file mode 100644
index 0000000000..131763c2a8
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class RHEL3Handler(BaseHandler):
24 version = RHEL3
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py
new file mode 100644
index 0000000000..3496c43ea5
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class RHEL4Handler(BaseHandler):
24 version = RHEL4
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py
new file mode 100644
index 0000000000..abb7a8d36c
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class RHEL5Handler(BaseHandler):
24 version = RHEL5
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py
new file mode 100644
index 0000000000..7202419780
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py
@@ -0,0 +1,24 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2010 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20from pykickstart.base import *
21from pykickstart.version import *
22
23class RHEL6Handler(BaseHandler):
24 version = RHEL6
diff --git a/scripts/lib/mic/3rdparty/pykickstart/ko.py b/scripts/lib/mic/3rdparty/pykickstart/ko.py
new file mode 100644
index 0000000000..1350d19c70
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/ko.py
@@ -0,0 +1,37 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2009 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20"""
21Base classes for internal pykickstart use.
22
23The module exports the following important classes:
24
25 KickstartObject - The base class for all classes in pykickstart
26"""
27
28class KickstartObject(object):
29 """The base class for all other classes in pykickstart."""
30 def __init__(self, *args, **kwargs):
31 """Create a new KickstartObject instance. All other classes in
32 pykickstart should be derived from this one. Instance attributes:
33 """
34 pass
35
36 def __str__(self):
37 return ""
diff --git a/scripts/lib/mic/3rdparty/pykickstart/options.py b/scripts/lib/mic/3rdparty/pykickstart/options.py
new file mode 100644
index 0000000000..341c5d7298
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/options.py
@@ -0,0 +1,204 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2005, 2006, 2007 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20"""
21Specialized option handling.
22
23This module exports two classes:
24
25 KSOptionParser - A specialized subclass of OptionParser to be used
26 in BaseHandler subclasses.
27
28 KSOption - A specialized subclass of Option.
29"""
30import warnings
31from copy import copy
32from optparse import *
33
34from constants import *
35from errors import *
36from version import *
37
38import gettext
39_ = lambda x: gettext.ldgettext("pykickstart", x)
40
41class KSOptionParser(OptionParser):
42 """A specialized subclass of optparse.OptionParser to handle extra option
43 attribute checking, work error reporting into the KickstartParseError
44 framework, and to turn off the default help.
45 """
46 def exit(self, status=0, msg=None):
47 pass
48
49 def error(self, msg):
50 if self.lineno != None:
51 raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg)
52 else:
53 raise KickstartParseError, msg
54
55 def keys(self):
56 retval = []
57
58 for opt in self.option_list:
59 if opt not in retval:
60 retval.append(opt.dest)
61
62 return retval
63
64 def _init_parsing_state (self):
65 OptionParser._init_parsing_state(self)
66 self.option_seen = {}
67
68 def check_values (self, values, args):
69 def seen(self, option):
70 return self.option_seen.has_key(option)
71
72 def usedTooNew(self, option):
73 return option.introduced and option.introduced > self.version
74
75 def usedDeprecated(self, option):
76 return option.deprecated
77
78 def usedRemoved(self, option):
79 return option.removed and option.removed <= self.version
80
81 for option in filter(lambda o: isinstance(o, Option), self.option_list):
82 if option.required and not seen(self, option):
83 raise KickstartValueError, formatErrorMsg(self.lineno, _("Option %s is required") % option)
84 elif seen(self, option) and usedTooNew(self, option):
85 mapping = {"option": option, "intro": versionToString(option.introduced),
86 "version": versionToString(self.version)}
87 self.error(_("The %(option)s option was introduced in version %(intro)s, but you are using kickstart syntax version %(version)s.") % mapping)
88 elif seen(self, option) and usedRemoved(self, option):
89 mapping = {"option": option, "removed": versionToString(option.removed),
90 "version": versionToString(self.version)}
91
92 if option.removed == self.version:
93 self.error(_("The %(option)s option is no longer supported.") % mapping)
94 else:
95 self.error(_("The %(option)s option was removed in version %(removed)s, but you are using kickstart syntax version %(version)s.") % mapping)
96 elif seen(self, option) and usedDeprecated(self, option):
97 mapping = {"lineno": self.lineno, "option": option}
98 warnings.warn(_("Ignoring deprecated option on line %(lineno)s: The %(option)s option has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this option.") % mapping, DeprecationWarning)
99
100 return (values, args)
101
102 def parse_args(self, *args, **kwargs):
103 if kwargs.has_key("lineno"):
104 self.lineno = kwargs.pop("lineno")
105
106 return OptionParser.parse_args(self, **kwargs)
107
108 def __init__(self, mapping=None, version=None):
109 """Create a new KSOptionParser instance. Each KickstartCommand
110 subclass should create one instance of KSOptionParser, providing
111 at least the lineno attribute. mapping and version are not required.
112 Instance attributes:
113
114 mapping -- A mapping from option strings to different values.
115 version -- The version of the kickstart syntax we are checking
116 against.
117 """
118 OptionParser.__init__(self, option_class=KSOption,
119 add_help_option=False,
120 conflict_handler="resolve")
121 if mapping is None:
122 self.map = {}
123 else:
124 self.map = mapping
125
126 self.lineno = None
127 self.option_seen = {}
128 self.version = version
129
130def _check_ksboolean(option, opt, value):
131 if value.lower() in ("on", "yes", "true", "1"):
132 return True
133 elif value.lower() in ("off", "no", "false", "0"):
134 return False
135 else:
136 mapping = {"opt": opt, "value": value}
137 raise OptionValueError(_("Option %(opt)s: invalid boolean value: %(value)r") % mapping)
138
139def _check_string(option, opt, value):
140 if len(value) > 2 and value.startswith("--"):
141 mapping = {"opt": opt, "value": value}
142 raise OptionValueError(_("Option %(opt)s: invalid string value: %(value)r") % mapping)
143 else:
144 return value
145
146# Creates a new Option class that supports several new attributes:
147# - required: any option with this attribute must be supplied or an exception
148# is thrown
149# - introduced: the kickstart syntax version that this option first appeared
150# in - an exception will be raised if the option is used and
151# the specified syntax version is less than the value of this
152# attribute
153# - deprecated: the kickstart syntax version that this option was deprecated
154# in - a DeprecationWarning will be thrown if the option is
155# used and the specified syntax version is greater than the
156# value of this attribute
157# - removed: the kickstart syntax version that this option was removed in - an
158# exception will be raised if the option is used and the specified
159# syntax version is greated than the value of this attribute
160# Also creates a new type:
161# - ksboolean: support various kinds of boolean values on an option
162# And two new actions:
163# - map : allows you to define an opt -> val mapping such that dest gets val
164# when opt is seen
165# - map_extend: allows you to define an opt -> [val1, ... valn] mapping such
166# that dest gets a list of vals built up when opt is seen
167class KSOption (Option):
168 ATTRS = Option.ATTRS + ['introduced', 'deprecated', 'removed', 'required']
169 ACTIONS = Option.ACTIONS + ("map", "map_extend",)
170 STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",)
171
172 TYPES = Option.TYPES + ("ksboolean", "string")
173 TYPE_CHECKER = copy(Option.TYPE_CHECKER)
174 TYPE_CHECKER["ksboolean"] = _check_ksboolean
175 TYPE_CHECKER["string"] = _check_string
176
177 def _check_required(self):
178 if self.required and not self.takes_value():
179 raise OptionError(_("Required flag set for option that doesn't take a value"), self)
180
181 # Make sure _check_required() is called from the constructor!
182 CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
183
184 def process (self, opt, value, values, parser):
185 Option.process(self, opt, value, values, parser)
186 parser.option_seen[self] = 1
187
188 # Override default take_action method to handle our custom actions.
189 def take_action(self, action, dest, opt, value, values, parser):
190 if action == "map":
191 values.ensure_value(dest, parser.map[opt.lstrip('-')])
192 elif action == "map_extend":
193 values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
194 else:
195 Option.take_action(self, action, dest, opt, value, values, parser)
196
197 def takes_value(self):
198 # Deprecated options don't take a value.
199 return Option.takes_value(self) and not self.deprecated
200
201 def __init__(self, *args, **kwargs):
202 self.deprecated = False
203 self.required = False
204 Option.__init__(self, *args, **kwargs)
diff --git a/scripts/lib/mic/3rdparty/pykickstart/parser.py b/scripts/lib/mic/3rdparty/pykickstart/parser.py
new file mode 100644
index 0000000000..840a448673
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/parser.py
@@ -0,0 +1,702 @@
1#
2# parser.py: Kickstart file parser.
3#
4# Chris Lumens <clumens@redhat.com>
5#
6# Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc.
7#
8# This copyrighted material is made available to anyone wishing to use, modify,
9# copy, or redistribute it subject to the terms and conditions of the GNU
10# General Public License v.2. This program is distributed in the hope that it
11# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
12# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13# See the GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along with
16# this program; if not, write to the Free Software Foundation, Inc., 51
17# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
18# trademarks that are incorporated in the source code or documentation are not
19# subject to the GNU General Public License and may only be used or replicated
20# with the express permission of Red Hat, Inc.
21#
22"""
23Main kickstart file processing module.
24
25This module exports several important classes:
26
27 Script - Representation of a single %pre, %post, or %traceback script.
28
29 Packages - Representation of the %packages section.
30
31 KickstartParser - The kickstart file parser state machine.
32"""
33
34from collections import Iterator
35import os
36import shlex
37import sys
38import tempfile
39from copy import copy
40from optparse import *
41from urlgrabber import urlread
42import urlgrabber.grabber as grabber
43
44import constants
45from errors import KickstartError, KickstartParseError, KickstartValueError, formatErrorMsg
46from ko import KickstartObject
47from sections import *
48import version
49
50import gettext
51_ = lambda x: gettext.ldgettext("pykickstart", x)
52
53STATE_END = "end"
54STATE_COMMANDS = "commands"
55
56ver = version.DEVEL
57
58def _preprocessStateMachine (lineIter):
59 l = None
60 lineno = 0
61
62 # Now open an output kickstart file that we are going to write to one
63 # line at a time.
64 (outF, outName) = tempfile.mkstemp("-ks.cfg", "", "/tmp")
65
66 while True:
67 try:
68 l = lineIter.next()
69 except StopIteration:
70 break
71
72 # At the end of the file?
73 if l == "":
74 break
75
76 lineno += 1
77 url = None
78
79 ll = l.strip()
80 if not ll.startswith("%ksappend"):
81 os.write(outF, l)
82 continue
83
84 # Try to pull down the remote file.
85 try:
86 ksurl = ll.split(' ')[1]
87 except:
88 raise KickstartParseError, formatErrorMsg(lineno, msg=_("Illegal url for %%ksappend: %s") % ll)
89
90 try:
91 url = grabber.urlopen(ksurl)
92 except grabber.URLGrabError, e:
93 raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file: %s") % e.strerror)
94 else:
95 # Sanity check result. Sometimes FTP doesn't catch a file
96 # is missing.
97 try:
98 if url.size < 1:
99 raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file"))
100 except:
101 raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file"))
102
103 # If that worked, write the remote file to the output kickstart
104 # file in one burst. Then close everything up to get ready to
105 # read ahead in the input file. This allows multiple %ksappend
106 # lines to exist.
107 if url is not None:
108 os.write(outF, url.read())
109 url.close()
110
111 # All done - close the temp file and return its location.
112 os.close(outF)
113 return outName
114
115def preprocessFromString (s):
116 """Preprocess the kickstart file, provided as the string str. This
117 method is currently only useful for handling %ksappend lines,
118 which need to be fetched before the real kickstart parser can be
119 run. Returns the location of the complete kickstart file.
120 """
121 i = iter(s.splitlines(True) + [""])
122 rc = _preprocessStateMachine (i.next)
123 return rc
124
125def preprocessKickstart (f):
126 """Preprocess the kickstart file, given by the filename file. This
127 method is currently only useful for handling %ksappend lines,
128 which need to be fetched before the real kickstart parser can be
129 run. Returns the location of the complete kickstart file.
130 """
131 try:
132 fh = urlopen(f)
133 except grabber.URLGrabError, e:
134 raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror)
135
136 rc = _preprocessStateMachine (iter(fh.readlines()))
137 fh.close()
138 return rc
139
140class PutBackIterator(Iterator):
141 def __init__(self, iterable):
142 self._iterable = iter(iterable)
143 self._buf = None
144
145 def __iter__(self):
146 return self
147
148 def put(self, s):
149 self._buf = s
150
151 def next(self):
152 if self._buf:
153 retval = self._buf
154 self._buf = None
155 return retval
156 else:
157 return self._iterable.next()
158
159###
160### SCRIPT HANDLING
161###
162class Script(KickstartObject):
163 """A class representing a single kickstart script. If functionality beyond
164 just a data representation is needed (for example, a run method in
165 anaconda), Script may be subclassed. Although a run method is not
166 provided, most of the attributes of Script have to do with running the
167 script. Instances of Script are held in a list by the Version object.
168 """
169 def __init__(self, script, *args , **kwargs):
170 """Create a new Script instance. Instance attributes:
171
172 errorOnFail -- If execution of the script fails, should anaconda
173 stop, display an error, and then reboot without
174 running any other scripts?
175 inChroot -- Does the script execute in anaconda's chroot
176 environment or not?
177 interp -- The program that should be used to interpret this
178 script.
179 lineno -- The line number this script starts on.
180 logfile -- Where all messages from the script should be logged.
181 script -- A string containing all the lines of the script.
182 type -- The type of the script, which can be KS_SCRIPT_* from
183 pykickstart.constants.
184 """
185 KickstartObject.__init__(self, *args, **kwargs)
186 self.script = "".join(script)
187
188 self.interp = kwargs.get("interp", "/bin/sh")
189 self.inChroot = kwargs.get("inChroot", False)
190 self.lineno = kwargs.get("lineno", None)
191 self.logfile = kwargs.get("logfile", None)
192 self.errorOnFail = kwargs.get("errorOnFail", False)
193 self.type = kwargs.get("type", constants.KS_SCRIPT_PRE)
194
195 def __str__(self):
196 """Return a string formatted for output to a kickstart file."""
197 retval = ""
198
199 if self.type == constants.KS_SCRIPT_PRE:
200 retval += '\n%pre'
201 elif self.type == constants.KS_SCRIPT_POST:
202 retval += '\n%post'
203 elif self.type == constants.KS_SCRIPT_TRACEBACK:
204 retval += '\n%traceback'
205
206 if self.interp != "/bin/sh" and self.interp != "":
207 retval += " --interpreter=%s" % self.interp
208 if self.type == constants.KS_SCRIPT_POST and not self.inChroot:
209 retval += " --nochroot"
210 if self.logfile != None:
211 retval += " --logfile %s" % self.logfile
212 if self.errorOnFail:
213 retval += " --erroronfail"
214
215 if self.script.endswith("\n"):
216 if ver >= version.F8:
217 return retval + "\n%s%%end\n" % self.script
218 else:
219 return retval + "\n%s\n" % self.script
220 else:
221 if ver >= version.F8:
222 return retval + "\n%s\n%%end\n" % self.script
223 else:
224 return retval + "\n%s\n" % self.script
225
226
227##
228## PACKAGE HANDLING
229##
230class Group:
231 """A class representing a single group in the %packages section."""
232 def __init__(self, name="", include=constants.GROUP_DEFAULT):
233 """Create a new Group instance. Instance attributes:
234
235 name -- The group's identifier
236 include -- The level of how much of the group should be included.
237 Values can be GROUP_* from pykickstart.constants.
238 """
239 self.name = name
240 self.include = include
241
242 def __str__(self):
243 """Return a string formatted for output to a kickstart file."""
244 if self.include == constants.GROUP_REQUIRED:
245 return "@%s --nodefaults" % self.name
246 elif self.include == constants.GROUP_ALL:
247 return "@%s --optional" % self.name
248 else:
249 return "@%s" % self.name
250
251 def __cmp__(self, other):
252 if self.name < other.name:
253 return -1
254 elif self.name > other.name:
255 return 1
256 return 0
257
258class Packages(KickstartObject):
259 """A class representing the %packages section of the kickstart file."""
260 def __init__(self, *args, **kwargs):
261 """Create a new Packages instance. Instance attributes:
262
263 addBase -- Should the Base group be installed even if it is
264 not specified?
265 default -- Should the default package set be selected?
266 excludedList -- A list of all the packages marked for exclusion in
267 the %packages section, without the leading minus
268 symbol.
269 excludeDocs -- Should documentation in each package be excluded?
270 groupList -- A list of Group objects representing all the groups
271 specified in the %packages section. Names will be
272 stripped of the leading @ symbol.
273 excludedGroupList -- A list of Group objects representing all the
274 groups specified for removal in the %packages
275 section. Names will be stripped of the leading
276 -@ symbols.
277 handleMissing -- If unknown packages are specified in the %packages
278 section, should it be ignored or not? Values can
279 be KS_MISSING_* from pykickstart.constants.
280 packageList -- A list of all the packages specified in the
281 %packages section.
282 instLangs -- A list of languages to install.
283 """
284 KickstartObject.__init__(self, *args, **kwargs)
285
286 self.addBase = True
287 self.default = False
288 self.excludedList = []
289 self.excludedGroupList = []
290 self.excludeDocs = False
291 self.groupList = []
292 self.handleMissing = constants.KS_MISSING_PROMPT
293 self.packageList = []
294 self.instLangs = None
295
296 def __str__(self):
297 """Return a string formatted for output to a kickstart file."""
298 pkgs = ""
299
300 if not self.default:
301 grps = self.groupList
302 grps.sort()
303 for grp in grps:
304 pkgs += "%s\n" % grp.__str__()
305
306 p = self.packageList
307 p.sort()
308 for pkg in p:
309 pkgs += "%s\n" % pkg
310
311 grps = self.excludedGroupList
312 grps.sort()
313 for grp in grps:
314 pkgs += "-%s\n" % grp.__str__()
315
316 p = self.excludedList
317 p.sort()
318 for pkg in p:
319 pkgs += "-%s\n" % pkg
320
321 if pkgs == "":
322 return ""
323
324 retval = "\n%packages"
325
326 if self.default:
327 retval += " --default"
328 if self.excludeDocs:
329 retval += " --excludedocs"
330 if not self.addBase:
331 retval += " --nobase"
332 if self.handleMissing == constants.KS_MISSING_IGNORE:
333 retval += " --ignoremissing"
334 if self.instLangs:
335 retval += " --instLangs=%s" % self.instLangs
336
337 if ver >= version.F8:
338 return retval + "\n" + pkgs + "\n%end\n"
339 else:
340 return retval + "\n" + pkgs + "\n"
341
342 def _processGroup (self, line):
343 op = OptionParser()
344 op.add_option("--nodefaults", action="store_true", default=False)
345 op.add_option("--optional", action="store_true", default=False)
346
347 (opts, extra) = op.parse_args(args=line.split())
348
349 if opts.nodefaults and opts.optional:
350 raise KickstartValueError, _("Group cannot specify both --nodefaults and --optional")
351
352 # If the group name has spaces in it, we have to put it back together
353 # now.
354 grp = " ".join(extra)
355
356 if opts.nodefaults:
357 self.groupList.append(Group(name=grp, include=constants.GROUP_REQUIRED))
358 elif opts.optional:
359 self.groupList.append(Group(name=grp, include=constants.GROUP_ALL))
360 else:
361 self.groupList.append(Group(name=grp, include=constants.GROUP_DEFAULT))
362
363 def add (self, pkgList):
364 """Given a list of lines from the input file, strip off any leading
365 symbols and add the result to the appropriate list.
366 """
367 existingExcludedSet = set(self.excludedList)
368 existingPackageSet = set(self.packageList)
369 newExcludedSet = set()
370 newPackageSet = set()
371
372 excludedGroupList = []
373
374 for pkg in pkgList:
375 stripped = pkg.strip()
376
377 if stripped[0] == "@":
378 self._processGroup(stripped[1:])
379 elif stripped[0] == "-":
380 if stripped[1] == "@":
381 excludedGroupList.append(Group(name=stripped[2:]))
382 else:
383 newExcludedSet.add(stripped[1:])
384 else:
385 newPackageSet.add(stripped)
386
387 # Groups have to be excluded in two different ways (note: can't use
388 # sets here because we have to store objects):
389 excludedGroupNames = map(lambda g: g.name, excludedGroupList)
390
391 # First, an excluded group may be cancelling out a previously given
392 # one. This is often the case when using %include. So there we should
393 # just remove the group from the list.
394 self.groupList = filter(lambda g: g.name not in excludedGroupNames, self.groupList)
395
396 # Second, the package list could have included globs which are not
397 # processed by pykickstart. In that case we need to preserve a list of
398 # excluded groups so whatever tool doing package/group installation can
399 # take appropriate action.
400 self.excludedGroupList.extend(excludedGroupList)
401
402 existingPackageSet = (existingPackageSet - newExcludedSet) | newPackageSet
403 existingExcludedSet = (existingExcludedSet - existingPackageSet) | newExcludedSet
404
405 self.packageList = list(existingPackageSet)
406 self.excludedList = list(existingExcludedSet)
407
408
409###
410### PARSER
411###
412class KickstartParser:
413 """The kickstart file parser class as represented by a basic state
414 machine. To create a specialized parser, make a subclass and override
415 any of the methods you care about. Methods that don't need to do
416 anything may just pass. However, _stateMachine should never be
417 overridden.
418 """
419 def __init__ (self, handler, followIncludes=True, errorsAreFatal=True,
420 missingIncludeIsFatal=True):
421 """Create a new KickstartParser instance. Instance attributes:
422
423 errorsAreFatal -- Should errors cause processing to halt, or
424 just print a message to the screen? This
425 is most useful for writing syntax checkers
426 that may want to continue after an error is
427 encountered.
428 followIncludes -- If %include is seen, should the included
429 file be checked as well or skipped?
430 handler -- An instance of a BaseHandler subclass. If
431 None, the input file will still be parsed
432 but no data will be saved and no commands
433 will be executed.
434 missingIncludeIsFatal -- Should missing include files be fatal, even
435 if errorsAreFatal is False?
436 """
437 self.errorsAreFatal = errorsAreFatal
438 self.followIncludes = followIncludes
439 self.handler = handler
440 self.currentdir = {}
441 self.missingIncludeIsFatal = missingIncludeIsFatal
442
443 self._state = STATE_COMMANDS
444 self._includeDepth = 0
445 self._line = ""
446
447 self.version = self.handler.version
448
449 global ver
450 ver = self.version
451
452 self._sections = {}
453 self.setupSections()
454
455 def _reset(self):
456 """Reset the internal variables of the state machine for a new kickstart file."""
457 self._state = STATE_COMMANDS
458 self._includeDepth = 0
459
460 def getSection(self, s):
461 """Return a reference to the requested section (s must start with '%'s),
462 or raise KeyError if not found.
463 """
464 return self._sections[s]
465
466 def handleCommand (self, lineno, args):
467 """Given the list of command and arguments, call the Version's
468 dispatcher method to handle the command. Returns the command or
469 data object returned by the dispatcher. This method may be
470 overridden in a subclass if necessary.
471 """
472 if self.handler:
473 self.handler.currentCmd = args[0]
474 self.handler.currentLine = self._line
475 retval = self.handler.dispatcher(args, lineno)
476
477 return retval
478
479 def registerSection(self, obj):
480 """Given an instance of a Section subclass, register the new section
481 with the parser. Calling this method means the parser will
482 recognize your new section and dispatch into the given object to
483 handle it.
484 """
485 if not obj.sectionOpen:
486 raise TypeError, "no sectionOpen given for section %s" % obj
487
488 if not obj.sectionOpen.startswith("%"):
489 raise TypeError, "section %s tag does not start with a %%" % obj.sectionOpen
490
491 self._sections[obj.sectionOpen] = obj
492
493 def _finalize(self, obj):
494 """Called at the close of a kickstart section to take any required
495 actions. Internally, this is used to add scripts once we have the
496 whole body read.
497 """
498 obj.finalize()
499 self._state = STATE_COMMANDS
500
501 def _handleSpecialComments(self, line):
502 """Kickstart recognizes a couple special comments."""
503 if self._state != STATE_COMMANDS:
504 return
505
506 # Save the platform for s-c-kickstart.
507 if line[:10] == "#platform=":
508 self.handler.platform = self._line[11:]
509
510 def _readSection(self, lineIter, lineno):
511 obj = self._sections[self._state]
512
513 while True:
514 try:
515 line = lineIter.next()
516 if line == "":
517 # This section ends at the end of the file.
518 if self.version >= version.F8:
519 raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end."))
520
521 self._finalize(obj)
522 except StopIteration:
523 break
524
525 lineno += 1
526
527 # Throw away blank lines and comments, unless the section wants all
528 # lines.
529 if self._isBlankOrComment(line) and not obj.allLines:
530 continue
531
532 if line.startswith("%"):
533 args = shlex.split(line)
534
535 if args and args[0] == "%end":
536 # This is a properly terminated section.
537 self._finalize(obj)
538 break
539 elif args and args[0] == "%ksappend":
540 continue
541 elif args and (self._validState(args[0]) or args[0] in ["%include", "%ksappend"]):
542 # This is an unterminated section.
543 if self.version >= version.F8:
544 raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end."))
545
546 # Finish up. We do not process the header here because
547 # kicking back out to STATE_COMMANDS will ensure that happens.
548 lineIter.put(line)
549 lineno -= 1
550 self._finalize(obj)
551 break
552 else:
553 # This is just a line within a section. Pass it off to whatever
554 # section handles it.
555 obj.handleLine(line)
556
557 return lineno
558
559 def _validState(self, st):
560 """Is the given section tag one that has been registered with the parser?"""
561 return st in self._sections.keys()
562
563 def _tryFunc(self, fn):
564 """Call the provided function (which doesn't take any arguments) and
565 do the appropriate error handling. If errorsAreFatal is False, this
566 function will just print the exception and keep going.
567 """
568 try:
569 fn()
570 except Exception, msg:
571 if self.errorsAreFatal:
572 raise
573 else:
574 print msg
575
576 def _isBlankOrComment(self, line):
577 return line.isspace() or line == "" or line.lstrip()[0] == '#'
578
579 def _stateMachine(self, lineIter):
580 # For error reporting.
581 lineno = 0
582
583 while True:
584 # Get the next line out of the file, quitting if this is the last line.
585 try:
586 self._line = lineIter.next()
587 if self._line == "":
588 break
589 except StopIteration:
590 break
591
592 lineno += 1
593
594 # Eliminate blank lines, whitespace-only lines, and comments.
595 if self._isBlankOrComment(self._line):
596 self._handleSpecialComments(self._line)
597 continue
598
599 # Remove any end-of-line comments.
600 sanitized = self._line.split("#")[0]
601
602 # Then split the line.
603 args = shlex.split(sanitized.rstrip())
604
605 if args[0] == "%include":
606 # This case comes up primarily in ksvalidator.
607 if not self.followIncludes:
608 continue
609
610 if len(args) == 1 or not args[1]:
611 raise KickstartParseError, formatErrorMsg(lineno)
612
613 self._includeDepth += 1
614
615 try:
616 self.readKickstart(args[1], reset=False)
617 except KickstartError:
618 # Handle the include file being provided over the
619 # network in a %pre script. This case comes up in the
620 # early parsing in anaconda.
621 if self.missingIncludeIsFatal:
622 raise
623
624 self._includeDepth -= 1
625 continue
626
627 # Now on to the main event.
628 if self._state == STATE_COMMANDS:
629 if args[0] == "%ksappend":
630 # This is handled by the preprocess* functions, so continue.
631 continue
632 elif args[0][0] == '%':
633 # This is the beginning of a new section. Handle its header
634 # here.
635 newSection = args[0]
636 if not self._validState(newSection):
637 raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown kickstart section: %s" % newSection))
638
639 self._state = newSection
640 obj = self._sections[self._state]
641 self._tryFunc(lambda: obj.handleHeader(lineno, args))
642
643 # This will handle all section processing, kicking us back
644 # out to STATE_COMMANDS at the end with the current line
645 # being the next section header, etc.
646 lineno = self._readSection(lineIter, lineno)
647 else:
648 # This is a command in the command section. Dispatch to it.
649 self._tryFunc(lambda: self.handleCommand(lineno, args))
650 elif self._state == STATE_END:
651 break
652
653 def readKickstartFromString (self, s, reset=True):
654 """Process a kickstart file, provided as the string str."""
655 if reset:
656 self._reset()
657
658 # Add a "" to the end of the list so the string reader acts like the
659 # file reader and we only get StopIteration when we're after the final
660 # line of input.
661 i = PutBackIterator(s.splitlines(True) + [""])
662 self._stateMachine (i)
663
664 def readKickstart(self, f, reset=True):
665 """Process a kickstart file, given by the filename f."""
666 if reset:
667 self._reset()
668
669 # an %include might not specify a full path. if we don't try to figure
670 # out what the path should have been, then we're unable to find it
671 # requiring full path specification, though, sucks. so let's make
672 # the reading "smart" by keeping track of what the path is at each
673 # include depth.
674 if not os.path.exists(f):
675 if self.currentdir.has_key(self._includeDepth - 1):
676 if os.path.exists(os.path.join(self.currentdir[self._includeDepth - 1], f)):
677 f = os.path.join(self.currentdir[self._includeDepth - 1], f)
678
679 cd = os.path.dirname(f)
680 if not cd.startswith("/"):
681 cd = os.path.abspath(cd)
682 self.currentdir[self._includeDepth] = cd
683
684 try:
685 s = urlread(f)
686 except grabber.URLGrabError, e:
687 raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror)
688
689 self.readKickstartFromString(s, reset=False)
690
691 def setupSections(self):
692 """Install the sections all kickstart files support. You may override
693 this method in a subclass, but should avoid doing so unless you know
694 what you're doing.
695 """
696 self._sections = {}
697
698 # Install the sections all kickstart files support.
699 self.registerSection(PreScriptSection(self.handler, dataObj=Script))
700 self.registerSection(PostScriptSection(self.handler, dataObj=Script))
701 self.registerSection(TracebackScriptSection(self.handler, dataObj=Script))
702 self.registerSection(PackageSection(self.handler))
diff --git a/scripts/lib/mic/3rdparty/pykickstart/sections.py b/scripts/lib/mic/3rdparty/pykickstart/sections.py
new file mode 100644
index 0000000000..44df856b8d
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/sections.py
@@ -0,0 +1,244 @@
1#
2# sections.py: Kickstart file sections.
3#
4# Chris Lumens <clumens@redhat.com>
5#
6# Copyright 2011 Red Hat, Inc.
7#
8# This copyrighted material is made available to anyone wishing to use, modify,
9# copy, or redistribute it subject to the terms and conditions of the GNU
10# General Public License v.2. This program is distributed in the hope that it
11# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
12# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13# See the GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along with
16# this program; if not, write to the Free Software Foundation, Inc., 51
17# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
18# trademarks that are incorporated in the source code or documentation are not
19# subject to the GNU General Public License and may only be used or replicated
20# with the express permission of Red Hat, Inc.
21#
22"""
23This module exports the classes that define a section of a kickstart file. A
24section is a chunk of the file starting with a %tag and ending with a %end.
25Examples of sections include %packages, %pre, and %post.
26
27You may use this module to define your own custom sections which will be
28treated just the same as a predefined one by the kickstart parser. All that
29is necessary is to create a new subclass of Section and call
30parser.registerSection with an instance of your new class.
31"""
32from constants import *
33from options import KSOptionParser
34from version import *
35
36class Section(object):
37 """The base class for defining kickstart sections. You are free to
38 subclass this as appropriate.
39
40 Class attributes:
41
42 allLines -- Does this section require the parser to call handleLine
43 for every line in the section, even blanks and comments?
44 sectionOpen -- The string that denotes the start of this section. You
45 must start your tag with a percent sign.
46 timesSeen -- This attribute is for informational purposes only. It is
47 incremented every time handleHeader is called to keep
48 track of the number of times a section of this type is
49 seen.
50 """
51 allLines = False
52 sectionOpen = ""
53 timesSeen = 0
54
55 def __init__(self, handler, **kwargs):
56 """Create a new Script instance. At the least, you must pass in an
57 instance of a baseHandler subclass.
58
59 Valid kwargs:
60
61 dataObj --
62 """
63 self.handler = handler
64
65 self.version = self.handler.version
66
67 self.dataObj = kwargs.get("dataObj", None)
68
69 def finalize(self):
70 """This method is called when the %end tag for a section is seen. It
71 is not required to be provided.
72 """
73 pass
74
75 def handleLine(self, line):
76 """This method is called for every line of a section. Take whatever
77 action is appropriate. While this method is not required to be
78 provided, not providing it does not make a whole lot of sense.
79
80 Arguments:
81
82 line -- The complete line, with any trailing newline.
83 """
84 pass
85
86 def handleHeader(self, lineno, args):
87 """This method is called when the opening tag for a section is seen.
88 Not all sections will need this method, though all provided with
89 kickstart include one.
90
91 Arguments:
92
93 args -- A list of all strings passed as arguments to the section
94 opening tag.
95 """
96 self.timesSeen += 1
97
98class NullSection(Section):
99 """This defines a section that pykickstart will recognize but do nothing
100 with. If the parser runs across a %section that has no object registered,
101 it will raise an error. Sometimes, you may want to simply ignore those
102 sections instead. This class is useful for that purpose.
103 """
104 def __init__(self, *args, **kwargs):
105 """Create a new NullSection instance. You must pass a sectionOpen
106 parameter (including a leading '%') for the section you wish to
107 ignore.
108 """
109 Section.__init__(self, *args, **kwargs)
110 self.sectionOpen = kwargs.get("sectionOpen")
111
112class ScriptSection(Section):
113 allLines = True
114
115 def __init__(self, *args, **kwargs):
116 Section.__init__(self, *args, **kwargs)
117 self._script = {}
118 self._resetScript()
119
120 def _getParser(self):
121 op = KSOptionParser(self.version)
122 op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
123 default=False)
124 op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
125 op.add_option("--log", "--logfile", dest="log")
126 return op
127
128 def _resetScript(self):
129 self._script = {"interp": "/bin/sh", "log": None, "errorOnFail": False,
130 "lineno": None, "chroot": False, "body": []}
131
132 def handleLine(self, line):
133 self._script["body"].append(line)
134
135 def finalize(self):
136 if " ".join(self._script["body"]).strip() == "":
137 return
138
139 kwargs = {"interp": self._script["interp"],
140 "inChroot": self._script["chroot"],
141 "lineno": self._script["lineno"],
142 "logfile": self._script["log"],
143 "errorOnFail": self._script["errorOnFail"],
144 "type": self._script["type"]}
145
146 s = self.dataObj (self._script["body"], **kwargs)
147 self._resetScript()
148
149 if self.handler:
150 self.handler.scripts.append(s)
151
152 def handleHeader(self, lineno, args):
153 """Process the arguments to a %pre/%post/%traceback header for later
154 setting on a Script instance once the end of the script is found.
155 This method may be overridden in a subclass if necessary.
156 """
157 Section.handleHeader(self, lineno, args)
158 op = self._getParser()
159
160 (opts, extra) = op.parse_args(args=args[1:], lineno=lineno)
161
162 self._script["interp"] = opts.interpreter
163 self._script["lineno"] = lineno
164 self._script["log"] = opts.log
165 self._script["errorOnFail"] = opts.errorOnFail
166 if hasattr(opts, "nochroot"):
167 self._script["chroot"] = not opts.nochroot
168
169class PreScriptSection(ScriptSection):
170 sectionOpen = "%pre"
171
172 def _resetScript(self):
173 ScriptSection._resetScript(self)
174 self._script["type"] = KS_SCRIPT_PRE
175
176class PostScriptSection(ScriptSection):
177 sectionOpen = "%post"
178
179 def _getParser(self):
180 op = ScriptSection._getParser(self)
181 op.add_option("--nochroot", dest="nochroot", action="store_true",
182 default=False)
183 return op
184
185 def _resetScript(self):
186 ScriptSection._resetScript(self)
187 self._script["chroot"] = True
188 self._script["type"] = KS_SCRIPT_POST
189
190class TracebackScriptSection(ScriptSection):
191 sectionOpen = "%traceback"
192
193 def _resetScript(self):
194 ScriptSection._resetScript(self)
195 self._script["type"] = KS_SCRIPT_TRACEBACK
196
197class PackageSection(Section):
198 sectionOpen = "%packages"
199
200 def handleLine(self, line):
201 if not self.handler:
202 return
203
204 (h, s, t) = line.partition('#')
205 line = h.rstrip()
206
207 self.handler.packages.add([line])
208
209 def handleHeader(self, lineno, args):
210 """Process the arguments to the %packages header and set attributes
211 on the Version's Packages instance appropriate. This method may be
212 overridden in a subclass if necessary.
213 """
214 Section.handleHeader(self, lineno, args)
215 op = KSOptionParser(version=self.version)
216 op.add_option("--excludedocs", dest="excludedocs", action="store_true",
217 default=False)
218 op.add_option("--ignoremissing", dest="ignoremissing",
219 action="store_true", default=False)
220 op.add_option("--nobase", dest="nobase", action="store_true",
221 default=False)
222 op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
223 deprecated=FC4, removed=F9)
224 op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
225 deprecated=FC4, removed=F9)
226 op.add_option("--default", dest="defaultPackages", action="store_true",
227 default=False, introduced=F7)
228 op.add_option("--instLangs", dest="instLangs", type="string",
229 default="", introduced=F9)
230
231 (opts, extra) = op.parse_args(args=args[1:], lineno=lineno)
232
233 self.handler.packages.excludeDocs = opts.excludedocs
234 self.handler.packages.addBase = not opts.nobase
235 if opts.ignoremissing:
236 self.handler.packages.handleMissing = KS_MISSING_IGNORE
237 else:
238 self.handler.packages.handleMissing = KS_MISSING_PROMPT
239
240 if opts.defaultPackages:
241 self.handler.packages.default = True
242
243 if opts.instLangs:
244 self.handler.packages.instLangs = opts.instLangs
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py
new file mode 100644
index 0000000000..7bcd9d5541
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py
@@ -0,0 +1,53 @@
1# This program is free software; you can redistribute it and/or modify
2# it under the terms of the GNU General Public License as published by
3# the Free Software Foundation; either version 2 of the License, or
4# (at your option) any later version.
5#
6# This program is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9# GNU Library General Public License for more details.
10#
11# You should have received a copy of the GNU General Public License
12# along with this program; if not, write to the Free Software
13# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14
15# Copyright 2002-2006 Michael D. Stenner, Ryan Tomayko
16
17# $Id: __init__.py,v 1.20 2006/09/22 00:58:55 mstenner Exp $
18
19"""A high-level cross-protocol url-grabber.
20
21Using urlgrabber, data can be fetched in three basic ways:
22
23 urlgrab(url) copy the file to the local filesystem
24 urlopen(url) open the remote file and return a file object
25 (like urllib2.urlopen)
26 urlread(url) return the contents of the file as a string
27
28When using these functions (or methods), urlgrabber supports the
29following features:
30
31 * identical behavior for http://, ftp://, and file:// urls
32 * http keepalive - faster downloads of many files by using
33 only a single connection
34 * byte ranges - fetch only a portion of the file
35 * reget - for a urlgrab, resume a partial download
36 * progress meters - the ability to report download progress
37 automatically, even when using urlopen!
38 * throttling - restrict bandwidth usage
39 * retries - automatically retry a download if it fails. The
40 number of retries and failure types are configurable.
41 * authenticated server access for http and ftp
42 * proxy support - support for authenticated http and ftp proxies
43 * mirror groups - treat a list of mirrors as a single source,
44 automatically switching mirrors if there is a failure.
45"""
46
47__version__ = '3.1.0'
48__date__ = '2006/09/21'
49__author__ = 'Michael D. Stenner <mstenner@linux.duke.edu>, ' \
50 'Ryan Tomayko <rtomayko@naeblis.cx>'
51__url__ = 'http://linux.duke.edu/projects/urlgrabber/'
52
53from grabber import urlgrab, urlopen, urlread
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py
new file mode 100644
index 0000000000..001b4e32d6
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py
@@ -0,0 +1,463 @@
1# This library is free software; you can redistribute it and/or
2# modify it under the terms of the GNU Lesser General Public
3# License as published by the Free Software Foundation; either
4# version 2.1 of the License, or (at your option) any later version.
5#
6# This library is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9# Lesser General Public License for more details.
10#
11# You should have received a copy of the GNU Lesser General Public
12# License along with this library; if not, write to the
13# Free Software Foundation, Inc.,
14# 59 Temple Place, Suite 330,
15# Boston, MA 02111-1307 USA
16
17# This file is part of urlgrabber, a high-level cross-protocol url-grabber
18# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
19
20# $Id: byterange.py,v 1.12 2006/07/20 20:15:58 mstenner Exp $
21
22import os
23import stat
24import urllib
25import urllib2
26import rfc822
27
28DEBUG = None
29
30try:
31 from cStringIO import StringIO
32except ImportError, msg:
33 from StringIO import StringIO
34
35class RangeError(IOError):
36 """Error raised when an unsatisfiable range is requested."""
37 pass
38
39class HTTPRangeHandler(urllib2.BaseHandler):
40 """Handler that enables HTTP Range headers.
41
42 This was extremely simple. The Range header is a HTTP feature to
43 begin with so all this class does is tell urllib2 that the
44 "206 Partial Content" reponse from the HTTP server is what we
45 expected.
46
47 Example:
48 import urllib2
49 import byterange
50
51 range_handler = range.HTTPRangeHandler()
52 opener = urllib2.build_opener(range_handler)
53
54 # install it
55 urllib2.install_opener(opener)
56
57 # create Request and set Range header
58 req = urllib2.Request('http://www.python.org/')
59 req.header['Range'] = 'bytes=30-50'
60 f = urllib2.urlopen(req)
61 """
62
63 def http_error_206(self, req, fp, code, msg, hdrs):
64 # 206 Partial Content Response
65 r = urllib.addinfourl(fp, hdrs, req.get_full_url())
66 r.code = code
67 r.msg = msg
68 return r
69
70 def http_error_416(self, req, fp, code, msg, hdrs):
71 # HTTP's Range Not Satisfiable error
72 raise RangeError('Requested Range Not Satisfiable')
73
74class HTTPSRangeHandler(HTTPRangeHandler):
75 """ Range Header support for HTTPS. """
76
77 def https_error_206(self, req, fp, code, msg, hdrs):
78 return self.http_error_206(req, fp, code, msg, hdrs)
79
80 def https_error_416(self, req, fp, code, msg, hdrs):
81 self.https_error_416(req, fp, code, msg, hdrs)
82
83class RangeableFileObject:
84 """File object wrapper to enable raw range handling.
85 This was implemented primarilary for handling range
86 specifications for file:// urls. This object effectively makes
87 a file object look like it consists only of a range of bytes in
88 the stream.
89
90 Examples:
91 # expose 10 bytes, starting at byte position 20, from
92 # /etc/aliases.
93 >>> fo = RangeableFileObject(file('/etc/passwd', 'r'), (20,30))
94 # seek seeks within the range (to position 23 in this case)
95 >>> fo.seek(3)
96 # tell tells where your at _within the range_ (position 3 in
97 # this case)
98 >>> fo.tell()
99 # read EOFs if an attempt is made to read past the last
100 # byte in the range. the following will return only 7 bytes.
101 >>> fo.read(30)
102 """
103
104 def __init__(self, fo, rangetup):
105 """Create a RangeableFileObject.
106 fo -- a file like object. only the read() method need be
107 supported but supporting an optimized seek() is
108 preferable.
109 rangetup -- a (firstbyte,lastbyte) tuple specifying the range
110 to work over.
111 The file object provided is assumed to be at byte offset 0.
112 """
113 self.fo = fo
114 (self.firstbyte, self.lastbyte) = range_tuple_normalize(rangetup)
115 self.realpos = 0
116 self._do_seek(self.firstbyte)
117
118 def __getattr__(self, name):
119 """This effectively allows us to wrap at the instance level.
120 Any attribute not found in _this_ object will be searched for
121 in self.fo. This includes methods."""
122 if hasattr(self.fo, name):
123 return getattr(self.fo, name)
124 raise AttributeError, name
125
126 def tell(self):
127 """Return the position within the range.
128 This is different from fo.seek in that position 0 is the
129 first byte position of the range tuple. For example, if
130 this object was created with a range tuple of (500,899),
131 tell() will return 0 when at byte position 500 of the file.
132 """
133 return (self.realpos - self.firstbyte)
134
135 def seek(self,offset,whence=0):
136 """Seek within the byte range.
137 Positioning is identical to that described under tell().
138 """
139 assert whence in (0, 1, 2)
140 if whence == 0: # absolute seek
141 realoffset = self.firstbyte + offset
142 elif whence == 1: # relative seek
143 realoffset = self.realpos + offset
144 elif whence == 2: # absolute from end of file
145 # XXX: are we raising the right Error here?
146 raise IOError('seek from end of file not supported.')
147
148 # do not allow seek past lastbyte in range
149 if self.lastbyte and (realoffset >= self.lastbyte):
150 realoffset = self.lastbyte
151
152 self._do_seek(realoffset - self.realpos)
153
154 def read(self, size=-1):
155 """Read within the range.
156 This method will limit the size read based on the range.
157 """
158 size = self._calc_read_size(size)
159 rslt = self.fo.read(size)
160 self.realpos += len(rslt)
161 return rslt
162
163 def readline(self, size=-1):
164 """Read lines within the range.
165 This method will limit the size read based on the range.
166 """
167 size = self._calc_read_size(size)
168 rslt = self.fo.readline(size)
169 self.realpos += len(rslt)
170 return rslt
171
172 def _calc_read_size(self, size):
173 """Handles calculating the amount of data to read based on
174 the range.
175 """
176 if self.lastbyte:
177 if size > -1:
178 if ((self.realpos + size) >= self.lastbyte):
179 size = (self.lastbyte - self.realpos)
180 else:
181 size = (self.lastbyte - self.realpos)
182 return size
183
184 def _do_seek(self,offset):
185 """Seek based on whether wrapped object supports seek().
186 offset is relative to the current position (self.realpos).
187 """
188 assert offset >= 0
189 if not hasattr(self.fo, 'seek'):
190 self._poor_mans_seek(offset)
191 else:
192 self.fo.seek(self.realpos + offset)
193 self.realpos+= offset
194
195 def _poor_mans_seek(self,offset):
196 """Seek by calling the wrapped file objects read() method.
197 This is used for file like objects that do not have native
198 seek support. The wrapped objects read() method is called
199 to manually seek to the desired position.
200 offset -- read this number of bytes from the wrapped
201 file object.
202 raise RangeError if we encounter EOF before reaching the
203 specified offset.
204 """
205 pos = 0
206 bufsize = 1024
207 while pos < offset:
208 if (pos + bufsize) > offset:
209 bufsize = offset - pos
210 buf = self.fo.read(bufsize)
211 if len(buf) != bufsize:
212 raise RangeError('Requested Range Not Satisfiable')
213 pos+= bufsize
214
215class FileRangeHandler(urllib2.FileHandler):
216 """FileHandler subclass that adds Range support.
217 This class handles Range headers exactly like an HTTP
218 server would.
219 """
220 def open_local_file(self, req):
221 import mimetypes
222 import mimetools
223 host = req.get_host()
224 file = req.get_selector()
225 localfile = urllib.url2pathname(file)
226 stats = os.stat(localfile)
227 size = stats[stat.ST_SIZE]
228 modified = rfc822.formatdate(stats[stat.ST_MTIME])
229 mtype = mimetypes.guess_type(file)[0]
230 if host:
231 host, port = urllib.splitport(host)
232 if port or socket.gethostbyname(host) not in self.get_names():
233 raise urllib2.URLError('file not on local host')
234 fo = open(localfile,'rb')
235 brange = req.headers.get('Range',None)
236 brange = range_header_to_tuple(brange)
237 assert brange != ()
238 if brange:
239 (fb,lb) = brange
240 if lb == '': lb = size
241 if fb < 0 or fb > size or lb > size:
242 raise RangeError('Requested Range Not Satisfiable')
243 size = (lb - fb)
244 fo = RangeableFileObject(fo, (fb,lb))
245 headers = mimetools.Message(StringIO(
246 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' %
247 (mtype or 'text/plain', size, modified)))
248 return urllib.addinfourl(fo, headers, 'file:'+file)
249
250
251# FTP Range Support
252# Unfortunately, a large amount of base FTP code had to be copied
253# from urllib and urllib2 in order to insert the FTP REST command.
254# Code modifications for range support have been commented as
255# follows:
256# -- range support modifications start/end here
257
258from urllib import splitport, splituser, splitpasswd, splitattr, \
259 unquote, addclosehook, addinfourl
260import ftplib
261import socket
262import sys
263import ftplib
264import mimetypes
265import mimetools
266
267class FTPRangeHandler(urllib2.FTPHandler):
268 def ftp_open(self, req):
269 host = req.get_host()
270 if not host:
271 raise IOError, ('ftp error', 'no host given')
272 host, port = splitport(host)
273 if port is None:
274 port = ftplib.FTP_PORT
275
276 # username/password handling
277 user, host = splituser(host)
278 if user:
279 user, passwd = splitpasswd(user)
280 else:
281 passwd = None
282 host = unquote(host)
283 user = unquote(user or '')
284 passwd = unquote(passwd or '')
285
286 try:
287 host = socket.gethostbyname(host)
288 except socket.error, msg:
289 raise urllib2.URLError(msg)
290 path, attrs = splitattr(req.get_selector())
291 dirs = path.split('/')
292 dirs = map(unquote, dirs)
293 dirs, file = dirs[:-1], dirs[-1]
294 if dirs and not dirs[0]:
295 dirs = dirs[1:]
296 try:
297 fw = self.connect_ftp(user, passwd, host, port, dirs)
298 type = file and 'I' or 'D'
299 for attr in attrs:
300 attr, value = splitattr(attr)
301 if attr.lower() == 'type' and \
302 value in ('a', 'A', 'i', 'I', 'd', 'D'):
303 type = value.upper()
304
305 # -- range support modifications start here
306 rest = None
307 range_tup = range_header_to_tuple(req.headers.get('Range',None))
308 assert range_tup != ()
309 if range_tup:
310 (fb,lb) = range_tup
311 if fb > 0: rest = fb
312 # -- range support modifications end here
313
314 fp, retrlen = fw.retrfile(file, type, rest)
315
316 # -- range support modifications start here
317 if range_tup:
318 (fb,lb) = range_tup
319 if lb == '':
320 if retrlen is None or retrlen == 0:
321 raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.')
322 lb = retrlen
323 retrlen = lb - fb
324 if retrlen < 0:
325 # beginning of range is larger than file
326 raise RangeError('Requested Range Not Satisfiable')
327 else:
328 retrlen = lb - fb
329 fp = RangeableFileObject(fp, (0,retrlen))
330 # -- range support modifications end here
331
332 headers = ""
333 mtype = mimetypes.guess_type(req.get_full_url())[0]
334 if mtype:
335 headers += "Content-Type: %s\n" % mtype
336 if retrlen is not None and retrlen >= 0:
337 headers += "Content-Length: %d\n" % retrlen
338 sf = StringIO(headers)
339 headers = mimetools.Message(sf)
340 return addinfourl(fp, headers, req.get_full_url())
341 except ftplib.all_errors, msg:
342 raise IOError, ('ftp error', msg), sys.exc_info()[2]
343
344 def connect_ftp(self, user, passwd, host, port, dirs):
345 fw = ftpwrapper(user, passwd, host, port, dirs)
346 return fw
347
348class ftpwrapper(urllib.ftpwrapper):
349 # range support note:
350 # this ftpwrapper code is copied directly from
351 # urllib. The only enhancement is to add the rest
352 # argument and pass it on to ftp.ntransfercmd
353 def retrfile(self, file, type, rest=None):
354 self.endtransfer()
355 if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
356 else: cmd = 'TYPE ' + type; isdir = 0
357 try:
358 self.ftp.voidcmd(cmd)
359 except ftplib.all_errors:
360 self.init()
361 self.ftp.voidcmd(cmd)
362 conn = None
363 if file and not isdir:
364 # Use nlst to see if the file exists at all
365 try:
366 self.ftp.nlst(file)
367 except ftplib.error_perm, reason:
368 raise IOError, ('ftp error', reason), sys.exc_info()[2]
369 # Restore the transfer mode!
370 self.ftp.voidcmd(cmd)
371 # Try to retrieve as a file
372 try:
373 cmd = 'RETR ' + file
374 conn = self.ftp.ntransfercmd(cmd, rest)
375 except ftplib.error_perm, reason:
376 if str(reason)[:3] == '501':
377 # workaround for REST not supported error
378 fp, retrlen = self.retrfile(file, type)
379 fp = RangeableFileObject(fp, (rest,''))
380 return (fp, retrlen)
381 elif str(reason)[:3] != '550':
382 raise IOError, ('ftp error', reason), sys.exc_info()[2]
383 if not conn:
384 # Set transfer mode to ASCII!
385 self.ftp.voidcmd('TYPE A')
386 # Try a directory listing
387 if file: cmd = 'LIST ' + file
388 else: cmd = 'LIST'
389 conn = self.ftp.ntransfercmd(cmd)
390 self.busy = 1
391 # Pass back both a suitably decorated object and a retrieval length
392 return (addclosehook(conn[0].makefile('rb'),
393 self.endtransfer), conn[1])
394
395
396####################################################################
397# Range Tuple Functions
398# XXX: These range tuple functions might go better in a class.
399
400_rangere = None
401def range_header_to_tuple(range_header):
402 """Get a (firstbyte,lastbyte) tuple from a Range header value.
403
404 Range headers have the form "bytes=<firstbyte>-<lastbyte>". This
405 function pulls the firstbyte and lastbyte values and returns
406 a (firstbyte,lastbyte) tuple. If lastbyte is not specified in
407 the header value, it is returned as an empty string in the
408 tuple.
409
410 Return None if range_header is None
411 Return () if range_header does not conform to the range spec
412 pattern.
413
414 """
415 global _rangere
416 if range_header is None: return None
417 if _rangere is None:
418 import re
419 _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)')
420 match = _rangere.match(range_header)
421 if match:
422 tup = range_tuple_normalize(match.group(1,2))
423 if tup and tup[1]:
424 tup = (tup[0],tup[1]+1)
425 return tup
426 return ()
427
428def range_tuple_to_header(range_tup):
429 """Convert a range tuple to a Range header value.
430 Return a string of the form "bytes=<firstbyte>-<lastbyte>" or None
431 if no range is needed.
432 """
433 if range_tup is None: return None
434 range_tup = range_tuple_normalize(range_tup)
435 if range_tup:
436 if range_tup[1]:
437 range_tup = (range_tup[0],range_tup[1] - 1)
438 return 'bytes=%s-%s' % range_tup
439
440def range_tuple_normalize(range_tup):
441 """Normalize a (first_byte,last_byte) range tuple.
442 Return a tuple whose first element is guaranteed to be an int
443 and whose second element will be '' (meaning: the last byte) or
444 an int. Finally, return None if the normalized tuple == (0,'')
445 as that is equivelant to retrieving the entire file.
446 """
447 if range_tup is None: return None
448 # handle first byte
449 fb = range_tup[0]
450 if fb in (None,''): fb = 0
451 else: fb = int(fb)
452 # handle last byte
453 try: lb = range_tup[1]
454 except IndexError: lb = ''
455 else:
456 if lb is None: lb = ''
457 elif lb != '': lb = int(lb)
458 # check if range is over the entire file
459 if (fb,lb) == (0,''): return None
460 # check that the range is valid
461 if lb < fb: raise RangeError('Invalid byte range: %s-%s' % (fb,lb))
462 return (fb,lb)
463
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py
new file mode 100644
index 0000000000..fefdab36f6
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py
@@ -0,0 +1,1477 @@
1# This library is free software; you can redistribute it and/or
2# modify it under the terms of the GNU Lesser General Public
3# License as published by the Free Software Foundation; either
4# version 2.1 of the License, or (at your option) any later version.
5#
6# This library is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9# Lesser General Public License for more details.
10#
11# You should have received a copy of the GNU Lesser General Public
12# License along with this library; if not, write to the
13# Free Software Foundation, Inc.,
14# 59 Temple Place, Suite 330,
15# Boston, MA 02111-1307 USA
16
17# This file is part of urlgrabber, a high-level cross-protocol url-grabber
18# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
19
20"""A high-level cross-protocol url-grabber.
21
22GENERAL ARGUMENTS (kwargs)
23
24 Where possible, the module-level default is indicated, and legal
25 values are provided.
26
27 copy_local = 0 [0|1]
28
29 ignored except for file:// urls, in which case it specifies
30 whether urlgrab should still make a copy of the file, or simply
31 point to the existing copy. The module level default for this
32 option is 0.
33
34 close_connection = 0 [0|1]
35
36 tells URLGrabber to close the connection after a file has been
37 transfered. This is ignored unless the download happens with the
38 http keepalive handler (keepalive=1). Otherwise, the connection
39 is left open for further use. The module level default for this
40 option is 0 (keepalive connections will not be closed).
41
42 keepalive = 1 [0|1]
43
44 specifies whether keepalive should be used for HTTP/1.1 servers
45 that support it. The module level default for this option is 1
46 (keepalive is enabled).
47
48 progress_obj = None
49
50 a class instance that supports the following methods:
51 po.start(filename, url, basename, length, text)
52 # length will be None if unknown
53 po.update(read) # read == bytes read so far
54 po.end()
55
56 text = None
57
58 specifies an alternativ text item in the beginning of the progress
59 bar line. If not given, the basename of the file is used.
60
61 throttle = 1.0
62
63 a number - if it's an int, it's the bytes/second throttle limit.
64 If it's a float, it is first multiplied by bandwidth. If throttle
65 == 0, throttling is disabled. If None, the module-level default
66 (which can be set on default_grabber.throttle) is used. See
67 BANDWIDTH THROTTLING for more information.
68
69 timeout = None
70
71 a positive float expressing the number of seconds to wait for socket
72 operations. If the value is None or 0.0, socket operations will block
73 forever. Setting this option causes urlgrabber to call the settimeout
74 method on the Socket object used for the request. See the Python
75 documentation on settimeout for more information.
76 http://www.python.org/doc/current/lib/socket-objects.html
77
78 bandwidth = 0
79
80 the nominal max bandwidth in bytes/second. If throttle is a float
81 and bandwidth == 0, throttling is disabled. If None, the
82 module-level default (which can be set on
83 default_grabber.bandwidth) is used. See BANDWIDTH THROTTLING for
84 more information.
85
86 range = None
87
88 a tuple of the form (first_byte, last_byte) describing a byte
89 range to retrieve. Either or both of the values may set to
90 None. If first_byte is None, byte offset 0 is assumed. If
91 last_byte is None, the last byte available is assumed. Note that
92 the range specification is python-like in that (0,10) will yeild
93 the first 10 bytes of the file.
94
95 If set to None, no range will be used.
96
97 reget = None [None|'simple'|'check_timestamp']
98
99 whether to attempt to reget a partially-downloaded file. Reget
100 only applies to .urlgrab and (obviously) only if there is a
101 partially downloaded file. Reget has two modes:
102
103 'simple' -- the local file will always be trusted. If there
104 are 100 bytes in the local file, then the download will always
105 begin 100 bytes into the requested file.
106
107 'check_timestamp' -- the timestamp of the server file will be
108 compared to the timestamp of the local file. ONLY if the
109 local file is newer than or the same age as the server file
110 will reget be used. If the server file is newer, or the
111 timestamp is not returned, the entire file will be fetched.
112
113 NOTE: urlgrabber can do very little to verify that the partial
114 file on disk is identical to the beginning of the remote file.
115 You may want to either employ a custom "checkfunc" or simply avoid
116 using reget in situations where corruption is a concern.
117
118 user_agent = 'urlgrabber/VERSION'
119
120 a string, usually of the form 'AGENT/VERSION' that is provided to
121 HTTP servers in the User-agent header. The module level default
122 for this option is "urlgrabber/VERSION".
123
124 http_headers = None
125
126 a tuple of 2-tuples, each containing a header and value. These
127 will be used for http and https requests only. For example, you
128 can do
129 http_headers = (('Pragma', 'no-cache'),)
130
131 ftp_headers = None
132
133 this is just like http_headers, but will be used for ftp requests.
134
135 proxies = None
136
137 a dictionary that maps protocol schemes to proxy hosts. For
138 example, to use a proxy server on host "foo" port 3128 for http
139 and https URLs:
140 proxies={ 'http' : 'http://foo:3128', 'https' : 'http://foo:3128' }
141 note that proxy authentication information may be provided using
142 normal URL constructs:
143 proxies={ 'http' : 'http://user:host@foo:3128' }
144 Lastly, if proxies is None, the default environment settings will
145 be used.
146
147 prefix = None
148
149 a url prefix that will be prepended to all requested urls. For
150 example:
151 g = URLGrabber(prefix='http://foo.com/mirror/')
152 g.urlgrab('some/file.txt')
153 ## this will fetch 'http://foo.com/mirror/some/file.txt'
154 This option exists primarily to allow identical behavior to
155 MirrorGroup (and derived) instances. Note: a '/' will be inserted
156 if necessary, so you cannot specify a prefix that ends with a
157 partial file or directory name.
158
159 opener = None
160
161 Overrides the default urllib2.OpenerDirector provided to urllib2
162 when making requests. This option exists so that the urllib2
163 handler chain may be customized. Note that the range, reget,
164 proxy, and keepalive features require that custom handlers be
165 provided to urllib2 in order to function properly. If an opener
166 option is provided, no attempt is made by urlgrabber to ensure
167 chain integrity. You are responsible for ensuring that any
168 extension handlers are present if said features are required.
169
170 data = None
171
172 Only relevant for the HTTP family (and ignored for other
173 protocols), this allows HTTP POSTs. When the data kwarg is
174 present (and not None), an HTTP request will automatically become
175 a POST rather than GET. This is done by direct passthrough to
176 urllib2. If you use this, you may also want to set the
177 'Content-length' and 'Content-type' headers with the http_headers
178 option. Note that python 2.2 handles the case of these
179 badly and if you do not use the proper case (shown here), your
180 values will be overridden with the defaults.
181
182
183RETRY RELATED ARGUMENTS
184
185 retry = None
186
187 the number of times to retry the grab before bailing. If this is
188 zero, it will retry forever. This was intentional... really, it
189 was :). If this value is not supplied or is supplied but is None
190 retrying does not occur.
191
192 retrycodes = [-1,2,4,5,6,7]
193
194 a sequence of errorcodes (values of e.errno) for which it should
195 retry. See the doc on URLGrabError for more details on this. You
196 might consider modifying a copy of the default codes rather than
197 building yours from scratch so that if the list is extended in the
198 future (or one code is split into two) you can still enjoy the
199 benefits of the default list. You can do that with something like
200 this:
201
202 retrycodes = urlgrabber.grabber.URLGrabberOptions().retrycodes
203 if 12 not in retrycodes:
204 retrycodes.append(12)
205
206 checkfunc = None
207
208 a function to do additional checks. This defaults to None, which
209 means no additional checking. The function should simply return
210 on a successful check. It should raise URLGrabError on an
211 unsuccessful check. Raising of any other exception will be
212 considered immediate failure and no retries will occur.
213
214 If it raises URLGrabError, the error code will determine the retry
215 behavior. Negative error numbers are reserved for use by these
216 passed in functions, so you can use many negative numbers for
217 different types of failure. By default, -1 results in a retry,
218 but this can be customized with retrycodes.
219
220 If you simply pass in a function, it will be given exactly one
221 argument: a CallbackObject instance with the .url attribute
222 defined and either .filename (for urlgrab) or .data (for urlread).
223 For urlgrab, .filename is the name of the local file. For
224 urlread, .data is the actual string data. If you need other
225 arguments passed to the callback (program state of some sort), you
226 can do so like this:
227
228 checkfunc=(function, ('arg1', 2), {'kwarg': 3})
229
230 if the downloaded file has filename /tmp/stuff, then this will
231 result in this call (for urlgrab):
232
233 function(obj, 'arg1', 2, kwarg=3)
234 # obj.filename = '/tmp/stuff'
235 # obj.url = 'http://foo.com/stuff'
236
237 NOTE: both the "args" tuple and "kwargs" dict must be present if
238 you use this syntax, but either (or both) can be empty.
239
240 failure_callback = None
241
242 The callback that gets called during retries when an attempt to
243 fetch a file fails. The syntax for specifying the callback is
244 identical to checkfunc, except for the attributes defined in the
245 CallbackObject instance. The attributes for failure_callback are:
246
247 exception = the raised exception
248 url = the url we're trying to fetch
249 tries = the number of tries so far (including this one)
250 retry = the value of the retry option
251
252 The callback is present primarily to inform the calling program of
253 the failure, but if it raises an exception (including the one it's
254 passed) that exception will NOT be caught and will therefore cause
255 future retries to be aborted.
256
257 The callback is called for EVERY failure, including the last one.
258 On the last try, the callback can raise an alternate exception,
259 but it cannot (without severe trickiness) prevent the exception
260 from being raised.
261
262 interrupt_callback = None
263
264 This callback is called if KeyboardInterrupt is received at any
265 point in the transfer. Basically, this callback can have three
266 impacts on the fetch process based on the way it exits:
267
268 1) raise no exception: the current fetch will be aborted, but
269 any further retries will still take place
270
271 2) raise a URLGrabError: if you're using a MirrorGroup, then
272 this will prompt a failover to the next mirror according to
273 the behavior of the MirrorGroup subclass. It is recommended
274 that you raise URLGrabError with code 15, 'user abort'. If
275 you are NOT using a MirrorGroup subclass, then this is the
276 same as (3).
277
278 3) raise some other exception (such as KeyboardInterrupt), which
279 will not be caught at either the grabber or mirror levels.
280 That is, it will be raised up all the way to the caller.
281
282 This callback is very similar to failure_callback. They are
283 passed the same arguments, so you could use the same function for
284 both.
285
286 urlparser = URLParser()
287
288 The URLParser class handles pre-processing of URLs, including
289 auth-handling for user/pass encoded in http urls, file handing
290 (that is, filenames not sent as a URL), and URL quoting. If you
291 want to override any of this behavior, you can pass in a
292 replacement instance. See also the 'quote' option.
293
294 quote = None
295
296 Whether or not to quote the path portion of a url.
297 quote = 1 -> quote the URLs (they're not quoted yet)
298 quote = 0 -> do not quote them (they're already quoted)
299 quote = None -> guess what to do
300
301 This option only affects proper urls like 'file:///etc/passwd'; it
302 does not affect 'raw' filenames like '/etc/passwd'. The latter
303 will always be quoted as they are converted to URLs. Also, only
304 the path part of a url is quoted. If you need more fine-grained
305 control, you should probably subclass URLParser and pass it in via
306 the 'urlparser' option.
307
308BANDWIDTH THROTTLING
309
310 urlgrabber supports throttling via two values: throttle and
311 bandwidth Between the two, you can either specify and absolute
312 throttle threshold or specify a theshold as a fraction of maximum
313 available bandwidth.
314
315 throttle is a number - if it's an int, it's the bytes/second
316 throttle limit. If it's a float, it is first multiplied by
317 bandwidth. If throttle == 0, throttling is disabled. If None, the
318 module-level default (which can be set with set_throttle) is used.
319
320 bandwidth is the nominal max bandwidth in bytes/second. If throttle
321 is a float and bandwidth == 0, throttling is disabled. If None, the
322 module-level default (which can be set with set_bandwidth) is used.
323
324 THROTTLING EXAMPLES:
325
326 Lets say you have a 100 Mbps connection. This is (about) 10^8 bits
327 per second, or 12,500,000 Bytes per second. You have a number of
328 throttling options:
329
330 *) set_bandwidth(12500000); set_throttle(0.5) # throttle is a float
331
332 This will limit urlgrab to use half of your available bandwidth.
333
334 *) set_throttle(6250000) # throttle is an int
335
336 This will also limit urlgrab to use half of your available
337 bandwidth, regardless of what bandwidth is set to.
338
339 *) set_throttle(6250000); set_throttle(1.0) # float
340
341 Use half your bandwidth
342
343 *) set_throttle(6250000); set_throttle(2.0) # float
344
345 Use up to 12,500,000 Bytes per second (your nominal max bandwidth)
346
347 *) set_throttle(6250000); set_throttle(0) # throttle = 0
348
349 Disable throttling - this is more efficient than a very large
350 throttle setting.
351
352 *) set_throttle(0); set_throttle(1.0) # throttle is float, bandwidth = 0
353
354 Disable throttling - this is the default when the module is loaded.
355
356 SUGGESTED AUTHOR IMPLEMENTATION (THROTTLING)
357
358 While this is flexible, it's not extremely obvious to the user. I
359 suggest you implement a float throttle as a percent to make the
360 distinction between absolute and relative throttling very explicit.
361
362 Also, you may want to convert the units to something more convenient
363 than bytes/second, such as kbps or kB/s, etc.
364
365"""
366
367# $Id: grabber.py,v 1.48 2006/09/22 00:58:05 mstenner Exp $
368
369import os
370import os.path
371import sys
372import urlparse
373import rfc822
374import time
375import string
376import urllib
377import urllib2
378from stat import * # S_* and ST_*
379
380########################################################################
381# MODULE INITIALIZATION
382########################################################################
383try:
384 exec('from ' + (__name__.split('.'))[0] + ' import __version__')
385except:
386 __version__ = '???'
387
388import sslfactory
389
390auth_handler = urllib2.HTTPBasicAuthHandler( \
391 urllib2.HTTPPasswordMgrWithDefaultRealm())
392
393try:
394 from i18n import _
395except ImportError, msg:
396 def _(st): return st
397
398try:
399 from httplib import HTTPException
400except ImportError, msg:
401 HTTPException = None
402
403try:
404 # This is a convenient way to make keepalive optional.
405 # Just rename the module so it can't be imported.
406 import keepalive
407 from keepalive import HTTPHandler, HTTPSHandler
408 have_keepalive = True
409except ImportError, msg:
410 have_keepalive = False
411
412try:
413 # add in range support conditionally too
414 import byterange
415 from byterange import HTTPRangeHandler, HTTPSRangeHandler, \
416 FileRangeHandler, FTPRangeHandler, range_tuple_normalize, \
417 range_tuple_to_header, RangeError
418except ImportError, msg:
419 range_handlers = ()
420 RangeError = None
421 have_range = 0
422else:
423 range_handlers = (HTTPRangeHandler(), HTTPSRangeHandler(),
424 FileRangeHandler(), FTPRangeHandler())
425 have_range = 1
426
427
428# check whether socket timeout support is available (Python >= 2.3)
429import socket
430try:
431 TimeoutError = socket.timeout
432 have_socket_timeout = True
433except AttributeError:
434 TimeoutError = None
435 have_socket_timeout = False
436
437########################################################################
438# functions for debugging output. These functions are here because they
439# are also part of the module initialization.
440DEBUG = None
441def set_logger(DBOBJ):
442 """Set the DEBUG object. This is called by _init_default_logger when
443 the environment variable URLGRABBER_DEBUG is set, but can also be
444 called by a calling program. Basically, if the calling program uses
445 the logging module and would like to incorporate urlgrabber logging,
446 then it can do so this way. It's probably not necessary as most
447 internal logging is only for debugging purposes.
448
449 The passed-in object should be a logging.Logger instance. It will
450 be pushed into the keepalive and byterange modules if they're
451 being used. The mirror module pulls this object in on import, so
452 you will need to manually push into it. In fact, you may find it
453 tidier to simply push your logging object (or objects) into each
454 of these modules independently.
455 """
456
457 global DEBUG
458 DEBUG = DBOBJ
459 if have_keepalive and keepalive.DEBUG is None:
460 keepalive.DEBUG = DBOBJ
461 if have_range and byterange.DEBUG is None:
462 byterange.DEBUG = DBOBJ
463 if sslfactory.DEBUG is None:
464 sslfactory.DEBUG = DBOBJ
465
466def _init_default_logger():
467 '''Examines the environment variable URLGRABBER_DEBUG and creates
468 a logging object (logging.logger) based on the contents. It takes
469 the form
470
471 URLGRABBER_DEBUG=level,filename
472
473 where "level" can be either an integer or a log level from the
474 logging module (DEBUG, INFO, etc). If the integer is zero or
475 less, logging will be disabled. Filename is the filename where
476 logs will be sent. If it is "-", then stdout will be used. If
477 the filename is empty or missing, stderr will be used. If the
478 variable cannot be processed or the logging module cannot be
479 imported (python < 2.3) then logging will be disabled. Here are
480 some examples:
481
482 URLGRABBER_DEBUG=1,debug.txt # log everything to debug.txt
483 URLGRABBER_DEBUG=WARNING,- # log warning and higher to stdout
484 URLGRABBER_DEBUG=INFO # log info and higher to stderr
485
486 This funtion is called during module initialization. It is not
487 intended to be called from outside. The only reason it is a
488 function at all is to keep the module-level namespace tidy and to
489 collect the code into a nice block.'''
490
491 try:
492 dbinfo = os.environ['URLGRABBER_DEBUG'].split(',')
493 import logging
494 level = logging._levelNames.get(dbinfo[0], int(dbinfo[0]))
495 if level < 1: raise ValueError()
496
497 formatter = logging.Formatter('%(asctime)s %(message)s')
498 if len(dbinfo) > 1: filename = dbinfo[1]
499 else: filename = ''
500 if filename == '': handler = logging.StreamHandler(sys.stderr)
501 elif filename == '-': handler = logging.StreamHandler(sys.stdout)
502 else: handler = logging.FileHandler(filename)
503 handler.setFormatter(formatter)
504 DBOBJ = logging.getLogger('urlgrabber')
505 DBOBJ.addHandler(handler)
506 DBOBJ.setLevel(level)
507 except (KeyError, ImportError, ValueError):
508 DBOBJ = None
509 set_logger(DBOBJ)
510
511_init_default_logger()
512########################################################################
513# END MODULE INITIALIZATION
514########################################################################
515
516
517
518class URLGrabError(IOError):
519 """
520 URLGrabError error codes:
521
522 URLGrabber error codes (0 -- 255)
523 0 - everything looks good (you should never see this)
524 1 - malformed url
525 2 - local file doesn't exist
526 3 - request for non-file local file (dir, etc)
527 4 - IOError on fetch
528 5 - OSError on fetch
529 6 - no content length header when we expected one
530 7 - HTTPException
531 8 - Exceeded read limit (for urlread)
532 9 - Requested byte range not satisfiable.
533 10 - Byte range requested, but range support unavailable
534 11 - Illegal reget mode
535 12 - Socket timeout
536 13 - malformed proxy url
537 14 - HTTPError (includes .code and .exception attributes)
538 15 - user abort
539
540 MirrorGroup error codes (256 -- 511)
541 256 - No more mirrors left to try
542
543 Custom (non-builtin) classes derived from MirrorGroup (512 -- 767)
544 [ this range reserved for application-specific error codes ]
545
546 Retry codes (< 0)
547 -1 - retry the download, unknown reason
548
549 Note: to test which group a code is in, you can simply do integer
550 division by 256: e.errno / 256
551
552 Negative codes are reserved for use by functions passed in to
553 retrygrab with checkfunc. The value -1 is built in as a generic
554 retry code and is already included in the retrycodes list.
555 Therefore, you can create a custom check function that simply
556 returns -1 and the fetch will be re-tried. For more customized
557 retries, you can use other negative number and include them in
558 retry-codes. This is nice for outputting useful messages about
559 what failed.
560
561 You can use these error codes like so:
562 try: urlgrab(url)
563 except URLGrabError, e:
564 if e.errno == 3: ...
565 # or
566 print e.strerror
567 # or simply
568 print e #### print '[Errno %i] %s' % (e.errno, e.strerror)
569 """
570 pass
571
572class CallbackObject:
573 """Container for returned callback data.
574
575 This is currently a dummy class into which urlgrabber can stuff
576 information for passing to callbacks. This way, the prototype for
577 all callbacks is the same, regardless of the data that will be
578 passed back. Any function that accepts a callback function as an
579 argument SHOULD document what it will define in this object.
580
581 It is possible that this class will have some greater
582 functionality in the future.
583 """
584 def __init__(self, **kwargs):
585 self.__dict__.update(kwargs)
586
587def urlgrab(url, filename=None, **kwargs):
588 """grab the file at <url> and make a local copy at <filename>
589 If filename is none, the basename of the url is used.
590 urlgrab returns the filename of the local file, which may be different
591 from the passed-in filename if the copy_local kwarg == 0.
592
593 See module documentation for a description of possible kwargs.
594 """
595 return default_grabber.urlgrab(url, filename, **kwargs)
596
597def urlopen(url, **kwargs):
598 """open the url and return a file object
599 If a progress object or throttle specifications exist, then
600 a special file object will be returned that supports them.
601 The file object can be treated like any other file object.
602
603 See module documentation for a description of possible kwargs.
604 """
605 return default_grabber.urlopen(url, **kwargs)
606
607def urlread(url, limit=None, **kwargs):
608 """read the url into a string, up to 'limit' bytes
609 If the limit is exceeded, an exception will be thrown. Note that urlread
610 is NOT intended to be used as a way of saying "I want the first N bytes"
611 but rather 'read the whole file into memory, but don't use too much'
612
613 See module documentation for a description of possible kwargs.
614 """
615 return default_grabber.urlread(url, limit, **kwargs)
616
617
618class URLParser:
619 """Process the URLs before passing them to urllib2.
620
621 This class does several things:
622
623 * add any prefix
624 * translate a "raw" file to a proper file: url
625 * handle any http or https auth that's encoded within the url
626 * quote the url
627
628 Only the "parse" method is called directly, and it calls sub-methods.
629
630 An instance of this class is held in the options object, which
631 means that it's easy to change the behavior by sub-classing and
632 passing the replacement in. It need only have a method like:
633
634 url, parts = urlparser.parse(url, opts)
635 """
636
637 def parse(self, url, opts):
638 """parse the url and return the (modified) url and its parts
639
640 Note: a raw file WILL be quoted when it's converted to a URL.
641 However, other urls (ones which come with a proper scheme) may
642 or may not be quoted according to opts.quote
643
644 opts.quote = 1 --> quote it
645 opts.quote = 0 --> do not quote it
646 opts.quote = None --> guess
647 """
648 quote = opts.quote
649
650 if opts.prefix:
651 url = self.add_prefix(url, opts.prefix)
652
653 parts = urlparse.urlparse(url)
654 (scheme, host, path, parm, query, frag) = parts
655
656 if not scheme or (len(scheme) == 1 and scheme in string.letters):
657 # if a scheme isn't specified, we guess that it's "file:"
658 if url[0] not in '/\\': url = os.path.abspath(url)
659 url = 'file:' + urllib.pathname2url(url)
660 parts = urlparse.urlparse(url)
661 quote = 0 # pathname2url quotes, so we won't do it again
662
663 if scheme in ['http', 'https']:
664 parts = self.process_http(parts)
665
666 if quote is None:
667 quote = self.guess_should_quote(parts)
668 if quote:
669 parts = self.quote(parts)
670
671 url = urlparse.urlunparse(parts)
672 return url, parts
673
674 def add_prefix(self, url, prefix):
675 if prefix[-1] == '/' or url[0] == '/':
676 url = prefix + url
677 else:
678 url = prefix + '/' + url
679 return url
680
681 def process_http(self, parts):
682 (scheme, host, path, parm, query, frag) = parts
683
684 if '@' in host and auth_handler:
685 try:
686 user_pass, host = host.split('@', 1)
687 if ':' in user_pass:
688 user, password = user_pass.split(':', 1)
689 except ValueError, e:
690 raise URLGrabError(1, _('Bad URL: %s') % url)
691 if DEBUG: DEBUG.info('adding HTTP auth: %s, XXXXXXXX', user)
692 auth_handler.add_password(None, host, user, password)
693
694 return (scheme, host, path, parm, query, frag)
695
696 def quote(self, parts):
697 """quote the URL
698
699 This method quotes ONLY the path part. If you need to quote
700 other parts, you should override this and pass in your derived
701 class. The other alternative is to quote other parts before
702 passing into urlgrabber.
703 """
704 (scheme, host, path, parm, query, frag) = parts
705 path = urllib.quote(path)
706 return (scheme, host, path, parm, query, frag)
707
708 hexvals = '0123456789ABCDEF'
709 def guess_should_quote(self, parts):
710 """
711 Guess whether we should quote a path. This amounts to
712 guessing whether it's already quoted.
713
714 find ' ' -> 1
715 find '%' -> 1
716 find '%XX' -> 0
717 else -> 1
718 """
719 (scheme, host, path, parm, query, frag) = parts
720 if ' ' in path:
721 return 1
722 ind = string.find(path, '%')
723 if ind > -1:
724 while ind > -1:
725 if len(path) < ind+3:
726 return 1
727 code = path[ind+1:ind+3].upper()
728 if code[0] not in self.hexvals or \
729 code[1] not in self.hexvals:
730 return 1
731 ind = string.find(path, '%', ind+1)
732 return 0
733 return 1
734
735class URLGrabberOptions:
736 """Class to ease kwargs handling."""
737
738 def __init__(self, delegate=None, **kwargs):
739 """Initialize URLGrabberOptions object.
740 Set default values for all options and then update options specified
741 in kwargs.
742 """
743 self.delegate = delegate
744 if delegate is None:
745 self._set_defaults()
746 self._set_attributes(**kwargs)
747
748 def __getattr__(self, name):
749 if self.delegate and hasattr(self.delegate, name):
750 return getattr(self.delegate, name)
751 raise AttributeError, name
752
753 def raw_throttle(self):
754 """Calculate raw throttle value from throttle and bandwidth
755 values.
756 """
757 if self.throttle <= 0:
758 return 0
759 elif type(self.throttle) == type(0):
760 return float(self.throttle)
761 else: # throttle is a float
762 return self.bandwidth * self.throttle
763
764 def derive(self, **kwargs):
765 """Create a derived URLGrabberOptions instance.
766 This method creates a new instance and overrides the
767 options specified in kwargs.
768 """
769 return URLGrabberOptions(delegate=self, **kwargs)
770
771 def _set_attributes(self, **kwargs):
772 """Update object attributes with those provided in kwargs."""
773 self.__dict__.update(kwargs)
774 if have_range and kwargs.has_key('range'):
775 # normalize the supplied range value
776 self.range = range_tuple_normalize(self.range)
777 if not self.reget in [None, 'simple', 'check_timestamp']:
778 raise URLGrabError(11, _('Illegal reget mode: %s') \
779 % (self.reget, ))
780
781 def _set_defaults(self):
782 """Set all options to their default values.
783 When adding new options, make sure a default is
784 provided here.
785 """
786 self.progress_obj = None
787 self.throttle = 1.0
788 self.bandwidth = 0
789 self.retry = None
790 self.retrycodes = [-1,2,4,5,6,7]
791 self.checkfunc = None
792 self.copy_local = 0
793 self.close_connection = 0
794 self.range = None
795 self.user_agent = 'urlgrabber/%s' % __version__
796 self.keepalive = 1
797 self.proxies = None
798 self.reget = None
799 self.failure_callback = None
800 self.interrupt_callback = None
801 self.prefix = None
802 self.opener = None
803 self.cache_openers = True
804 self.timeout = None
805 self.text = None
806 self.http_headers = None
807 self.ftp_headers = None
808 self.data = None
809 self.urlparser = URLParser()
810 self.quote = None
811 self.ssl_ca_cert = None
812 self.ssl_context = None
813
814class URLGrabber:
815 """Provides easy opening of URLs with a variety of options.
816
817 All options are specified as kwargs. Options may be specified when
818 the class is created and may be overridden on a per request basis.
819
820 New objects inherit default values from default_grabber.
821 """
822
823 def __init__(self, **kwargs):
824 self.opts = URLGrabberOptions(**kwargs)
825
826 def _retry(self, opts, func, *args):
827 tries = 0
828 while 1:
829 # there are only two ways out of this loop. The second has
830 # several "sub-ways"
831 # 1) via the return in the "try" block
832 # 2) by some exception being raised
833 # a) an excepton is raised that we don't "except"
834 # b) a callback raises ANY exception
835 # c) we're not retry-ing or have run out of retries
836 # d) the URLGrabError code is not in retrycodes
837 # beware of infinite loops :)
838 tries = tries + 1
839 exception = None
840 retrycode = None
841 callback = None
842 if DEBUG: DEBUG.info('attempt %i/%s: %s',
843 tries, opts.retry, args[0])
844 try:
845 r = apply(func, (opts,) + args, {})
846 if DEBUG: DEBUG.info('success')
847 return r
848 except URLGrabError, e:
849 exception = e
850 callback = opts.failure_callback
851 retrycode = e.errno
852 except KeyboardInterrupt, e:
853 exception = e
854 callback = opts.interrupt_callback
855
856 if DEBUG: DEBUG.info('exception: %s', exception)
857 if callback:
858 if DEBUG: DEBUG.info('calling callback: %s', callback)
859 cb_func, cb_args, cb_kwargs = self._make_callback(callback)
860 obj = CallbackObject(exception=exception, url=args[0],
861 tries=tries, retry=opts.retry)
862 cb_func(obj, *cb_args, **cb_kwargs)
863
864 if (opts.retry is None) or (tries == opts.retry):
865 if DEBUG: DEBUG.info('retries exceeded, re-raising')
866 raise
867
868 if (retrycode is not None) and (retrycode not in opts.retrycodes):
869 if DEBUG: DEBUG.info('retrycode (%i) not in list %s, re-raising',
870 retrycode, opts.retrycodes)
871 raise
872
873 def urlopen(self, url, **kwargs):
874 """open the url and return a file object
875 If a progress object or throttle value specified when this
876 object was created, then a special file object will be
877 returned that supports them. The file object can be treated
878 like any other file object.
879 """
880 opts = self.opts.derive(**kwargs)
881 (url,parts) = opts.urlparser.parse(url, opts)
882 def retryfunc(opts, url):
883 return URLGrabberFileObject(url, filename=None, opts=opts)
884 return self._retry(opts, retryfunc, url)
885
886 def urlgrab(self, url, filename=None, **kwargs):
887 """grab the file at <url> and make a local copy at <filename>
888 If filename is none, the basename of the url is used.
889 urlgrab returns the filename of the local file, which may be
890 different from the passed-in filename if copy_local == 0.
891 """
892 opts = self.opts.derive(**kwargs)
893 (url,parts) = opts.urlparser.parse(url, opts)
894 (scheme, host, path, parm, query, frag) = parts
895 if filename is None:
896 filename = os.path.basename( urllib.unquote(path) )
897 if scheme == 'file' and not opts.copy_local:
898 # just return the name of the local file - don't make a
899 # copy currently
900 path = urllib.url2pathname(path)
901 if host:
902 path = os.path.normpath('//' + host + path)
903 if not os.path.exists(path):
904 raise URLGrabError(2,
905 _('Local file does not exist: %s') % (path, ))
906 elif not os.path.isfile(path):
907 raise URLGrabError(3,
908 _('Not a normal file: %s') % (path, ))
909 elif not opts.range:
910 return path
911
912 def retryfunc(opts, url, filename):
913 fo = URLGrabberFileObject(url, filename, opts)
914 try:
915 fo._do_grab()
916 if not opts.checkfunc is None:
917 cb_func, cb_args, cb_kwargs = \
918 self._make_callback(opts.checkfunc)
919 obj = CallbackObject()
920 obj.filename = filename
921 obj.url = url
922 apply(cb_func, (obj, )+cb_args, cb_kwargs)
923 finally:
924 fo.close()
925 return filename
926
927 return self._retry(opts, retryfunc, url, filename)
928
929 def urlread(self, url, limit=None, **kwargs):
930 """read the url into a string, up to 'limit' bytes
931 If the limit is exceeded, an exception will be thrown. Note
932 that urlread is NOT intended to be used as a way of saying
933 "I want the first N bytes" but rather 'read the whole file
934 into memory, but don't use too much'
935 """
936 opts = self.opts.derive(**kwargs)
937 (url,parts) = opts.urlparser.parse(url, opts)
938 if limit is not None:
939 limit = limit + 1
940
941 def retryfunc(opts, url, limit):
942 fo = URLGrabberFileObject(url, filename=None, opts=opts)
943 s = ''
944 try:
945 # this is an unfortunate thing. Some file-like objects
946 # have a default "limit" of None, while the built-in (real)
947 # file objects have -1. They each break the other, so for
948 # now, we just force the default if necessary.
949 if limit is None: s = fo.read()
950 else: s = fo.read(limit)
951
952 if not opts.checkfunc is None:
953 cb_func, cb_args, cb_kwargs = \
954 self._make_callback(opts.checkfunc)
955 obj = CallbackObject()
956 obj.data = s
957 obj.url = url
958 apply(cb_func, (obj, )+cb_args, cb_kwargs)
959 finally:
960 fo.close()
961 return s
962
963 s = self._retry(opts, retryfunc, url, limit)
964 if limit and len(s) > limit:
965 raise URLGrabError(8,
966 _('Exceeded limit (%i): %s') % (limit, url))
967 return s
968
969 def _make_callback(self, callback_obj):
970 if callable(callback_obj):
971 return callback_obj, (), {}
972 else:
973 return callback_obj
974
975# create the default URLGrabber used by urlXXX functions.
976# NOTE: actual defaults are set in URLGrabberOptions
977default_grabber = URLGrabber()
978
979class URLGrabberFileObject:
980 """This is a file-object wrapper that supports progress objects
981 and throttling.
982
983 This exists to solve the following problem: lets say you want to
984 drop-in replace a normal open with urlopen. You want to use a
985 progress meter and/or throttling, but how do you do that without
986 rewriting your code? Answer: urlopen will return a wrapped file
987 object that does the progress meter and-or throttling internally.
988 """
989
990 def __init__(self, url, filename, opts):
991 self.url = url
992 self.filename = filename
993 self.opts = opts
994 self.fo = None
995 self._rbuf = ''
996 self._rbufsize = 1024*8
997 self._ttime = time.time()
998 self._tsize = 0
999 self._amount_read = 0
1000 self._opener = None
1001 self._do_open()
1002
1003 def __getattr__(self, name):
1004 """This effectively allows us to wrap at the instance level.
1005 Any attribute not found in _this_ object will be searched for
1006 in self.fo. This includes methods."""
1007 if hasattr(self.fo, name):
1008 return getattr(self.fo, name)
1009 raise AttributeError, name
1010
1011 def _get_opener(self):
1012 """Build a urllib2 OpenerDirector based on request options."""
1013 if self.opts.opener:
1014 return self.opts.opener
1015 elif self._opener is None:
1016 handlers = []
1017 need_keepalive_handler = (have_keepalive and self.opts.keepalive)
1018 need_range_handler = (range_handlers and \
1019 (self.opts.range or self.opts.reget))
1020 # if you specify a ProxyHandler when creating the opener
1021 # it _must_ come before all other handlers in the list or urllib2
1022 # chokes.
1023 if self.opts.proxies:
1024 handlers.append( CachedProxyHandler(self.opts.proxies) )
1025
1026 # -------------------------------------------------------
1027 # OK, these next few lines are a serious kludge to get
1028 # around what I think is a bug in python 2.2's
1029 # urllib2. The basic idea is that default handlers
1030 # get applied first. If you override one (like a
1031 # proxy handler), then the default gets pulled, but
1032 # the replacement goes on the end. In the case of
1033 # proxies, this means the normal handler picks it up
1034 # first and the proxy isn't used. Now, this probably
1035 # only happened with ftp or non-keepalive http, so not
1036 # many folks saw it. The simple approach to fixing it
1037 # is just to make sure you override the other
1038 # conflicting defaults as well. I would LOVE to see
1039 # these go way or be dealt with more elegantly. The
1040 # problem isn't there after 2.2. -MDS 2005/02/24
1041 if not need_keepalive_handler:
1042 handlers.append( urllib2.HTTPHandler() )
1043 if not need_range_handler:
1044 handlers.append( urllib2.FTPHandler() )
1045 # -------------------------------------------------------
1046
1047 ssl_factory = sslfactory.get_factory(self.opts.ssl_ca_cert,
1048 self.opts.ssl_context)
1049
1050 if need_keepalive_handler:
1051 handlers.append(HTTPHandler())
1052 handlers.append(HTTPSHandler(ssl_factory))
1053 if need_range_handler:
1054 handlers.extend( range_handlers )
1055 handlers.append( auth_handler )
1056 if self.opts.cache_openers:
1057 self._opener = CachedOpenerDirector(ssl_factory, *handlers)
1058 else:
1059 self._opener = ssl_factory.create_opener(*handlers)
1060 # OK, I don't like to do this, but otherwise, we end up with
1061 # TWO user-agent headers.
1062 self._opener.addheaders = []
1063 return self._opener
1064
1065 def _do_open(self):
1066 opener = self._get_opener()
1067
1068 req = urllib2.Request(self.url, self.opts.data) # build request object
1069 self._add_headers(req) # add misc headers that we need
1070 self._build_range(req) # take care of reget and byterange stuff
1071
1072 fo, hdr = self._make_request(req, opener)
1073 if self.reget_time and self.opts.reget == 'check_timestamp':
1074 # do this if we have a local file with known timestamp AND
1075 # we're in check_timestamp reget mode.
1076 fetch_again = 0
1077 try:
1078 modified_tuple = hdr.getdate_tz('last-modified')
1079 modified_stamp = rfc822.mktime_tz(modified_tuple)
1080 if modified_stamp > self.reget_time: fetch_again = 1
1081 except (TypeError,):
1082 fetch_again = 1
1083
1084 if fetch_again:
1085 # the server version is newer than the (incomplete) local
1086 # version, so we should abandon the version we're getting
1087 # and fetch the whole thing again.
1088 fo.close()
1089 self.opts.reget = None
1090 del req.headers['Range']
1091 self._build_range(req)
1092 fo, hdr = self._make_request(req, opener)
1093
1094 (scheme, host, path, parm, query, frag) = urlparse.urlparse(self.url)
1095 path = urllib.unquote(path)
1096 if not (self.opts.progress_obj or self.opts.raw_throttle() \
1097 or self.opts.timeout):
1098 # if we're not using the progress_obj, throttling, or timeout
1099 # we can get a performance boost by going directly to
1100 # the underlying fileobject for reads.
1101 self.read = fo.read
1102 if hasattr(fo, 'readline'):
1103 self.readline = fo.readline
1104 elif self.opts.progress_obj:
1105 try:
1106 length = int(hdr['Content-Length'])
1107 length = length + self._amount_read # Account for regets
1108 except (KeyError, ValueError, TypeError):
1109 length = None
1110
1111 self.opts.progress_obj.start(str(self.filename),
1112 urllib.unquote(self.url),
1113 os.path.basename(path),
1114 length, text=self.opts.text)
1115 self.opts.progress_obj.update(0)
1116 (self.fo, self.hdr) = (fo, hdr)
1117
1118 def _add_headers(self, req):
1119 if self.opts.user_agent:
1120 req.add_header('User-agent', self.opts.user_agent)
1121 try: req_type = req.get_type()
1122 except ValueError: req_type = None
1123 if self.opts.http_headers and req_type in ('http', 'https'):
1124 for h, v in self.opts.http_headers:
1125 req.add_header(h, v)
1126 if self.opts.ftp_headers and req_type == 'ftp':
1127 for h, v in self.opts.ftp_headers:
1128 req.add_header(h, v)
1129
1130 def _build_range(self, req):
1131 self.reget_time = None
1132 self.append = 0
1133 reget_length = 0
1134 rt = None
1135 if have_range and self.opts.reget and type(self.filename) == type(''):
1136 # we have reget turned on and we're dumping to a file
1137 try:
1138 s = os.stat(self.filename)
1139 except OSError:
1140 pass
1141 else:
1142 self.reget_time = s[ST_MTIME]
1143 reget_length = s[ST_SIZE]
1144
1145 # Set initial length when regetting
1146 self._amount_read = reget_length
1147
1148 rt = reget_length, ''
1149 self.append = 1
1150
1151 if self.opts.range:
1152 if not have_range:
1153 raise URLGrabError(10, _('Byte range requested but range '\
1154 'support unavailable'))
1155 rt = self.opts.range
1156 if rt[0]: rt = (rt[0] + reget_length, rt[1])
1157
1158 if rt:
1159 header = range_tuple_to_header(rt)
1160 if header: req.add_header('Range', header)
1161
1162 def _make_request(self, req, opener):
1163 try:
1164 if have_socket_timeout and self.opts.timeout:
1165 old_to = socket.getdefaulttimeout()
1166 socket.setdefaulttimeout(self.opts.timeout)
1167 try:
1168 fo = opener.open(req)
1169 finally:
1170 socket.setdefaulttimeout(old_to)
1171 else:
1172 fo = opener.open(req)
1173 hdr = fo.info()
1174 except ValueError, e:
1175 raise URLGrabError(1, _('Bad URL: %s') % (e, ))
1176 except RangeError, e:
1177 raise URLGrabError(9, str(e))
1178 except urllib2.HTTPError, e:
1179 new_e = URLGrabError(14, str(e))
1180 new_e.code = e.code
1181 new_e.exception = e
1182 raise new_e
1183 except IOError, e:
1184 if hasattr(e, 'reason') and have_socket_timeout and \
1185 isinstance(e.reason, TimeoutError):
1186 raise URLGrabError(12, _('Timeout: %s') % (e, ))
1187 else:
1188 raise URLGrabError(4, _('IOError: %s') % (e, ))
1189 except OSError, e:
1190 raise URLGrabError(5, _('OSError: %s') % (e, ))
1191 except HTTPException, e:
1192 raise URLGrabError(7, _('HTTP Exception (%s): %s') % \
1193 (e.__class__.__name__, e))
1194 else:
1195 return (fo, hdr)
1196
1197 def _do_grab(self):
1198 """dump the file to self.filename."""
1199 if self.append: new_fo = open(self.filename, 'ab')
1200 else: new_fo = open(self.filename, 'wb')
1201 bs = 1024*8
1202 size = 0
1203
1204 block = self.read(bs)
1205 size = size + len(block)
1206 while block:
1207 new_fo.write(block)
1208 block = self.read(bs)
1209 size = size + len(block)
1210
1211 new_fo.close()
1212 try:
1213 modified_tuple = self.hdr.getdate_tz('last-modified')
1214 modified_stamp = rfc822.mktime_tz(modified_tuple)
1215 os.utime(self.filename, (modified_stamp, modified_stamp))
1216 except (TypeError,), e: pass
1217
1218 return size
1219
1220 def _fill_buffer(self, amt=None):
1221 """fill the buffer to contain at least 'amt' bytes by reading
1222 from the underlying file object. If amt is None, then it will
1223 read until it gets nothing more. It updates the progress meter
1224 and throttles after every self._rbufsize bytes."""
1225 # the _rbuf test is only in this first 'if' for speed. It's not
1226 # logically necessary
1227 if self._rbuf and not amt is None:
1228 L = len(self._rbuf)
1229 if amt > L:
1230 amt = amt - L
1231 else:
1232 return
1233
1234 # if we've made it here, then we don't have enough in the buffer
1235 # and we need to read more.
1236
1237 buf = [self._rbuf]
1238 bufsize = len(self._rbuf)
1239 while amt is None or amt:
1240 # first, delay if necessary for throttling reasons
1241 if self.opts.raw_throttle():
1242 diff = self._tsize/self.opts.raw_throttle() - \
1243 (time.time() - self._ttime)
1244 if diff > 0: time.sleep(diff)
1245 self._ttime = time.time()
1246
1247 # now read some data, up to self._rbufsize
1248 if amt is None: readamount = self._rbufsize
1249 else: readamount = min(amt, self._rbufsize)
1250 try:
1251 new = self.fo.read(readamount)
1252 except socket.error, e:
1253 raise URLGrabError(4, _('Socket Error: %s') % (e, ))
1254 except TimeoutError, e:
1255 raise URLGrabError(12, _('Timeout: %s') % (e, ))
1256 except IOError, e:
1257 raise URLGrabError(4, _('IOError: %s') %(e,))
1258 newsize = len(new)
1259 if not newsize: break # no more to read
1260
1261 if amt: amt = amt - newsize
1262 buf.append(new)
1263 bufsize = bufsize + newsize
1264 self._tsize = newsize
1265 self._amount_read = self._amount_read + newsize
1266 if self.opts.progress_obj:
1267 self.opts.progress_obj.update(self._amount_read)
1268
1269 self._rbuf = string.join(buf, '')
1270 return
1271
1272 def read(self, amt=None):
1273 self._fill_buffer(amt)
1274 if amt is None:
1275 s, self._rbuf = self._rbuf, ''
1276 else:
1277 s, self._rbuf = self._rbuf[:amt], self._rbuf[amt:]
1278 return s
1279
1280 def readline(self, limit=-1):
1281 i = string.find(self._rbuf, '\n')
1282 while i < 0 and not (0 < limit <= len(self._rbuf)):
1283 L = len(self._rbuf)
1284 self._fill_buffer(L + self._rbufsize)
1285 if not len(self._rbuf) > L: break
1286 i = string.find(self._rbuf, '\n', L)
1287
1288 if i < 0: i = len(self._rbuf)
1289 else: i = i+1
1290 if 0 <= limit < len(self._rbuf): i = limit
1291
1292 s, self._rbuf = self._rbuf[:i], self._rbuf[i:]
1293 return s
1294
1295 def close(self):
1296 if self.opts.progress_obj:
1297 self.opts.progress_obj.end(self._amount_read)
1298 self.fo.close()
1299 if self.opts.close_connection:
1300 try: self.fo.close_connection()
1301 except: pass
1302
1303_handler_cache = []
1304def CachedOpenerDirector(ssl_factory = None, *handlers):
1305 for (cached_handlers, opener) in _handler_cache:
1306 if cached_handlers == handlers:
1307 for handler in opener.handlers:
1308 handler.add_parent(opener)
1309 return opener
1310 if not ssl_factory:
1311 ssl_factory = sslfactory.get_factory()
1312 opener = ssl_factory.create_opener(*handlers)
1313 _handler_cache.append( (handlers, opener) )
1314 return opener
1315
1316_proxy_cache = []
1317def CachedProxyHandler(proxies):
1318 for (pdict, handler) in _proxy_cache:
1319 if pdict == proxies:
1320 if DEBUG: DEBUG.debug('re-using proxy settings: %s', proxies)
1321 break
1322 else:
1323 for k, v in proxies.items():
1324 utype, url = urllib.splittype(v)
1325 host, other = urllib.splithost(url)
1326 if (utype is None) or (host is None):
1327 raise URLGrabError(13, _('Bad proxy URL: %s') % v)
1328
1329 if DEBUG: DEBUG.info('creating new proxy handler: %s', proxies)
1330 handler = urllib2.ProxyHandler(proxies)
1331 _proxy_cache.append( (proxies, handler) )
1332 return handler
1333
1334#####################################################################
1335# DEPRECATED FUNCTIONS
1336def set_throttle(new_throttle):
1337 """Deprecated. Use: default_grabber.throttle = new_throttle"""
1338 default_grabber.throttle = new_throttle
1339
1340def set_bandwidth(new_bandwidth):
1341 """Deprecated. Use: default_grabber.bandwidth = new_bandwidth"""
1342 default_grabber.bandwidth = new_bandwidth
1343
1344def set_progress_obj(new_progress_obj):
1345 """Deprecated. Use: default_grabber.progress_obj = new_progress_obj"""
1346 default_grabber.progress_obj = new_progress_obj
1347
1348def set_user_agent(new_user_agent):
1349 """Deprecated. Use: default_grabber.user_agent = new_user_agent"""
1350 default_grabber.user_agent = new_user_agent
1351
1352def retrygrab(url, filename=None, copy_local=0, close_connection=0,
1353 progress_obj=None, throttle=None, bandwidth=None,
1354 numtries=3, retrycodes=[-1,2,4,5,6,7], checkfunc=None):
1355 """Deprecated. Use: urlgrab() with the retry arg instead"""
1356 kwargs = {'copy_local' : copy_local,
1357 'close_connection' : close_connection,
1358 'progress_obj' : progress_obj,
1359 'throttle' : throttle,
1360 'bandwidth' : bandwidth,
1361 'retry' : numtries,
1362 'retrycodes' : retrycodes,
1363 'checkfunc' : checkfunc
1364 }
1365 return urlgrab(url, filename, **kwargs)
1366
1367
1368#####################################################################
1369# TESTING
1370def _main_test():
1371 import sys
1372 try: url, filename = sys.argv[1:3]
1373 except ValueError:
1374 print 'usage:', sys.argv[0], \
1375 '<url> <filename> [copy_local=0|1] [close_connection=0|1]'
1376 sys.exit()
1377
1378 kwargs = {}
1379 for a in sys.argv[3:]:
1380 k, v = string.split(a, '=', 1)
1381 kwargs[k] = int(v)
1382
1383 set_throttle(1.0)
1384 set_bandwidth(32 * 1024)
1385 print "throttle: %s, throttle bandwidth: %s B/s" % (default_grabber.throttle,
1386 default_grabber.bandwidth)
1387
1388 try: from progress import text_progress_meter
1389 except ImportError, e: pass
1390 else: kwargs['progress_obj'] = text_progress_meter()
1391
1392 try: name = apply(urlgrab, (url, filename), kwargs)
1393 except URLGrabError, e: print e
1394 else: print 'LOCAL FILE:', name
1395
1396
1397def _retry_test():
1398 import sys
1399 try: url, filename = sys.argv[1:3]
1400 except ValueError:
1401 print 'usage:', sys.argv[0], \
1402 '<url> <filename> [copy_local=0|1] [close_connection=0|1]'
1403 sys.exit()
1404
1405 kwargs = {}
1406 for a in sys.argv[3:]:
1407 k, v = string.split(a, '=', 1)
1408 kwargs[k] = int(v)
1409
1410 try: from progress import text_progress_meter
1411 except ImportError, e: pass
1412 else: kwargs['progress_obj'] = text_progress_meter()
1413
1414 def cfunc(filename, hello, there='foo'):
1415 print hello, there
1416 import random
1417 rnum = random.random()
1418 if rnum < .5:
1419 print 'forcing retry'
1420 raise URLGrabError(-1, 'forcing retry')
1421 if rnum < .75:
1422 print 'forcing failure'
1423 raise URLGrabError(-2, 'forcing immediate failure')
1424 print 'success'
1425 return
1426
1427 kwargs['checkfunc'] = (cfunc, ('hello',), {'there':'there'})
1428 try: name = apply(retrygrab, (url, filename), kwargs)
1429 except URLGrabError, e: print e
1430 else: print 'LOCAL FILE:', name
1431
1432def _file_object_test(filename=None):
1433 import random, cStringIO, sys
1434 if filename is None:
1435 filename = __file__
1436 print 'using file "%s" for comparisons' % filename
1437 fo = open(filename)
1438 s_input = fo.read()
1439 fo.close()
1440
1441 for testfunc in [_test_file_object_smallread,
1442 _test_file_object_readall,
1443 _test_file_object_readline,
1444 _test_file_object_readlines]:
1445 fo_input = cStringIO.StringIO(s_input)
1446 fo_output = cStringIO.StringIO()
1447 wrapper = URLGrabberFileObject(fo_input, None, 0)
1448 print 'testing %-30s ' % testfunc.__name__,
1449 testfunc(wrapper, fo_output)
1450 s_output = fo_output.getvalue()
1451 if s_output == s_input: print 'passed'
1452 else: print 'FAILED'
1453
1454def _test_file_object_smallread(wrapper, fo_output):
1455 while 1:
1456 s = wrapper.read(23)
1457 fo_output.write(s)
1458 if not s: return
1459
1460def _test_file_object_readall(wrapper, fo_output):
1461 s = wrapper.read()
1462 fo_output.write(s)
1463
1464def _test_file_object_readline(wrapper, fo_output):
1465 while 1:
1466 s = wrapper.readline()
1467 fo_output.write(s)
1468 if not s: return
1469
1470def _test_file_object_readlines(wrapper, fo_output):
1471 li = wrapper.readlines()
1472 fo_output.write(string.join(li, ''))
1473
1474if __name__ == '__main__':
1475 _main_test()
1476 _retry_test()
1477 _file_object_test('test')
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py
new file mode 100644
index 0000000000..71393e2b8d
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py
@@ -0,0 +1,617 @@
1# This library is free software; you can redistribute it and/or
2# modify it under the terms of the GNU Lesser General Public
3# License as published by the Free Software Foundation; either
4# version 2.1 of the License, or (at your option) any later version.
5#
6# This library is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9# Lesser General Public License for more details.
10#
11# You should have received a copy of the GNU Lesser General Public
12# License along with this library; if not, write to the
13# Free Software Foundation, Inc.,
14# 59 Temple Place, Suite 330,
15# Boston, MA 02111-1307 USA
16
17# This file is part of urlgrabber, a high-level cross-protocol url-grabber
18# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
19
20"""An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.
21
22>>> import urllib2
23>>> from keepalive import HTTPHandler
24>>> keepalive_handler = HTTPHandler()
25>>> opener = urllib2.build_opener(keepalive_handler)
26>>> urllib2.install_opener(opener)
27>>>
28>>> fo = urllib2.urlopen('http://www.python.org')
29
30If a connection to a given host is requested, and all of the existing
31connections are still in use, another connection will be opened. If
32the handler tries to use an existing connection but it fails in some
33way, it will be closed and removed from the pool.
34
35To remove the handler, simply re-run build_opener with no arguments, and
36install that opener.
37
38You can explicitly close connections by using the close_connection()
39method of the returned file-like object (described below) or you can
40use the handler methods:
41
42 close_connection(host)
43 close_all()
44 open_connections()
45
46NOTE: using the close_connection and close_all methods of the handler
47should be done with care when using multiple threads.
48 * there is nothing that prevents another thread from creating new
49 connections immediately after connections are closed
50 * no checks are done to prevent in-use connections from being closed
51
52>>> keepalive_handler.close_all()
53
54EXTRA ATTRIBUTES AND METHODS
55
56 Upon a status of 200, the object returned has a few additional
57 attributes and methods, which should not be used if you want to
58 remain consistent with the normal urllib2-returned objects:
59
60 close_connection() - close the connection to the host
61 readlines() - you know, readlines()
62 status - the return status (ie 404)
63 reason - english translation of status (ie 'File not found')
64
65 If you want the best of both worlds, use this inside an
66 AttributeError-catching try:
67
68 >>> try: status = fo.status
69 >>> except AttributeError: status = None
70
71 Unfortunately, these are ONLY there if status == 200, so it's not
72 easy to distinguish between non-200 responses. The reason is that
73 urllib2 tries to do clever things with error codes 301, 302, 401,
74 and 407, and it wraps the object upon return.
75
76 For python versions earlier than 2.4, you can avoid this fancy error
77 handling by setting the module-level global HANDLE_ERRORS to zero.
78 You see, prior to 2.4, it's the HTTP Handler's job to determine what
79 to handle specially, and what to just pass up. HANDLE_ERRORS == 0
80 means "pass everything up". In python 2.4, however, this job no
81 longer belongs to the HTTP Handler and is now done by a NEW handler,
82 HTTPErrorProcessor. Here's the bottom line:
83
84 python version < 2.4
85 HANDLE_ERRORS == 1 (default) pass up 200, treat the rest as
86 errors
87 HANDLE_ERRORS == 0 pass everything up, error processing is
88 left to the calling code
89 python version >= 2.4
90 HANDLE_ERRORS == 1 pass up 200, treat the rest as errors
91 HANDLE_ERRORS == 0 (default) pass everything up, let the
92 other handlers (specifically,
93 HTTPErrorProcessor) decide what to do
94
95 In practice, setting the variable either way makes little difference
96 in python 2.4, so for the most consistent behavior across versions,
97 you probably just want to use the defaults, which will give you
98 exceptions on errors.
99
100"""
101
102# $Id: keepalive.py,v 1.16 2006/09/22 00:58:05 mstenner Exp $
103
104import urllib2
105import httplib
106import socket
107import thread
108
109DEBUG = None
110
111import sslfactory
112
113import sys
114if sys.version_info < (2, 4): HANDLE_ERRORS = 1
115else: HANDLE_ERRORS = 0
116
117class ConnectionManager:
118 """
119 The connection manager must be able to:
120 * keep track of all existing
121 """
122 def __init__(self):
123 self._lock = thread.allocate_lock()
124 self._hostmap = {} # map hosts to a list of connections
125 self._connmap = {} # map connections to host
126 self._readymap = {} # map connection to ready state
127
128 def add(self, host, connection, ready):
129 self._lock.acquire()
130 try:
131 if not self._hostmap.has_key(host): self._hostmap[host] = []
132 self._hostmap[host].append(connection)
133 self._connmap[connection] = host
134 self._readymap[connection] = ready
135 finally:
136 self._lock.release()
137
138 def remove(self, connection):
139 self._lock.acquire()
140 try:
141 try:
142 host = self._connmap[connection]
143 except KeyError:
144 pass
145 else:
146 del self._connmap[connection]
147 del self._readymap[connection]
148 self._hostmap[host].remove(connection)
149 if not self._hostmap[host]: del self._hostmap[host]
150 finally:
151 self._lock.release()
152
153 def set_ready(self, connection, ready):
154 try: self._readymap[connection] = ready
155 except KeyError: pass
156
157 def get_ready_conn(self, host):
158 conn = None
159 self._lock.acquire()
160 try:
161 if self._hostmap.has_key(host):
162 for c in self._hostmap[host]:
163 if self._readymap[c]:
164 self._readymap[c] = 0
165 conn = c
166 break
167 finally:
168 self._lock.release()
169 return conn
170
171 def get_all(self, host=None):
172 if host:
173 return list(self._hostmap.get(host, []))
174 else:
175 return dict(self._hostmap)
176
177class KeepAliveHandler:
178 def __init__(self):
179 self._cm = ConnectionManager()
180
181 #### Connection Management
182 def open_connections(self):
183 """return a list of connected hosts and the number of connections
184 to each. [('foo.com:80', 2), ('bar.org', 1)]"""
185 return [(host, len(li)) for (host, li) in self._cm.get_all().items()]
186
187 def close_connection(self, host):
188 """close connection(s) to <host>
189 host is the host:port spec, as in 'www.cnn.com:8080' as passed in.
190 no error occurs if there is no connection to that host."""
191 for h in self._cm.get_all(host):
192 self._cm.remove(h)
193 h.close()
194
195 def close_all(self):
196 """close all open connections"""
197 for host, conns in self._cm.get_all().items():
198 for h in conns:
199 self._cm.remove(h)
200 h.close()
201
202 def _request_closed(self, request, host, connection):
203 """tells us that this request is now closed and the the
204 connection is ready for another request"""
205 self._cm.set_ready(connection, 1)
206
207 def _remove_connection(self, host, connection, close=0):
208 if close: connection.close()
209 self._cm.remove(connection)
210
211 #### Transaction Execution
212 def do_open(self, req):
213 host = req.get_host()
214 if not host:
215 raise urllib2.URLError('no host given')
216
217 try:
218 h = self._cm.get_ready_conn(host)
219 while h:
220 r = self._reuse_connection(h, req, host)
221
222 # if this response is non-None, then it worked and we're
223 # done. Break out, skipping the else block.
224 if r: break
225
226 # connection is bad - possibly closed by server
227 # discard it and ask for the next free connection
228 h.close()
229 self._cm.remove(h)
230 h = self._cm.get_ready_conn(host)
231 else:
232 # no (working) free connections were found. Create a new one.
233 h = self._get_connection(host)
234 if DEBUG: DEBUG.info("creating new connection to %s (%d)",
235 host, id(h))
236 self._cm.add(host, h, 0)
237 self._start_transaction(h, req)
238 r = h.getresponse()
239 except (socket.error, httplib.HTTPException), err:
240 raise urllib2.URLError(err)
241
242 # if not a persistent connection, don't try to reuse it
243 if r.will_close: self._cm.remove(h)
244
245 if DEBUG: DEBUG.info("STATUS: %s, %s", r.status, r.reason)
246 r._handler = self
247 r._host = host
248 r._url = req.get_full_url()
249 r._connection = h
250 r.code = r.status
251 r.headers = r.msg
252 r.msg = r.reason
253
254 if r.status == 200 or not HANDLE_ERRORS:
255 return r
256 else:
257 return self.parent.error('http', req, r,
258 r.status, r.msg, r.headers)
259
260 def _reuse_connection(self, h, req, host):
261 """start the transaction with a re-used connection
262 return a response object (r) upon success or None on failure.
263 This DOES not close or remove bad connections in cases where
264 it returns. However, if an unexpected exception occurs, it
265 will close and remove the connection before re-raising.
266 """
267 try:
268 self._start_transaction(h, req)
269 r = h.getresponse()
270 # note: just because we got something back doesn't mean it
271 # worked. We'll check the version below, too.
272 except (socket.error, httplib.HTTPException):
273 r = None
274 except:
275 # adding this block just in case we've missed
276 # something we will still raise the exception, but
277 # lets try and close the connection and remove it
278 # first. We previously got into a nasty loop
279 # where an exception was uncaught, and so the
280 # connection stayed open. On the next try, the
281 # same exception was raised, etc. The tradeoff is
282 # that it's now possible this call will raise
283 # a DIFFERENT exception
284 if DEBUG: DEBUG.error("unexpected exception - closing " + \
285 "connection to %s (%d)", host, id(h))
286 self._cm.remove(h)
287 h.close()
288 raise
289
290 if r is None or r.version == 9:
291 # httplib falls back to assuming HTTP 0.9 if it gets a
292 # bad header back. This is most likely to happen if
293 # the socket has been closed by the server since we
294 # last used the connection.
295 if DEBUG: DEBUG.info("failed to re-use connection to %s (%d)",
296 host, id(h))
297 r = None
298 else:
299 if DEBUG: DEBUG.info("re-using connection to %s (%d)", host, id(h))
300
301 return r
302
303 def _start_transaction(self, h, req):
304 try:
305 if req.has_data():
306 data = req.get_data()
307 h.putrequest('POST', req.get_selector())
308 if not req.headers.has_key('Content-type'):
309 h.putheader('Content-type',
310 'application/x-www-form-urlencoded')
311 if not req.headers.has_key('Content-length'):
312 h.putheader('Content-length', '%d' % len(data))
313 else:
314 h.putrequest('GET', req.get_selector())
315 except (socket.error, httplib.HTTPException), err:
316 raise urllib2.URLError(err)
317
318 for args in self.parent.addheaders:
319 h.putheader(*args)
320 for k, v in req.headers.items():
321 h.putheader(k, v)
322 h.endheaders()
323 if req.has_data():
324 h.send(data)
325
326 def _get_connection(self, host):
327 return NotImplementedError
328
329class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler):
330 def __init__(self):
331 KeepAliveHandler.__init__(self)
332
333 def http_open(self, req):
334 return self.do_open(req)
335
336 def _get_connection(self, host):
337 return HTTPConnection(host)
338
339class HTTPSHandler(KeepAliveHandler, urllib2.HTTPSHandler):
340 def __init__(self, ssl_factory=None):
341 KeepAliveHandler.__init__(self)
342 if not ssl_factory:
343 ssl_factory = sslfactory.get_factory()
344 self._ssl_factory = ssl_factory
345
346 def https_open(self, req):
347 return self.do_open(req)
348
349 def _get_connection(self, host):
350 return self._ssl_factory.get_https_connection(host)
351
352class HTTPResponse(httplib.HTTPResponse):
353 # we need to subclass HTTPResponse in order to
354 # 1) add readline() and readlines() methods
355 # 2) add close_connection() methods
356 # 3) add info() and geturl() methods
357
358 # in order to add readline(), read must be modified to deal with a
359 # buffer. example: readline must read a buffer and then spit back
360 # one line at a time. The only real alternative is to read one
361 # BYTE at a time (ick). Once something has been read, it can't be
362 # put back (ok, maybe it can, but that's even uglier than this),
363 # so if you THEN do a normal read, you must first take stuff from
364 # the buffer.
365
366 # the read method wraps the original to accomodate buffering,
367 # although read() never adds to the buffer.
368 # Both readline and readlines have been stolen with almost no
369 # modification from socket.py
370
371
372 def __init__(self, sock, debuglevel=0, strict=0, method=None):
373 if method: # the httplib in python 2.3 uses the method arg
374 httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
375 else: # 2.2 doesn't
376 httplib.HTTPResponse.__init__(self, sock, debuglevel)
377 self.fileno = sock.fileno
378 self.code = None
379 self._rbuf = ''
380 self._rbufsize = 8096
381 self._handler = None # inserted by the handler later
382 self._host = None # (same)
383 self._url = None # (same)
384 self._connection = None # (same)
385
386 _raw_read = httplib.HTTPResponse.read
387
388 def close(self):
389 if self.fp:
390 self.fp.close()
391 self.fp = None
392 if self._handler:
393 self._handler._request_closed(self, self._host,
394 self._connection)
395
396 def close_connection(self):
397 self._handler._remove_connection(self._host, self._connection, close=1)
398 self.close()
399
400 def info(self):
401 return self.headers
402
403 def geturl(self):
404 return self._url
405
406 def read(self, amt=None):
407 # the _rbuf test is only in this first if for speed. It's not
408 # logically necessary
409 if self._rbuf and not amt is None:
410 L = len(self._rbuf)
411 if amt > L:
412 amt -= L
413 else:
414 s = self._rbuf[:amt]
415 self._rbuf = self._rbuf[amt:]
416 return s
417
418 s = self._rbuf + self._raw_read(amt)
419 self._rbuf = ''
420 return s
421
422 def readline(self, limit=-1):
423 data = ""
424 i = self._rbuf.find('\n')
425 while i < 0 and not (0 < limit <= len(self._rbuf)):
426 new = self._raw_read(self._rbufsize)
427 if not new: break
428 i = new.find('\n')
429 if i >= 0: i = i + len(self._rbuf)
430 self._rbuf = self._rbuf + new
431 if i < 0: i = len(self._rbuf)
432 else: i = i+1
433 if 0 <= limit < len(self._rbuf): i = limit
434 data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
435 return data
436
437 def readlines(self, sizehint = 0):
438 total = 0
439 list = []
440 while 1:
441 line = self.readline()
442 if not line: break
443 list.append(line)
444 total += len(line)
445 if sizehint and total >= sizehint:
446 break
447 return list
448
449
450class HTTPConnection(httplib.HTTPConnection):
451 # use the modified response class
452 response_class = HTTPResponse
453
454class HTTPSConnection(httplib.HTTPSConnection):
455 response_class = HTTPResponse
456
457#########################################################################
458##### TEST FUNCTIONS
459#########################################################################
460
461def error_handler(url):
462 global HANDLE_ERRORS
463 orig = HANDLE_ERRORS
464 keepalive_handler = HTTPHandler()
465 opener = urllib2.build_opener(keepalive_handler)
466 urllib2.install_opener(opener)
467 pos = {0: 'off', 1: 'on'}
468 for i in (0, 1):
469 print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)
470 HANDLE_ERRORS = i
471 try:
472 fo = urllib2.urlopen(url)
473 foo = fo.read()
474 fo.close()
475 try: status, reason = fo.status, fo.reason
476 except AttributeError: status, reason = None, None
477 except IOError, e:
478 print " EXCEPTION: %s" % e
479 raise
480 else:
481 print " status = %s, reason = %s" % (status, reason)
482 HANDLE_ERRORS = orig
483 hosts = keepalive_handler.open_connections()
484 print "open connections:", hosts
485 keepalive_handler.close_all()
486
487def continuity(url):
488 import md5
489 format = '%25s: %s'
490
491 # first fetch the file with the normal http handler
492 opener = urllib2.build_opener()
493 urllib2.install_opener(opener)
494 fo = urllib2.urlopen(url)
495 foo = fo.read()
496 fo.close()
497 m = md5.new(foo)
498 print format % ('normal urllib', m.hexdigest())
499
500 # now install the keepalive handler and try again
501 opener = urllib2.build_opener(HTTPHandler())
502 urllib2.install_opener(opener)
503
504 fo = urllib2.urlopen(url)
505 foo = fo.read()
506 fo.close()
507 m = md5.new(foo)
508 print format % ('keepalive read', m.hexdigest())
509
510 fo = urllib2.urlopen(url)
511 foo = ''
512 while 1:
513 f = fo.readline()
514 if f: foo = foo + f
515 else: break
516 fo.close()
517 m = md5.new(foo)
518 print format % ('keepalive readline', m.hexdigest())
519
520def comp(N, url):
521 print ' making %i connections to:\n %s' % (N, url)
522
523 sys.stdout.write(' first using the normal urllib handlers')
524 # first use normal opener
525 opener = urllib2.build_opener()
526 urllib2.install_opener(opener)
527 t1 = fetch(N, url)
528 print ' TIME: %.3f s' % t1
529
530 sys.stdout.write(' now using the keepalive handler ')
531 # now install the keepalive handler and try again
532 opener = urllib2.build_opener(HTTPHandler())
533 urllib2.install_opener(opener)
534 t2 = fetch(N, url)
535 print ' TIME: %.3f s' % t2
536 print ' improvement factor: %.2f' % (t1/t2, )
537
538def fetch(N, url, delay=0):
539 import time
540 lens = []
541 starttime = time.time()
542 for i in range(N):
543 if delay and i > 0: time.sleep(delay)
544 fo = urllib2.urlopen(url)
545 foo = fo.read()
546 fo.close()
547 lens.append(len(foo))
548 diff = time.time() - starttime
549
550 j = 0
551 for i in lens[1:]:
552 j = j + 1
553 if not i == lens[0]:
554 print "WARNING: inconsistent length on read %i: %i" % (j, i)
555
556 return diff
557
558def test_timeout(url):
559 global DEBUG
560 dbbackup = DEBUG
561 class FakeLogger:
562 def debug(self, msg, *args): print msg % args
563 info = warning = error = debug
564 DEBUG = FakeLogger()
565 print " fetching the file to establish a connection"
566 fo = urllib2.urlopen(url)
567 data1 = fo.read()
568 fo.close()
569
570 i = 20
571 print " waiting %i seconds for the server to close the connection" % i
572 while i > 0:
573 sys.stdout.write('\r %2i' % i)
574 sys.stdout.flush()
575 time.sleep(1)
576 i -= 1
577 sys.stderr.write('\r')
578
579 print " fetching the file a second time"
580 fo = urllib2.urlopen(url)
581 data2 = fo.read()
582 fo.close()
583
584 if data1 == data2:
585 print ' data are identical'
586 else:
587 print ' ERROR: DATA DIFFER'
588
589 DEBUG = dbbackup
590
591
592def test(url, N=10):
593 print "checking error hander (do this on a non-200)"
594 try: error_handler(url)
595 except IOError, e:
596 print "exiting - exception will prevent further tests"
597 sys.exit()
598 print
599 print "performing continuity test (making sure stuff isn't corrupted)"
600 continuity(url)
601 print
602 print "performing speed comparison"
603 comp(N, url)
604 print
605 print "performing dropped-connection check"
606 test_timeout(url)
607
608if __name__ == '__main__':
609 import time
610 import sys
611 try:
612 N = int(sys.argv[1])
613 url = sys.argv[2]
614 except:
615 print "%s <integer> <url>" % sys.argv[0]
616 else:
617 test(url, N)
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py
new file mode 100644
index 0000000000..9664c6b5c5
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py
@@ -0,0 +1,458 @@
1# This library is free software; you can redistribute it and/or
2# modify it under the terms of the GNU Lesser General Public
3# License as published by the Free Software Foundation; either
4# version 2.1 of the License, or (at your option) any later version.
5#
6# This library is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9# Lesser General Public License for more details.
10#
11# You should have received a copy of the GNU Lesser General Public
12# License along with this library; if not, write to the
13# Free Software Foundation, Inc.,
14# 59 Temple Place, Suite 330,
15# Boston, MA 02111-1307 USA
16
17# This file is part of urlgrabber, a high-level cross-protocol url-grabber
18# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
19
20"""Module for downloading files from a pool of mirrors
21
22DESCRIPTION
23
24 This module provides support for downloading files from a pool of
25 mirrors with configurable failover policies. To a large extent, the
26 failover policy is chosen by using different classes derived from
27 the main class, MirrorGroup.
28
29 Instances of MirrorGroup (and cousins) act very much like URLGrabber
30 instances in that they have urlread, urlgrab, and urlopen methods.
31 They can therefore, be used in very similar ways.
32
33 from urlgrabber.grabber import URLGrabber
34 from urlgrabber.mirror import MirrorGroup
35 gr = URLGrabber()
36 mg = MirrorGroup(gr, ['http://foo.com/some/directory/',
37 'http://bar.org/maybe/somewhere/else/',
38 'ftp://baz.net/some/other/place/entirely/']
39 mg.urlgrab('relative/path.zip')
40
41 The assumption is that all mirrors are identical AFTER the base urls
42 specified, so that any mirror can be used to fetch any file.
43
44FAILOVER
45
46 The failover mechanism is designed to be customized by subclassing
47 from MirrorGroup to change the details of the behavior. In general,
48 the classes maintain a master mirror list and a "current mirror"
49 index. When a download is initiated, a copy of this list and index
50 is created for that download only. The specific failover policy
51 depends on the class used, and so is documented in the class
52 documentation. Note that ANY behavior of the class can be
53 overridden, so any failover policy at all is possible (although
54 you may need to change the interface in extreme cases).
55
56CUSTOMIZATION
57
58 Most customization of a MirrorGroup object is done at instantiation
59 time (or via subclassing). There are four major types of
60 customization:
61
62 1) Pass in a custom urlgrabber - The passed in urlgrabber will be
63 used (by default... see #2) for the grabs, so options to it
64 apply for the url-fetching
65
66 2) Custom mirror list - Mirror lists can simply be a list of
67 stings mirrors (as shown in the example above) but each can
68 also be a dict, allowing for more options. For example, the
69 first mirror in the list above could also have been:
70
71 {'mirror': 'http://foo.com/some/directory/',
72 'grabber': <a custom grabber to be used for this mirror>,
73 'kwargs': { <a dict of arguments passed to the grabber> }}
74
75 All mirrors are converted to this format internally. If
76 'grabber' is omitted, the default grabber will be used. If
77 kwargs are omitted, then (duh) they will not be used.
78
79 3) Pass keyword arguments when instantiating the mirror group.
80 See, for example, the failure_callback argument.
81
82 4) Finally, any kwargs passed in for the specific file (to the
83 urlgrab method, for example) will be folded in. The options
84 passed into the grabber's urlXXX methods will override any
85 options specified in a custom mirror dict.
86
87"""
88
89# $Id: mirror.py,v 1.14 2006/02/22 18:26:46 mstenner Exp $
90
91import random
92import thread # needed for locking to make this threadsafe
93
94from grabber import URLGrabError, CallbackObject, DEBUG
95
96try:
97 from i18n import _
98except ImportError, msg:
99 def _(st): return st
100
101class GrabRequest:
102 """This is a dummy class used to hold information about the specific
103 request. For example, a single file. By maintaining this information
104 separately, we can accomplish two things:
105
106 1) make it a little easier to be threadsafe
107 2) have request-specific parameters
108 """
109 pass
110
111class MirrorGroup:
112 """Base Mirror class
113
114 Instances of this class are built with a grabber object and a list
115 of mirrors. Then all calls to urlXXX should be passed relative urls.
116 The requested file will be searched for on the first mirror. If the
117 grabber raises an exception (possibly after some retries) then that
118 mirror will be removed from the list, and the next will be attempted.
119 If all mirrors are exhausted, then an exception will be raised.
120
121 MirrorGroup has the following failover policy:
122
123 * downloads begin with the first mirror
124
125 * by default (see default_action below) a failure (after retries)
126 causes it to increment the local AND master indices. Also,
127 the current mirror is removed from the local list (but NOT the
128 master list - the mirror can potentially be used for other
129 files)
130
131 * if the local list is ever exhausted, a URLGrabError will be
132 raised (errno=256, no more mirrors)
133
134 OPTIONS
135
136 In addition to the required arguments "grabber" and "mirrors",
137 MirrorGroup also takes the following optional arguments:
138
139 default_action
140
141 A dict that describes the actions to be taken upon failure
142 (after retries). default_action can contain any of the
143 following keys (shown here with their default values):
144
145 default_action = {'increment': 1,
146 'increment_master': 1,
147 'remove': 1,
148 'remove_master': 0,
149 'fail': 0}
150
151 In this context, 'increment' means "use the next mirror" and
152 'remove' means "never use this mirror again". The two
153 'master' values refer to the instance-level mirror list (used
154 for all files), whereas the non-master values refer to the
155 current download only.
156
157 The 'fail' option will cause immediate failure by re-raising
158 the exception and no further attempts to get the current
159 download.
160
161 This dict can be set at instantiation time,
162 mg = MirrorGroup(grabber, mirrors, default_action={'fail':1})
163 at method-execution time (only applies to current fetch),
164 filename = mg.urlgrab(url, default_action={'increment': 0})
165 or by returning an action dict from the failure_callback
166 return {'fail':0}
167 in increasing precedence.
168
169 If all three of these were done, the net result would be:
170 {'increment': 0, # set in method
171 'increment_master': 1, # class default
172 'remove': 1, # class default
173 'remove_master': 0, # class default
174 'fail': 0} # set at instantiation, reset
175 # from callback
176
177 failure_callback
178
179 this is a callback that will be called when a mirror "fails",
180 meaning the grabber raises some URLGrabError. If this is a
181 tuple, it is interpreted to be of the form (cb, args, kwargs)
182 where cb is the actual callable object (function, method,
183 etc). Otherwise, it is assumed to be the callable object
184 itself. The callback will be passed a grabber.CallbackObject
185 instance along with args and kwargs (if present). The following
186 attributes are defined withing the instance:
187
188 obj.exception = < exception that was raised >
189 obj.mirror = < the mirror that was tried >
190 obj.relative_url = < url relative to the mirror >
191 obj.url = < full url that failed >
192 # .url is just the combination of .mirror
193 # and .relative_url
194
195 The failure callback can return an action dict, as described
196 above.
197
198 Like default_action, the failure_callback can be set at
199 instantiation time or when the urlXXX method is called. In
200 the latter case, it applies only for that fetch.
201
202 The callback can re-raise the exception quite easily. For
203 example, this is a perfectly adequate callback function:
204
205 def callback(obj): raise obj.exception
206
207 WARNING: do not save the exception object (or the
208 CallbackObject instance). As they contain stack frame
209 references, they can lead to circular references.
210
211 Notes:
212 * The behavior can be customized by deriving and overriding the
213 'CONFIGURATION METHODS'
214 * The 'grabber' instance is kept as a reference, not copied.
215 Therefore, the grabber instance can be modified externally
216 and changes will take effect immediately.
217 """
218
219 # notes on thread-safety:
220
221 # A GrabRequest should never be shared by multiple threads because
222 # it's never saved inside the MG object and never returned outside it.
223 # therefore, it should be safe to access/modify grabrequest data
224 # without a lock. However, accessing the mirrors and _next attributes
225 # of the MG itself must be done when locked to prevent (for example)
226 # removal of the wrong mirror.
227
228 ##############################################################
229 # CONFIGURATION METHODS - intended to be overridden to
230 # customize behavior
231 def __init__(self, grabber, mirrors, **kwargs):
232 """Initialize the MirrorGroup object.
233
234 REQUIRED ARGUMENTS
235
236 grabber - URLGrabber instance
237 mirrors - a list of mirrors
238
239 OPTIONAL ARGUMENTS
240
241 failure_callback - callback to be used when a mirror fails
242 default_action - dict of failure actions
243
244 See the module-level and class level documentation for more
245 details.
246 """
247
248 # OVERRIDE IDEAS:
249 # shuffle the list to randomize order
250 self.grabber = grabber
251 self.mirrors = self._parse_mirrors(mirrors)
252 self._next = 0
253 self._lock = thread.allocate_lock()
254 self.default_action = None
255 self._process_kwargs(kwargs)
256
257 # if these values are found in **kwargs passed to one of the urlXXX
258 # methods, they will be stripped before getting passed on to the
259 # grabber
260 options = ['default_action', 'failure_callback']
261
262 def _process_kwargs(self, kwargs):
263 self.failure_callback = kwargs.get('failure_callback')
264 self.default_action = kwargs.get('default_action')
265
266 def _parse_mirrors(self, mirrors):
267 parsed_mirrors = []
268 for m in mirrors:
269 if type(m) == type(''): m = {'mirror': m}
270 parsed_mirrors.append(m)
271 return parsed_mirrors
272
273 def _load_gr(self, gr):
274 # OVERRIDE IDEAS:
275 # shuffle gr list
276 self._lock.acquire()
277 gr.mirrors = list(self.mirrors)
278 gr._next = self._next
279 self._lock.release()
280
281 def _get_mirror(self, gr):
282 # OVERRIDE IDEAS:
283 # return a random mirror so that multiple mirrors get used
284 # even without failures.
285 if not gr.mirrors:
286 raise URLGrabError(256, _('No more mirrors to try.'))
287 return gr.mirrors[gr._next]
288
289 def _failure(self, gr, cb_obj):
290 # OVERRIDE IDEAS:
291 # inspect the error - remove=1 for 404, remove=2 for connection
292 # refused, etc. (this can also be done via
293 # the callback)
294 cb = gr.kw.get('failure_callback') or self.failure_callback
295 if cb:
296 if type(cb) == type( () ):
297 cb, args, kwargs = cb
298 else:
299 args, kwargs = (), {}
300 action = cb(cb_obj, *args, **kwargs) or {}
301 else:
302 action = {}
303 # XXXX - decide - there are two ways to do this
304 # the first is action-overriding as a whole - use the entire action
305 # or fall back on module level defaults
306 #action = action or gr.kw.get('default_action') or self.default_action
307 # the other is to fall through for each element in the action dict
308 a = dict(self.default_action or {})
309 a.update(gr.kw.get('default_action', {}))
310 a.update(action)
311 action = a
312 self.increment_mirror(gr, action)
313 if action and action.get('fail', 0): raise
314
315 def increment_mirror(self, gr, action={}):
316 """Tell the mirror object increment the mirror index
317
318 This increments the mirror index, which amounts to telling the
319 mirror object to use a different mirror (for this and future
320 downloads).
321
322 This is a SEMI-public method. It will be called internally,
323 and you may never need to call it. However, it is provided
324 (and is made public) so that the calling program can increment
325 the mirror choice for methods like urlopen. For example, with
326 urlopen, there's no good way for the mirror group to know that
327 an error occurs mid-download (it's already returned and given
328 you the file object).
329
330 remove --- can have several values
331 0 do not remove the mirror from the list
332 1 remove the mirror for this download only
333 2 remove the mirror permanently
334
335 beware of remove=0 as it can lead to infinite loops
336 """
337 badmirror = gr.mirrors[gr._next]
338
339 self._lock.acquire()
340 try:
341 ind = self.mirrors.index(badmirror)
342 except ValueError:
343 pass
344 else:
345 if action.get('remove_master', 0):
346 del self.mirrors[ind]
347 elif self._next == ind and action.get('increment_master', 1):
348 self._next += 1
349 if self._next >= len(self.mirrors): self._next = 0
350 self._lock.release()
351
352 if action.get('remove', 1):
353 del gr.mirrors[gr._next]
354 elif action.get('increment', 1):
355 gr._next += 1
356 if gr._next >= len(gr.mirrors): gr._next = 0
357
358 if DEBUG:
359 grm = [m['mirror'] for m in gr.mirrors]
360 DEBUG.info('GR mirrors: [%s] %i', ' '.join(grm), gr._next)
361 selfm = [m['mirror'] for m in self.mirrors]
362 DEBUG.info('MAIN mirrors: [%s] %i', ' '.join(selfm), self._next)
363
364 #####################################################################
365 # NON-CONFIGURATION METHODS
366 # these methods are designed to be largely workhorse methods that
367 # are not intended to be overridden. That doesn't mean you can't;
368 # if you want to, feel free, but most things can be done by
369 # by overriding the configuration methods :)
370
371 def _join_url(self, base_url, rel_url):
372 if base_url.endswith('/') or rel_url.startswith('/'):
373 return base_url + rel_url
374 else:
375 return base_url + '/' + rel_url
376
377 def _mirror_try(self, func, url, kw):
378 gr = GrabRequest()
379 gr.func = func
380 gr.url = url
381 gr.kw = dict(kw)
382 self._load_gr(gr)
383
384 for k in self.options:
385 try: del kw[k]
386 except KeyError: pass
387
388 while 1:
389 mirrorchoice = self._get_mirror(gr)
390 fullurl = self._join_url(mirrorchoice['mirror'], gr.url)
391 kwargs = dict(mirrorchoice.get('kwargs', {}))
392 kwargs.update(kw)
393 grabber = mirrorchoice.get('grabber') or self.grabber
394 func_ref = getattr(grabber, func)
395 if DEBUG: DEBUG.info('MIRROR: trying %s -> %s', url, fullurl)
396 try:
397 return func_ref( *(fullurl,), **kwargs )
398 except URLGrabError, e:
399 if DEBUG: DEBUG.info('MIRROR: failed')
400 obj = CallbackObject()
401 obj.exception = e
402 obj.mirror = mirrorchoice['mirror']
403 obj.relative_url = gr.url
404 obj.url = fullurl
405 self._failure(gr, obj)
406
407 def urlgrab(self, url, filename=None, **kwargs):
408 kw = dict(kwargs)
409 kw['filename'] = filename
410 func = 'urlgrab'
411 return self._mirror_try(func, url, kw)
412
413 def urlopen(self, url, **kwargs):
414 kw = dict(kwargs)
415 func = 'urlopen'
416 return self._mirror_try(func, url, kw)
417
418 def urlread(self, url, limit=None, **kwargs):
419 kw = dict(kwargs)
420 kw['limit'] = limit
421 func = 'urlread'
422 return self._mirror_try(func, url, kw)
423
424
425class MGRandomStart(MirrorGroup):
426 """A mirror group that starts at a random mirror in the list.
427
428 This behavior of this class is identical to MirrorGroup, except that
429 it starts at a random location in the mirror list.
430 """
431
432 def __init__(self, grabber, mirrors, **kwargs):
433 """Initialize the object
434
435 The arguments for intialization are the same as for MirrorGroup
436 """
437 MirrorGroup.__init__(self, grabber, mirrors, **kwargs)
438 self._next = random.randrange(len(mirrors))
439
440class MGRandomOrder(MirrorGroup):
441 """A mirror group that uses mirrors in a random order.
442
443 This behavior of this class is identical to MirrorGroup, except that
444 it uses the mirrors in a random order. Note that the order is set at
445 initialization time and fixed thereafter. That is, it does not pick a
446 random mirror after each failure.
447 """
448
449 def __init__(self, grabber, mirrors, **kwargs):
450 """Initialize the object
451
452 The arguments for intialization are the same as for MirrorGroup
453 """
454 MirrorGroup.__init__(self, grabber, mirrors, **kwargs)
455 random.shuffle(self.mirrors)
456
457if __name__ == '__main__':
458 pass
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py
new file mode 100644
index 0000000000..02db524e76
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py
@@ -0,0 +1,530 @@
1# This library is free software; you can redistribute it and/or
2# modify it under the terms of the GNU Lesser General Public
3# License as published by the Free Software Foundation; either
4# version 2.1 of the License, or (at your option) any later version.
5#
6# This library is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9# Lesser General Public License for more details.
10#
11# You should have received a copy of the GNU Lesser General Public
12# License along with this library; if not, write to the
13# Free Software Foundation, Inc.,
14# 59 Temple Place, Suite 330,
15# Boston, MA 02111-1307 USA
16
17# This file is part of urlgrabber, a high-level cross-protocol url-grabber
18# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
19
20# $Id: progress.py,v 1.7 2005/08/19 21:59:07 mstenner Exp $
21
22import sys
23import time
24import math
25import thread
26
27class BaseMeter:
28 def __init__(self):
29 self.update_period = 0.3 # seconds
30
31 self.filename = None
32 self.url = None
33 self.basename = None
34 self.text = None
35 self.size = None
36 self.start_time = None
37 self.last_amount_read = 0
38 self.last_update_time = None
39 self.re = RateEstimator()
40
41 def start(self, filename=None, url=None, basename=None,
42 size=None, now=None, text=None):
43 self.filename = filename
44 self.url = url
45 self.basename = basename
46 self.text = text
47
48 #size = None ######### TESTING
49 self.size = size
50 if not size is None: self.fsize = format_number(size) + 'B'
51
52 if now is None: now = time.time()
53 self.start_time = now
54 self.re.start(size, now)
55 self.last_amount_read = 0
56 self.last_update_time = now
57 self._do_start(now)
58
59 def _do_start(self, now=None):
60 pass
61
62 def update(self, amount_read, now=None):
63 # for a real gui, you probably want to override and put a call
64 # to your mainloop iteration function here
65 if now is None: now = time.time()
66 if (now >= self.last_update_time + self.update_period) or \
67 not self.last_update_time:
68 self.re.update(amount_read, now)
69 self.last_amount_read = amount_read
70 self.last_update_time = now
71 self._do_update(amount_read, now)
72
73 def _do_update(self, amount_read, now=None):
74 pass
75
76 def end(self, amount_read, now=None):
77 if now is None: now = time.time()
78 self.re.update(amount_read, now)
79 self.last_amount_read = amount_read
80 self.last_update_time = now
81 self._do_end(amount_read, now)
82
83 def _do_end(self, amount_read, now=None):
84 pass
85
86class TextMeter(BaseMeter):
87 def __init__(self, fo=sys.stderr):
88 BaseMeter.__init__(self)
89 self.fo = fo
90
91 def _do_update(self, amount_read, now=None):
92 etime = self.re.elapsed_time()
93 fetime = format_time(etime)
94 fread = format_number(amount_read)
95 #self.size = None
96 if self.text is not None:
97 text = self.text
98 else:
99 text = self.basename
100 if self.size is None:
101 out = '\r%-60.60s %5sB %s ' % \
102 (text, fread, fetime)
103 else:
104 rtime = self.re.remaining_time()
105 frtime = format_time(rtime)
106 frac = self.re.fraction_read()
107 bar = '='*int(25 * frac)
108
109 out = '\r%-25.25s %3i%% |%-25.25s| %5sB %8s ETA ' % \
110 (text, frac*100, bar, fread, frtime)
111
112 self.fo.write(out)
113 self.fo.flush()
114
115 def _do_end(self, amount_read, now=None):
116 total_time = format_time(self.re.elapsed_time())
117 total_size = format_number(amount_read)
118 if self.text is not None:
119 text = self.text
120 else:
121 text = self.basename
122 if self.size is None:
123 out = '\r%-60.60s %5sB %s ' % \
124 (text, total_size, total_time)
125 else:
126 bar = '='*25
127 out = '\r%-25.25s %3i%% |%-25.25s| %5sB %8s ' % \
128 (text, 100, bar, total_size, total_time)
129 self.fo.write(out + '\n')
130 self.fo.flush()
131
132text_progress_meter = TextMeter
133
134class MultiFileHelper(BaseMeter):
135 def __init__(self, master):
136 BaseMeter.__init__(self)
137 self.master = master
138
139 def _do_start(self, now):
140 self.master.start_meter(self, now)
141
142 def _do_update(self, amount_read, now):
143 # elapsed time since last update
144 self.master.update_meter(self, now)
145
146 def _do_end(self, amount_read, now):
147 self.ftotal_time = format_time(now - self.start_time)
148 self.ftotal_size = format_number(self.last_amount_read)
149 self.master.end_meter(self, now)
150
151 def failure(self, message, now=None):
152 self.master.failure_meter(self, message, now)
153
154 def message(self, message):
155 self.master.message_meter(self, message)
156
157class MultiFileMeter:
158 helperclass = MultiFileHelper
159 def __init__(self):
160 self.meters = []
161 self.in_progress_meters = []
162 self._lock = thread.allocate_lock()
163 self.update_period = 0.3 # seconds
164
165 self.numfiles = None
166 self.finished_files = 0
167 self.failed_files = 0
168 self.open_files = 0
169 self.total_size = None
170 self.failed_size = 0
171 self.start_time = None
172 self.finished_file_size = 0
173 self.last_update_time = None
174 self.re = RateEstimator()
175
176 def start(self, numfiles=None, total_size=None, now=None):
177 if now is None: now = time.time()
178 self.numfiles = numfiles
179 self.finished_files = 0
180 self.failed_files = 0
181 self.open_files = 0
182 self.total_size = total_size
183 self.failed_size = 0
184 self.start_time = now
185 self.finished_file_size = 0
186 self.last_update_time = now
187 self.re.start(total_size, now)
188 self._do_start(now)
189
190 def _do_start(self, now):
191 pass
192
193 def end(self, now=None):
194 if now is None: now = time.time()
195 self._do_end(now)
196
197 def _do_end(self, now):
198 pass
199
200 def lock(self): self._lock.acquire()
201 def unlock(self): self._lock.release()
202
203 ###########################################################
204 # child meter creation and destruction
205 def newMeter(self):
206 newmeter = self.helperclass(self)
207 self.meters.append(newmeter)
208 return newmeter
209
210 def removeMeter(self, meter):
211 self.meters.remove(meter)
212
213 ###########################################################
214 # child functions - these should only be called by helpers
215 def start_meter(self, meter, now):
216 if not meter in self.meters:
217 raise ValueError('attempt to use orphaned meter')
218 self._lock.acquire()
219 try:
220 if not meter in self.in_progress_meters:
221 self.in_progress_meters.append(meter)
222 self.open_files += 1
223 finally:
224 self._lock.release()
225 self._do_start_meter(meter, now)
226
227 def _do_start_meter(self, meter, now):
228 pass
229
230 def update_meter(self, meter, now):
231 if not meter in self.meters:
232 raise ValueError('attempt to use orphaned meter')
233 if (now >= self.last_update_time + self.update_period) or \
234 not self.last_update_time:
235 self.re.update(self._amount_read(), now)
236 self.last_update_time = now
237 self._do_update_meter(meter, now)
238
239 def _do_update_meter(self, meter, now):
240 pass
241
242 def end_meter(self, meter, now):
243 if not meter in self.meters:
244 raise ValueError('attempt to use orphaned meter')
245 self._lock.acquire()
246 try:
247 try: self.in_progress_meters.remove(meter)
248 except ValueError: pass
249 self.open_files -= 1
250 self.finished_files += 1
251 self.finished_file_size += meter.last_amount_read
252 finally:
253 self._lock.release()
254 self._do_end_meter(meter, now)
255
256 def _do_end_meter(self, meter, now):
257 pass
258
259 def failure_meter(self, meter, message, now):
260 if not meter in self.meters:
261 raise ValueError('attempt to use orphaned meter')
262 self._lock.acquire()
263 try:
264 try: self.in_progress_meters.remove(meter)
265 except ValueError: pass
266 self.open_files -= 1
267 self.failed_files += 1
268 if meter.size and self.failed_size is not None:
269 self.failed_size += meter.size
270 else:
271 self.failed_size = None
272 finally:
273 self._lock.release()
274 self._do_failure_meter(meter, message, now)
275
276 def _do_failure_meter(self, meter, message, now):
277 pass
278
279 def message_meter(self, meter, message):
280 pass
281
282 ########################################################
283 # internal functions
284 def _amount_read(self):
285 tot = self.finished_file_size
286 for m in self.in_progress_meters:
287 tot += m.last_amount_read
288 return tot
289
290
291class TextMultiFileMeter(MultiFileMeter):
292 def __init__(self, fo=sys.stderr):
293 self.fo = fo
294 MultiFileMeter.__init__(self)
295
296 # files: ###/### ###% data: ######/###### ###% time: ##:##:##/##:##:##
297 def _do_update_meter(self, meter, now):
298 self._lock.acquire()
299 try:
300 format = "files: %3i/%-3i %3i%% data: %6.6s/%-6.6s %3i%% " \
301 "time: %8.8s/%8.8s"
302 df = self.finished_files
303 tf = self.numfiles or 1
304 pf = 100 * float(df)/tf + 0.49
305 dd = self.re.last_amount_read
306 td = self.total_size
307 pd = 100 * (self.re.fraction_read() or 0) + 0.49
308 dt = self.re.elapsed_time()
309 rt = self.re.remaining_time()
310 if rt is None: tt = None
311 else: tt = dt + rt
312
313 fdd = format_number(dd) + 'B'
314 ftd = format_number(td) + 'B'
315 fdt = format_time(dt, 1)
316 ftt = format_time(tt, 1)
317
318 out = '%-79.79s' % (format % (df, tf, pf, fdd, ftd, pd, fdt, ftt))
319 self.fo.write('\r' + out)
320 self.fo.flush()
321 finally:
322 self._lock.release()
323
324 def _do_end_meter(self, meter, now):
325 self._lock.acquire()
326 try:
327 format = "%-30.30s %6.6s %8.8s %9.9s"
328 fn = meter.basename
329 size = meter.last_amount_read
330 fsize = format_number(size) + 'B'
331 et = meter.re.elapsed_time()
332 fet = format_time(et, 1)
333 frate = format_number(size / et) + 'B/s'
334
335 out = '%-79.79s' % (format % (fn, fsize, fet, frate))
336 self.fo.write('\r' + out + '\n')
337 finally:
338 self._lock.release()
339 self._do_update_meter(meter, now)
340
341 def _do_failure_meter(self, meter, message, now):
342 self._lock.acquire()
343 try:
344 format = "%-30.30s %6.6s %s"
345 fn = meter.basename
346 if type(message) in (type(''), type(u'')):
347 message = message.splitlines()
348 if not message: message = ['']
349 out = '%-79s' % (format % (fn, 'FAILED', message[0] or ''))
350 self.fo.write('\r' + out + '\n')
351 for m in message[1:]: self.fo.write(' ' + m + '\n')
352 self._lock.release()
353 finally:
354 self._do_update_meter(meter, now)
355
356 def message_meter(self, meter, message):
357 self._lock.acquire()
358 try:
359 pass
360 finally:
361 self._lock.release()
362
363 def _do_end(self, now):
364 self._do_update_meter(None, now)
365 self._lock.acquire()
366 try:
367 self.fo.write('\n')
368 self.fo.flush()
369 finally:
370 self._lock.release()
371
372######################################################################
373# support classes and functions
374
375class RateEstimator:
376 def __init__(self, timescale=5.0):
377 self.timescale = timescale
378
379 def start(self, total=None, now=None):
380 if now is None: now = time.time()
381 self.total = total
382 self.start_time = now
383 self.last_update_time = now
384 self.last_amount_read = 0
385 self.ave_rate = None
386
387 def update(self, amount_read, now=None):
388 if now is None: now = time.time()
389 if amount_read == 0:
390 # if we just started this file, all bets are off
391 self.last_update_time = now
392 self.last_amount_read = 0
393 self.ave_rate = None
394 return
395
396 #print 'times', now, self.last_update_time
397 time_diff = now - self.last_update_time
398 read_diff = amount_read - self.last_amount_read
399 self.last_update_time = now
400 self.last_amount_read = amount_read
401 self.ave_rate = self._temporal_rolling_ave(\
402 time_diff, read_diff, self.ave_rate, self.timescale)
403 #print 'results', time_diff, read_diff, self.ave_rate
404
405 #####################################################################
406 # result methods
407 def average_rate(self):
408 "get the average transfer rate (in bytes/second)"
409 return self.ave_rate
410
411 def elapsed_time(self):
412 "the time between the start of the transfer and the most recent update"
413 return self.last_update_time - self.start_time
414
415 def remaining_time(self):
416 "estimated time remaining"
417 if not self.ave_rate or not self.total: return None
418 return (self.total - self.last_amount_read) / self.ave_rate
419
420 def fraction_read(self):
421 """the fraction of the data that has been read
422 (can be None for unknown transfer size)"""
423 if self.total is None: return None
424 elif self.total == 0: return 1.0
425 else: return float(self.last_amount_read)/self.total
426
427 #########################################################################
428 # support methods
429 def _temporal_rolling_ave(self, time_diff, read_diff, last_ave, timescale):
430 """a temporal rolling average performs smooth averaging even when
431 updates come at irregular intervals. This is performed by scaling
432 the "epsilon" according to the time since the last update.
433 Specifically, epsilon = time_diff / timescale
434
435 As a general rule, the average will take on a completely new value
436 after 'timescale' seconds."""
437 epsilon = time_diff / timescale
438 if epsilon > 1: epsilon = 1.0
439 return self._rolling_ave(time_diff, read_diff, last_ave, epsilon)
440
441 def _rolling_ave(self, time_diff, read_diff, last_ave, epsilon):
442 """perform a "rolling average" iteration
443 a rolling average "folds" new data into an existing average with
444 some weight, epsilon. epsilon must be between 0.0 and 1.0 (inclusive)
445 a value of 0.0 means only the old value (initial value) counts,
446 and a value of 1.0 means only the newest value is considered."""
447
448 try:
449 recent_rate = read_diff / time_diff
450 except ZeroDivisionError:
451 recent_rate = None
452 if last_ave is None: return recent_rate
453 elif recent_rate is None: return last_ave
454
455 # at this point, both last_ave and recent_rate are numbers
456 return epsilon * recent_rate + (1 - epsilon) * last_ave
457
458 def _round_remaining_time(self, rt, start_time=15.0):
459 """round the remaining time, depending on its size
460 If rt is between n*start_time and (n+1)*start_time round downward
461 to the nearest multiple of n (for any counting number n).
462 If rt < start_time, round down to the nearest 1.
463 For example (for start_time = 15.0):
464 2.7 -> 2.0
465 25.2 -> 25.0
466 26.4 -> 26.0
467 35.3 -> 34.0
468 63.6 -> 60.0
469 """
470
471 if rt < 0: return 0.0
472 shift = int(math.log(rt/start_time)/math.log(2))
473 rt = int(rt)
474 if shift <= 0: return rt
475 return float(int(rt) >> shift << shift)
476
477
478def format_time(seconds, use_hours=0):
479 if seconds is None or seconds < 0:
480 if use_hours: return '--:--:--'
481 else: return '--:--'
482 else:
483 seconds = int(seconds)
484 minutes = seconds / 60
485 seconds = seconds % 60
486 if use_hours:
487 hours = minutes / 60
488 minutes = minutes % 60
489 return '%02i:%02i:%02i' % (hours, minutes, seconds)
490 else:
491 return '%02i:%02i' % (minutes, seconds)
492
493def format_number(number, SI=0, space=' '):
494 """Turn numbers into human-readable metric-like numbers"""
495 symbols = ['', # (none)
496 'k', # kilo
497 'M', # mega
498 'G', # giga
499 'T', # tera
500 'P', # peta
501 'E', # exa
502 'Z', # zetta
503 'Y'] # yotta
504
505 if SI: step = 1000.0
506 else: step = 1024.0
507
508 thresh = 999
509 depth = 0
510 max_depth = len(symbols) - 1
511
512 # we want numbers between 0 and thresh, but don't exceed the length
513 # of our list. In that event, the formatting will be screwed up,
514 # but it'll still show the right number.
515 while number > thresh and depth < max_depth:
516 depth = depth + 1
517 number = number / step
518
519 if type(number) == type(1) or type(number) == type(1L):
520 # it's an int or a long, which means it didn't get divided,
521 # which means it's already short enough
522 format = '%i%s%s'
523 elif number < 9.95:
524 # must use 9.95 for proper sizing. For example, 9.99 will be
525 # rounded to 10.0 with the .1f format string (which is too long)
526 format = '%.1f%s%s'
527 else:
528 format = '%.0f%s%s'
529
530 return(format % (float(number or 0), space, symbols[depth]))
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py
new file mode 100644
index 0000000000..07848dac7c
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py
@@ -0,0 +1,90 @@
1# This library is free software; you can redistribute it and/or
2# modify it under the terms of the GNU Lesser General Public
3# License as published by the Free Software Foundation; either
4# version 2.1 of the License, or (at your option) any later version.
5#
6# This library is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9# Lesser General Public License for more details.
10#
11# You should have received a copy of the GNU Lesser General Public
12# License along with this library; if not, write to the
13# Free Software Foundation, Inc.,
14# 59 Temple Place, Suite 330,
15# Boston, MA 02111-1307 USA
16
17# This file is part of urlgrabber, a high-level cross-protocol url-grabber
18
19import httplib
20import urllib2
21
22try:
23 from M2Crypto import SSL
24 from M2Crypto import httpslib
25 from M2Crypto import m2urllib2
26
27 SSL.Connection.clientPostConnectionCheck = None
28 have_m2crypto = True
29except ImportError:
30 have_m2crypto = False
31
32DEBUG = None
33
34if have_m2crypto:
35
36 class M2SSLFactory:
37
38 def __init__(self, ssl_ca_cert, ssl_context):
39 self.ssl_context = self._get_ssl_context(ssl_ca_cert, ssl_context)
40
41 def _get_ssl_context(self, ssl_ca_cert, ssl_context):
42 """
43 Create an ssl context using the CA cert file or ssl context.
44
45 The CA cert is used first if it was passed as an option. If not,
46 then the supplied ssl context is used. If no ssl context was supplied,
47 None is returned.
48 """
49 if ssl_ca_cert:
50 context = SSL.Context()
51 context.load_verify_locations(ssl_ca_cert)
52 context.set_verify(SSL.verify_none, -1)
53 return context
54 else:
55 return ssl_context
56
57 def create_https_connection(self, host, response_class = None):
58 connection = httplib.HTTPSConnection(host, self.ssl_context)
59 if response_class:
60 connection.response_class = response_class
61 return connection
62
63 def create_opener(self, *handlers):
64 return m2urllib2.build_opener(self.ssl_context, *handlers)
65
66
67class SSLFactory:
68
69 def create_https_connection(self, host, response_class = None):
70 connection = httplib.HTTPSConnection(host)
71 if response_class:
72 connection.response_class = response_class
73 return connection
74
75 def create_opener(self, *handlers):
76 return urllib2.build_opener(*handlers)
77
78
79
80def get_factory(ssl_ca_cert = None, ssl_context = None):
81 """ Return an SSLFactory, based on if M2Crypto is available. """
82 if have_m2crypto:
83 return M2SSLFactory(ssl_ca_cert, ssl_context)
84 else:
85 # Log here if someone provides the args but we don't use them.
86 if ssl_ca_cert or ssl_context:
87 if DEBUG:
88 DEBUG.warning("SSL arguments supplied, but M2Crypto is not available. "
89 "Using Python SSL.")
90 return SSLFactory()
diff --git a/scripts/lib/mic/3rdparty/pykickstart/version.py b/scripts/lib/mic/3rdparty/pykickstart/version.py
new file mode 100644
index 0000000000..102cc37d80
--- /dev/null
+++ b/scripts/lib/mic/3rdparty/pykickstart/version.py
@@ -0,0 +1,197 @@
1#
2# Chris Lumens <clumens@redhat.com>
3#
4# Copyright 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
5#
6# This copyrighted material is made available to anyone wishing to use, modify,
7# copy, or redistribute it subject to the terms and conditions of the GNU
8# General Public License v.2. This program is distributed in the hope that it
9# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
10# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11# See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 51
15# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
16# trademarks that are incorporated in the source code or documentation are not
17# subject to the GNU General Public License and may only be used or replicated
18# with the express permission of Red Hat, Inc.
19#
20"""
21Methods for working with kickstart versions.
22
23This module defines several symbolic constants that specify kickstart syntax
24versions. Each version corresponds roughly to one release of Red Hat Linux,
25Red Hat Enterprise Linux, or Fedora Core as these are where most syntax
26changes take place.
27
28This module also exports several functions:
29
30 makeVersion - Given a version number, return an instance of the
31 matching handler class.
32
33 returnClassForVersion - Given a version number, return the matching
34 handler class. This does not return an
35 instance of that class, however.
36
37 stringToVersion - Convert a string representation of a version number
38 into the symbolic constant.
39
40 versionToString - Perform the reverse mapping.
41
42 versionFromFile - Read a kickstart file and determine the version of
43 syntax it uses. This requires the kickstart file to
44 have a version= comment in it.
45"""
46import imputil, re, sys
47from urlgrabber import urlopen
48
49import gettext
50_ = lambda x: gettext.ldgettext("pykickstart", x)
51
52from pykickstart.errors import KickstartVersionError
53
54# Symbolic names for internal version numbers.
55RHEL3 = 900
56FC3 = 1000
57RHEL4 = 1100
58FC4 = 2000
59FC5 = 3000
60FC6 = 4000
61RHEL5 = 4100
62F7 = 5000
63F8 = 6000
64F9 = 7000
65F10 = 8000
66F11 = 9000
67F12 = 10000
68F13 = 11000
69RHEL6 = 11100
70F14 = 12000
71F15 = 13000
72F16 = 14000
73
74# This always points at the latest version and is the default.
75DEVEL = F16
76
77# A one-to-one mapping from string representations to version numbers.
78versionMap = {
79 "DEVEL": DEVEL,
80 "FC3": FC3, "FC4": FC4, "FC5": FC5, "FC6": FC6, "F7": F7, "F8": F8,
81 "F9": F9, "F10": F10, "F11": F11, "F12": F12, "F13": F13,
82 "F14": F14, "F15": F15, "F16": F16,
83 "RHEL3": RHEL3, "RHEL4": RHEL4, "RHEL5": RHEL5, "RHEL6": RHEL6
84}
85
86def stringToVersion(s):
87 """Convert string into one of the provided version constants. Raises
88 KickstartVersionError if string does not match anything.
89 """
90 # First try these short forms.
91 try:
92 return versionMap[s.upper()]
93 except KeyError:
94 pass
95
96 # Now try the Fedora versions.
97 m = re.match("^fedora.* (\d+)$", s, re.I)
98
99 if m and m.group(1):
100 if versionMap.has_key("FC" + m.group(1)):
101 return versionMap["FC" + m.group(1)]
102 elif versionMap.has_key("F" + m.group(1)):
103 return versionMap["F" + m.group(1)]
104 else:
105 raise KickstartVersionError(_("Unsupported version specified: %s") % s)
106
107 # Now try the RHEL versions.
108 m = re.match("^red hat enterprise linux.* (\d+)([\.\d]*)$", s, re.I)
109
110 if m and m.group(1):
111 if versionMap.has_key("RHEL" + m.group(1)):
112 return versionMap["RHEL" + m.group(1)]
113 else:
114 raise KickstartVersionError(_("Unsupported version specified: %s") % s)
115
116 # If nothing else worked, we're out of options.
117 raise KickstartVersionError(_("Unsupported version specified: %s") % s)
118
119def versionToString(version, skipDevel=False):
120 """Convert version into a string representation of the version number.
121 This is the reverse operation of stringToVersion. Raises
122 KickstartVersionError if version does not match anything.
123 """
124 if not skipDevel and version == versionMap["DEVEL"]:
125 return "DEVEL"
126
127 for (key, val) in versionMap.iteritems():
128 if key == "DEVEL":
129 continue
130 elif val == version:
131 return key
132
133 raise KickstartVersionError(_("Unsupported version specified: %s") % version)
134
135def versionFromFile(f):
136 """Given a file or URL, look for a line starting with #version= and
137 return the version number. If no version is found, return DEVEL.
138 """
139 v = DEVEL
140
141 fh = urlopen(f)
142
143 while True:
144 try:
145 l = fh.readline()
146 except StopIteration:
147 break
148
149 # At the end of the file?
150 if l == "":
151 break
152
153 if l.isspace() or l.strip() == "":
154 continue
155
156 if l[:9] == "#version=":
157 v = stringToVersion(l[9:].rstrip())
158 break
159
160 fh.close()
161 return v
162
163def returnClassForVersion(version=DEVEL):
164 """Return the class of the syntax handler for version. version can be
165 either a string or the matching constant. Raises KickstartValueError
166 if version does not match anything.
167 """
168 try:
169 version = int(version)
170 module = "%s" % versionToString(version, skipDevel=True)
171 except ValueError:
172 module = "%s" % version
173 version = stringToVersion(version)
174
175 module = module.lower()
176
177 try:
178 import pykickstart.handlers
179 sys.path.extend(pykickstart.handlers.__path__)
180 found = imputil.imp.find_module(module)
181 loaded = imputil.imp.load_module(module, found[0], found[1], found[2])
182
183 for (k, v) in loaded.__dict__.iteritems():
184 if k.lower().endswith("%shandler" % module):
185 return v
186 except:
187 raise KickstartVersionError(_("Unsupported version specified: %s") % version)
188
189def makeVersion(version=DEVEL):
190 """Return a new instance of the syntax handler for version. version can be
191 either a string or the matching constant. This function is useful for
192 standalone programs which just need to handle a specific version of
193 kickstart syntax (as provided by a command line argument, for example)
194 and need to instantiate the correct object.
195 """
196 cl = returnClassForVersion(version)
197 return cl()
diff --git a/scripts/lib/mic/__init__.py b/scripts/lib/mic/__init__.py
new file mode 100644
index 0000000000..63c1d9c846
--- /dev/null
+++ b/scripts/lib/mic/__init__.py
@@ -0,0 +1,4 @@
1import os, sys
2
3cur_path = os.path.dirname(__file__) or '.'
4sys.path.insert(0, cur_path + '/3rdparty')
diff --git a/scripts/lib/mic/__version__.py b/scripts/lib/mic/__version__.py
new file mode 100644
index 0000000000..60d7626cac
--- /dev/null
+++ b/scripts/lib/mic/__version__.py
@@ -0,0 +1 @@
VERSION = "0.14"
diff --git a/scripts/lib/mic/bootstrap.py b/scripts/lib/mic/bootstrap.py
new file mode 100644
index 0000000000..66c291b0a8
--- /dev/null
+++ b/scripts/lib/mic/bootstrap.py
@@ -0,0 +1,279 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2009, 2010, 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18from __future__ import with_statement
19import os
20import sys
21import tempfile
22import shutil
23import subprocess
24import rpm
25from mic import msger
26from mic.utils import errors, proxy, misc
27from mic.utils.rpmmisc import readRpmHeader, RPMInstallCallback
28from mic.chroot import cleanup_mounts, setup_chrootenv, cleanup_chrootenv
29
30PATH_BOOTSTRAP = "/usr/sbin:/usr/bin:/sbin:/bin"
31
32RPMTRANS_FLAGS = [
33 rpm.RPMTRANS_FLAG_ALLFILES,
34 rpm.RPMTRANS_FLAG_NOSCRIPTS,
35 rpm.RPMTRANS_FLAG_NOTRIGGERS,
36 ]
37
38RPMVSF_FLAGS = [
39 rpm._RPMVSF_NOSIGNATURES,
40 rpm._RPMVSF_NODIGESTS
41 ]
42
43RPMPROB_FLAGS = [
44 rpm.RPMPROB_FILTER_OLDPACKAGE,
45 rpm.RPMPROB_FILTER_REPLACEPKG,
46 rpm.RPMPROB_FILTER_IGNOREARCH
47 ]
48
49class MiniBackend(object):
50 def __init__(self, rootdir, arch=None, repomd=None):
51 self._ts = None
52 self.rootdir = os.path.abspath(rootdir)
53 self.arch = arch
54 self.repomd = repomd
55 self.dlpkgs = []
56 self.localpkgs = {}
57 self.optionals = []
58 self.preins = {}
59 self.postins = {}
60 self.scriptlets = False
61
62 def __del__(self):
63 try:
64 del self.ts
65 except:
66 pass
67
68 def get_ts(self):
69 if not self._ts:
70 self._ts = rpm.TransactionSet(self.rootdir)
71 self._ts.setFlags(reduce(lambda x, y: x|y, RPMTRANS_FLAGS))
72 self._ts.setVSFlags(reduce(lambda x, y: x|y, RPMVSF_FLAGS))
73 self._ts.setProbFilter(reduce(lambda x, y: x|y, RPMPROB_FLAGS))
74
75 return self._ts
76
77 def del_ts(self):
78 if self._ts:
79 self._ts.closeDB()
80 self._ts = None
81
82 ts = property(fget = lambda self: self.get_ts(),
83 fdel = lambda self: self.del_ts(),
84 doc="TransactionSet object")
85
86 def selectPackage(self, pkg):
87 if not pkg in self.dlpkgs:
88 self.dlpkgs.append(pkg)
89
90 def runInstall(self):
91 # FIXME: check space
92 self.downloadPkgs()
93 self.installPkgs()
94
95 if not self.scriptlets:
96 return
97
98 for pkg in self.preins.keys():
99 prog, script = self.preins[pkg]
100 self.run_pkg_script(pkg, prog, script, '0')
101 for pkg in self.postins.keys():
102 prog, script = self.postins[pkg]
103 self.run_pkg_script(pkg, prog, script, '1')
104
105 def downloadPkgs(self):
106 nonexist = []
107 for pkg in self.dlpkgs:
108 localpth = misc.get_package(pkg, self.repomd, self.arch)
109 if localpth:
110 self.localpkgs[pkg] = localpth
111 elif pkg in self.optionals:
112 # skip optional rpm
113 continue
114 else:
115 # mark nonexist rpm
116 nonexist.append(pkg)
117
118 if nonexist:
119 raise errors.BootstrapError("Can't get rpm binary: %s" %
120 ','.join(nonexist))
121
122 def installPkgs(self):
123 for pkg in self.localpkgs.keys():
124 rpmpath = self.localpkgs[pkg]
125
126 hdr = readRpmHeader(self.ts, rpmpath)
127
128 # save prein and postin scripts
129 self.preins[pkg] = (hdr['PREINPROG'], hdr['PREIN'])
130 self.postins[pkg] = (hdr['POSTINPROG'], hdr['POSTIN'])
131
132 # mark pkg as install
133 self.ts.addInstall(hdr, rpmpath, 'u')
134
135 # run transaction
136 self.ts.order()
137 cb = RPMInstallCallback(self.ts)
138 self.ts.run(cb.callback, '')
139
140 def run_pkg_script(self, pkg, prog, script, arg):
141 mychroot = lambda: os.chroot(self.rootdir)
142
143 if not script:
144 return
145
146 if prog == "<lua>":
147 prog = "/usr/bin/lua"
148
149 tmpdir = os.path.join(self.rootdir, "tmp")
150 if not os.path.exists(tmpdir):
151 os.makedirs(tmpdir)
152 tmpfd, tmpfp = tempfile.mkstemp(dir=tmpdir, prefix="%s.pre-" % pkg)
153 script = script.replace('\r', '')
154 os.write(tmpfd, script)
155 os.close(tmpfd)
156 os.chmod(tmpfp, 0700)
157
158 try:
159 script_fp = os.path.join('/tmp', os.path.basename(tmpfp))
160 subprocess.call([prog, script_fp, arg], preexec_fn=mychroot)
161 except (OSError, IOError), err:
162 msger.warning(str(err))
163 finally:
164 os.unlink(tmpfp)
165
166class Bootstrap(object):
167 def __init__(self, rootdir, distro, arch=None):
168 self.rootdir = misc.mkdtemp(dir=rootdir, prefix=distro)
169 self.distro = distro
170 self.arch = arch
171 self.logfile = None
172 self.pkgslist = []
173 self.repomd = None
174
175 def __del__(self):
176 self.cleanup()
177
178 def get_rootdir(self):
179 if os.path.exists(self.rootdir):
180 shutil.rmtree(self.rootdir, ignore_errors=True)
181 os.makedirs(self.rootdir)
182 return self.rootdir
183
184 def dirsetup(self, rootdir=None):
185 _path = lambda pth: os.path.join(rootdir, pth.lstrip('/'))
186
187 if not rootdir:
188 rootdir = self.rootdir
189
190 try:
191 # make /tmp and /etc path
192 tmpdir = _path('/tmp')
193 if not os.path.exists(tmpdir):
194 os.makedirs(tmpdir)
195 etcdir = _path('/etc')
196 if not os.path.exists(etcdir):
197 os.makedirs(etcdir)
198
199 # touch distro file
200 tzdist = _path('/etc/%s-release' % self.distro)
201 if not os.path.exists(tzdist):
202 with open(tzdist, 'w') as wf:
203 wf.write("bootstrap")
204 except:
205 pass
206
207 def create(self, repomd, pkglist, optlist=()):
208 try:
209 pkgmgr = MiniBackend(self.get_rootdir())
210 pkgmgr.arch = self.arch
211 pkgmgr.repomd = repomd
212 pkgmgr.optionals = list(optlist)
213 map(pkgmgr.selectPackage, pkglist + list(optlist))
214 pkgmgr.runInstall()
215 except (OSError, IOError, errors.CreatorError), err:
216 raise errors.BootstrapError("%s" % err)
217
218 def run(self, cmd, chdir, rootdir=None, bindmounts=None):
219 def mychroot():
220 os.chroot(rootdir)
221 os.chdir(chdir)
222
223 def sync_timesetting(rootdir):
224 try:
225 # sync time and zone info to bootstrap
226 if os.path.exists(rootdir + "/etc/localtime"):
227 os.unlink(rootdir + "/etc/localtime")
228 shutil.copyfile("/etc/localtime", rootdir + "/etc/localtime")
229 except:
230 pass
231
232 def sync_passwdfile(rootdir):
233 try:
234 # sync passwd file to bootstrap, saving the user info
235 if os.path.exists(rootdir + "/etc/passwd"):
236 os.unlink(rootdir + "/etc/passwd")
237 shutil.copyfile("/etc/passwd", rootdir + "/etc/passwd")
238 except:
239 pass
240
241 if not rootdir:
242 rootdir = self.rootdir
243
244 if isinstance(cmd, list):
245 shell = False
246 else:
247 shell = True
248
249 env = os.environ
250 env['PATH'] = "%s:%s" % (PATH_BOOTSTRAP, env['PATH'])
251
252 retcode = 0
253 gloablmounts = None
254 try:
255 proxy.set_proxy_environ()
256 gloablmounts = setup_chrootenv(rootdir, bindmounts, False)
257 sync_timesetting(rootdir)
258 sync_passwdfile(rootdir)
259 retcode = subprocess.call(cmd, preexec_fn=mychroot, env=env, shell=shell)
260 except (OSError, IOError):
261 # add additional information to original exception
262 value, tb = sys.exc_info()[1:]
263 value = '%s: %s' % (value, ' '.join(cmd))
264 raise RuntimeError, value, tb
265 finally:
266 if self.logfile and os.path.isfile(self.logfile):
267 msger.log(file(self.logfile).read())
268 cleanup_chrootenv(rootdir, bindmounts, gloablmounts)
269 proxy.unset_proxy_environ()
270 return retcode
271
272 def cleanup(self):
273 try:
274 # clean mounts
275 cleanup_mounts(self.rootdir)
276 # remove rootdir
277 shutil.rmtree(self.rootdir, ignore_errors=True)
278 except:
279 pass
diff --git a/scripts/lib/mic/chroot.py b/scripts/lib/mic/chroot.py
new file mode 100644
index 0000000000..99fb9a2c17
--- /dev/null
+++ b/scripts/lib/mic/chroot.py
@@ -0,0 +1,343 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2009, 2010, 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18from __future__ import with_statement
19import os
20import shutil
21import subprocess
22
23from mic import msger
24from mic.conf import configmgr
25from mic.utils import misc, errors, runner, fs_related
26
27chroot_lockfd = -1
28chroot_lock = ""
29BIND_MOUNTS = (
30 "/proc",
31 "/proc/sys/fs/binfmt_misc",
32 "/sys",
33 "/dev",
34 "/dev/pts",
35 "/dev/shm",
36 "/var/lib/dbus",
37 "/var/run/dbus",
38 "/var/lock",
39 )
40
41def cleanup_after_chroot(targettype,imgmount,tmpdir,tmpmnt):
42 if imgmount and targettype == "img":
43 imgmount.cleanup()
44
45 if tmpdir:
46 shutil.rmtree(tmpdir, ignore_errors = True)
47
48 if tmpmnt:
49 shutil.rmtree(tmpmnt, ignore_errors = True)
50
51def check_bind_mounts(chrootdir, bindmounts):
52 chrootmounts = []
53 for mount in bindmounts.split(";"):
54 if not mount:
55 continue
56
57 srcdst = mount.split(":")
58 if len(srcdst) == 1:
59 srcdst.append("none")
60
61 if not os.path.isdir(srcdst[0]):
62 return False
63
64 if srcdst[1] == "" or srcdst[1] == "none":
65 srcdst[1] = None
66
67 if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/':
68 continue
69
70 if chrootdir:
71 if not srcdst[1]:
72 srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[0]))
73 else:
74 srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
75
76 tmpdir = chrootdir + "/" + srcdst[1]
77 if os.path.isdir(tmpdir):
78 msger.warning("Warning: dir %s has existed." % tmpdir)
79
80 return True
81
82def cleanup_mounts(chrootdir):
83 umountcmd = misc.find_binary_path("umount")
84 abs_chrootdir = os.path.abspath(chrootdir)
85 mounts = open('/proc/mounts').readlines()
86 for line in reversed(mounts):
87 if abs_chrootdir not in line:
88 continue
89
90 point = line.split()[1]
91
92 # '/' to avoid common name prefix
93 if abs_chrootdir == point or point.startswith(abs_chrootdir + '/'):
94 args = [ umountcmd, "-l", point ]
95 ret = runner.quiet(args)
96 if ret != 0:
97 msger.warning("failed to unmount %s" % point)
98
99 return 0
100
101def setup_chrootenv(chrootdir, bindmounts = None, mountparent = True):
102 global chroot_lockfd, chroot_lock
103
104 def get_bind_mounts(chrootdir, bindmounts, mountparent = True):
105 chrootmounts = []
106 if bindmounts in ("", None):
107 bindmounts = ""
108
109 for mount in bindmounts.split(";"):
110 if not mount:
111 continue
112
113 srcdst = mount.split(":")
114 srcdst[0] = os.path.abspath(os.path.expanduser(srcdst[0]))
115 if len(srcdst) == 1:
116 srcdst.append("none")
117
118 # if some bindmount is not existed, but it's created inside
119 # chroot, this is not expected
120 if not os.path.exists(srcdst[0]):
121 os.makedirs(srcdst[0])
122
123 if not os.path.isdir(srcdst[0]):
124 continue
125
126 if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/':
127 msger.verbose("%s will be mounted by default." % srcdst[0])
128 continue
129
130 if srcdst[1] == "" or srcdst[1] == "none":
131 srcdst[1] = None
132 else:
133 srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
134 if os.path.isdir(chrootdir + "/" + srcdst[1]):
135 msger.warning("%s has existed in %s , skip it."\
136 % (srcdst[1], chrootdir))
137 continue
138
139 chrootmounts.append(fs_related.BindChrootMount(srcdst[0],
140 chrootdir,
141 srcdst[1]))
142
143 """Default bind mounts"""
144 for pt in BIND_MOUNTS:
145 if not os.path.exists(pt):
146 continue
147 chrootmounts.append(fs_related.BindChrootMount(pt,
148 chrootdir,
149 None))
150
151 if mountparent:
152 chrootmounts.append(fs_related.BindChrootMount("/",
153 chrootdir,
154 "/parentroot",
155 "ro"))
156
157 for kernel in os.listdir("/lib/modules"):
158 chrootmounts.append(fs_related.BindChrootMount(
159 "/lib/modules/"+kernel,
160 chrootdir,
161 None,
162 "ro"))
163
164 return chrootmounts
165
166 def bind_mount(chrootmounts):
167 for b in chrootmounts:
168 msger.verbose("bind_mount: %s -> %s" % (b.src, b.dest))
169 b.mount()
170
171 def setup_resolv(chrootdir):
172 try:
173 shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf")
174 except:
175 pass
176
177 globalmounts = get_bind_mounts(chrootdir, bindmounts, mountparent)
178 bind_mount(globalmounts)
179
180 setup_resolv(chrootdir)
181
182 mtab = "/etc/mtab"
183 dstmtab = chrootdir + mtab
184 if not os.path.islink(dstmtab):
185 shutil.copyfile(mtab, dstmtab)
186
187 chroot_lock = os.path.join(chrootdir, ".chroot.lock")
188 chroot_lockfd = open(chroot_lock, "w")
189
190 return globalmounts
191
192def cleanup_chrootenv(chrootdir, bindmounts=None, globalmounts=()):
193 global chroot_lockfd, chroot_lock
194
195 def bind_unmount(chrootmounts):
196 for b in reversed(chrootmounts):
197 msger.verbose("bind_unmount: %s -> %s" % (b.src, b.dest))
198 b.unmount()
199
200 def cleanup_resolv(chrootdir):
201 try:
202 fd = open(chrootdir + "/etc/resolv.conf", "w")
203 fd.truncate(0)
204 fd.close()
205 except:
206 pass
207
208 def kill_processes(chrootdir):
209 import glob
210 for fp in glob.glob("/proc/*/root"):
211 try:
212 if os.readlink(fp) == chrootdir:
213 pid = int(fp.split("/")[2])
214 os.kill(pid, 9)
215 except:
216 pass
217
218 def cleanup_mountdir(chrootdir, bindmounts):
219 if bindmounts == "" or bindmounts == None:
220 return
221 chrootmounts = []
222 for mount in bindmounts.split(";"):
223 if not mount:
224 continue
225
226 srcdst = mount.split(":")
227
228 if len(srcdst) == 1:
229 srcdst.append("none")
230
231 if srcdst[0] == "/":
232 continue
233
234 if srcdst[1] == "" or srcdst[1] == "none":
235 srcdst[1] = srcdst[0]
236
237 srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1]))
238 tmpdir = chrootdir + "/" + srcdst[1]
239 if os.path.isdir(tmpdir):
240 if len(os.listdir(tmpdir)) == 0:
241 shutil.rmtree(tmpdir, ignore_errors = True)
242 else:
243 msger.warning("Warning: dir %s isn't empty." % tmpdir)
244
245 chroot_lockfd.close()
246 bind_unmount(globalmounts)
247
248 if not fs_related.my_fuser(chroot_lock):
249 tmpdir = chrootdir + "/parentroot"
250 if os.path.exists(tmpdir) and len(os.listdir(tmpdir)) == 0:
251 shutil.rmtree(tmpdir, ignore_errors = True)
252
253 cleanup_resolv(chrootdir)
254
255 if os.path.exists(chrootdir + "/etc/mtab"):
256 os.unlink(chrootdir + "/etc/mtab")
257
258 kill_processes(chrootdir)
259
260 cleanup_mountdir(chrootdir, bindmounts)
261
262def chroot(chrootdir, bindmounts = None, execute = "/bin/bash"):
263 def mychroot():
264 os.chroot(chrootdir)
265 os.chdir("/")
266
267 if configmgr.chroot['saveto']:
268 savefs = True
269 saveto = configmgr.chroot['saveto']
270 wrnmsg = "Can't save chroot fs for dir %s exists" % saveto
271 if saveto == chrootdir:
272 savefs = False
273 wrnmsg = "Dir %s is being used to chroot" % saveto
274 elif os.path.exists(saveto):
275 if msger.ask("Dir %s already exists, cleanup and continue?" %
276 saveto):
277 shutil.rmtree(saveto, ignore_errors = True)
278 savefs = True
279 else:
280 savefs = False
281
282 if savefs:
283 msger.info("Saving image to directory %s" % saveto)
284 fs_related.makedirs(os.path.dirname(os.path.abspath(saveto)))
285 runner.quiet("cp -af %s %s" % (chrootdir, saveto))
286 devs = ['dev/fd',
287 'dev/stdin',
288 'dev/stdout',
289 'dev/stderr',
290 'etc/mtab']
291 ignlst = [os.path.join(saveto, x) for x in devs]
292 map(os.unlink, filter(os.path.exists, ignlst))
293 else:
294 msger.warning(wrnmsg)
295
296 dev_null = os.open("/dev/null", os.O_WRONLY)
297 files_to_check = ["/bin/bash", "/sbin/init"]
298
299 architecture_found = False
300
301 """ Register statically-linked qemu-arm if it is an ARM fs """
302 qemu_emulator = None
303
304 for ftc in files_to_check:
305 ftc = "%s/%s" % (chrootdir,ftc)
306
307 # Return code of 'file' is "almost always" 0 based on some man pages
308 # so we need to check the file existance first.
309 if not os.path.exists(ftc):
310 continue
311
312 for line in runner.outs(['file', ftc]).splitlines():
313 if 'ARM' in line:
314 qemu_emulator = misc.setup_qemu_emulator(chrootdir, "arm")
315 architecture_found = True
316 break
317
318 if 'Intel' in line:
319 architecture_found = True
320 break
321
322 if architecture_found:
323 break
324
325 os.close(dev_null)
326 if not architecture_found:
327 raise errors.CreatorError("Failed to get architecture from any of the "
328 "following files %s from chroot." \
329 % files_to_check)
330
331 try:
332 msger.info("Launching shell. Exit to continue.\n"
333 "----------------------------------")
334 globalmounts = setup_chrootenv(chrootdir, bindmounts)
335 subprocess.call(execute, preexec_fn = mychroot, shell=True)
336
337 except OSError, err:
338 raise errors.CreatorError("chroot err: %s" % str(err))
339
340 finally:
341 cleanup_chrootenv(chrootdir, bindmounts, globalmounts)
342 if qemu_emulator:
343 os.unlink(chrootdir + qemu_emulator)
diff --git a/scripts/lib/mic/conf.py b/scripts/lib/mic/conf.py
new file mode 100644
index 0000000000..e37334cc7a
--- /dev/null
+++ b/scripts/lib/mic/conf.py
@@ -0,0 +1,239 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os, sys, re
19import ConfigParser
20
21from mic import msger
22from mic import kickstart
23from mic.utils import misc, runner, proxy, errors
24
25
26DEFAULT_GSITECONF = '/etc/mic/mic.conf'
27
28
29def get_siteconf():
30 mic_path = os.path.dirname(__file__)
31
32 m = re.match(r"(?P<prefix>.*)\/lib(64)?\/.*", mic_path)
33 if m and m.group('prefix') != "/usr":
34 return os.path.join(m.group('prefix'), "etc/mic/mic.conf")
35
36 return DEFAULT_GSITECONF
37
38class ConfigMgr(object):
39 prefer_backends = ["zypp", "yum"]
40
41 DEFAULTS = {'common': {
42 "distro_name": "Default Distribution",
43 "plugin_dir": "/usr/lib/mic/plugins", # TODO use prefix also?
44 },
45 'create': {
46 "tmpdir": '/var/tmp/mic',
47 "cachedir": '/var/tmp/mic/cache',
48 "outdir": './mic-output',
49
50 "arch": None, # None means auto-detect
51 "pkgmgr": "auto",
52 "name": "output",
53 "ksfile": None,
54 "ks": None,
55 "repomd": None,
56 "local_pkgs_path": None,
57 "release": None,
58 "logfile": None,
59 "record_pkgs": [],
60 "pack_to": None,
61 "name_prefix": None,
62 "name_suffix": None,
63 "proxy": None,
64 "no_proxy": None,
65 "copy_kernel": False,
66 "install_pkgs": None,
67 "repourl": {},
68 "localrepos": [], # save localrepos
69 "runtime": "bootstrap",
70 },
71 'chroot': {
72 "saveto": None,
73 },
74 'convert': {
75 "shell": False,
76 },
77 'bootstrap': {
78 "rootdir": '/var/tmp/mic-bootstrap',
79 "packages": [],
80 },
81 }
82
83 # make the manager class as singleton
84 _instance = None
85 def __new__(cls, *args, **kwargs):
86 if not cls._instance:
87 cls._instance = super(ConfigMgr, cls).__new__(cls, *args, **kwargs)
88
89 return cls._instance
90
91 def __init__(self, ksconf=None, siteconf=None):
92 # reset config options
93 self.reset()
94
95 if not siteconf:
96 siteconf = get_siteconf()
97
98 # initial options from siteconf
99 self._siteconf = siteconf
100
101 if ksconf:
102 self._ksconf = ksconf
103
104 def reset(self):
105 self.__ksconf = None
106 self.__siteconf = None
107
108 # initialize the values with defaults
109 for sec, vals in self.DEFAULTS.iteritems():
110 setattr(self, sec, vals)
111
112 def __set_siteconf(self, siteconf):
113 try:
114 self.__siteconf = siteconf
115 self._parse_siteconf(siteconf)
116 except ConfigParser.Error, error:
117 raise errors.ConfigError("%s" % error)
118 def __get_siteconf(self):
119 return self.__siteconf
120 _siteconf = property(__get_siteconf, __set_siteconf)
121
122 def __set_ksconf(self, ksconf):
123 if not os.path.isfile(ksconf):
124 msger.error('Cannot find ks file: %s' % ksconf)
125
126 self.__ksconf = ksconf
127 self._parse_kickstart(ksconf)
128 def __get_ksconf(self):
129 return self.__ksconf
130 _ksconf = property(__get_ksconf, __set_ksconf)
131
132 def _parse_siteconf(self, siteconf):
133 if not siteconf:
134 return
135
136 if not os.path.exists(siteconf):
137 msger.warning("cannot read config file: %s" % siteconf)
138 return
139
140 parser = ConfigParser.SafeConfigParser()
141 parser.read(siteconf)
142
143 for section in parser.sections():
144 if section in self.DEFAULTS:
145 getattr(self, section).update(dict(parser.items(section)))
146
147 # append common section items to other sections
148 for section in self.DEFAULTS.keys():
149 if section != "common":
150 getattr(self, section).update(self.common)
151
152 # check and normalize the scheme of proxy url
153 if self.create['proxy']:
154 m = re.match('^(\w+)://.*', self.create['proxy'])
155 if m:
156 scheme = m.group(1)
157 if scheme not in ('http', 'https', 'ftp', 'socks'):
158 msger.error("%s: proxy scheme is incorrect" % siteconf)
159 else:
160 msger.warning("%s: proxy url w/o scheme, use http as default"
161 % siteconf)
162 self.create['proxy'] = "http://" + self.create['proxy']
163
164 proxy.set_proxies(self.create['proxy'], self.create['no_proxy'])
165
166 # bootstrap option handling
167 self.set_runtime(self.create['runtime'])
168 if isinstance(self.bootstrap['packages'], basestring):
169 packages = self.bootstrap['packages'].replace('\n', ' ')
170 if packages.find(',') != -1:
171 packages = packages.split(',')
172 else:
173 packages = packages.split()
174 self.bootstrap['packages'] = packages
175
176 def _parse_kickstart(self, ksconf=None):
177 if not ksconf:
178 return
179
180 ksconf = misc.normalize_ksfile(ksconf,
181 self.create['release'],
182 self.create['arch'])
183
184 ks = kickstart.read_kickstart(ksconf)
185
186 self.create['ks'] = ks
187 self.create['name'] = os.path.splitext(os.path.basename(ksconf))[0]
188
189 self.create['name'] = misc.build_name(ksconf,
190 self.create['release'],
191 self.create['name_prefix'],
192 self.create['name_suffix'])
193
194 msger.info("Retrieving repo metadata:")
195 ksrepos = misc.get_repostrs_from_ks(ks)
196 if not ksrepos:
197 raise errors.KsError('no valid repos found in ks file')
198
199 for repo in ksrepos:
200 if 'baseurl' in repo and repo['baseurl'].startswith("file:"):
201 repourl = repo['baseurl'].replace('file:', '')
202 repourl = "/%s" % repourl.lstrip('/')
203 self.create['localrepos'].append(repourl)
204
205 self.create['repomd'] = misc.get_metadata_from_repos(
206 ksrepos,
207 self.create['cachedir'])
208 msger.raw(" DONE")
209
210 target_archlist, archlist = misc.get_arch(self.create['repomd'])
211 if self.create['arch']:
212 if self.create['arch'] not in archlist:
213 raise errors.ConfigError("Invalid arch %s for repository. "
214 "Valid arches: %s" \
215 % (self.create['arch'], ', '.join(archlist)))
216 else:
217 if len(target_archlist) == 1:
218 self.create['arch'] = str(target_archlist[0])
219 msger.info("\nUse detected arch %s." % target_archlist[0])
220 else:
221 raise errors.ConfigError("Please specify a valid arch, "
222 "the choice can be: %s" \
223 % ', '.join(archlist))
224
225 kickstart.resolve_groups(self.create, self.create['repomd'])
226
227 # check selinux, it will block arm and btrfs image creation
228 misc.selinux_check(self.create['arch'],
229 [p.fstype for p in ks.handler.partition.partitions])
230
231 def set_runtime(self, runtime):
232 if runtime not in ("bootstrap", "native"):
233 msger.error("Invalid runtime mode: %s" % runtime)
234
235 if misc.get_distro()[0] in ("tizen", "Tizen"):
236 runtime = "native"
237 self.create['runtime'] = runtime
238
239configmgr = ConfigMgr()
diff --git a/scripts/lib/mic/creator.py b/scripts/lib/mic/creator.py
new file mode 100644
index 0000000000..af5fb82a1e
--- /dev/null
+++ b/scripts/lib/mic/creator.py
@@ -0,0 +1,354 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os, sys, re
19from optparse import SUPPRESS_HELP
20
21from mic import msger, rt_util
22from mic.utils import cmdln, errors, rpmmisc
23from mic.conf import configmgr
24from mic.plugin import pluginmgr
25
26
27class Creator(cmdln.Cmdln):
28 """${name}: create an image
29
30 Usage:
31 ${name} SUBCOMMAND <ksfile> [OPTS]
32
33 ${command_list}
34 ${option_list}
35 """
36
37 name = 'mic create(cr)'
38
39 def __init__(self, *args, **kwargs):
40 cmdln.Cmdln.__init__(self, *args, **kwargs)
41 self._subcmds = []
42
43 # get cmds from pluginmgr
44 # mix-in do_subcmd interface
45 for subcmd, klass in pluginmgr.get_plugins('imager').iteritems():
46 if not hasattr(klass, 'do_create'):
47 msger.warning("Unsurpport subcmd: %s" % subcmd)
48 continue
49
50 func = getattr(klass, 'do_create')
51 setattr(self.__class__, "do_"+subcmd, func)
52 self._subcmds.append(subcmd)
53
54 def get_optparser(self):
55 optparser = cmdln.CmdlnOptionParser(self)
56 optparser.add_option('-d', '--debug', action='store_true',
57 dest='debug',
58 help=SUPPRESS_HELP)
59 optparser.add_option('-v', '--verbose', action='store_true',
60 dest='verbose',
61 help=SUPPRESS_HELP)
62 optparser.add_option('', '--logfile', type='string', dest='logfile',
63 default=None,
64 help='Path of logfile')
65 optparser.add_option('-c', '--config', type='string', dest='config',
66 default=None,
67 help='Specify config file for mic')
68 optparser.add_option('-k', '--cachedir', type='string', action='store',
69 dest='cachedir', default=None,
70 help='Cache directory to store the downloaded')
71 optparser.add_option('-o', '--outdir', type='string', action='store',
72 dest='outdir', default=None,
73 help='Output directory')
74 optparser.add_option('-A', '--arch', type='string', dest='arch',
75 default=None,
76 help='Specify repo architecture')
77 optparser.add_option('', '--release', type='string', dest='release',
78 default=None, metavar='RID',
79 help='Generate a release of RID with all necessary'
80 ' files, when @BUILD_ID@ is contained in '
81 'kickstart file, it will be replaced by RID')
82 optparser.add_option("", "--record-pkgs", type="string",
83 dest="record_pkgs", default=None,
84 help='Record the info of installed packages, '
85 'multiple values can be specified which '
86 'joined by ",", valid values: "name", '
87 '"content", "license", "vcs"')
88 optparser.add_option('', '--pkgmgr', type='string', dest='pkgmgr',
89 default=None,
90 help='Specify backend package manager')
91 optparser.add_option('', '--local-pkgs-path', type='string',
92 dest='local_pkgs_path', default=None,
93 help='Path for local pkgs(rpms) to be installed')
94 optparser.add_option('', '--runtime', type='string',
95 dest='runtime', default=None,
96 help='Specify runtime mode, avaiable: bootstrap, native')
97 # --taring-to is alias to --pack-to
98 optparser.add_option('', '--taring-to', type='string',
99 dest='pack_to', default=None,
100 help=SUPPRESS_HELP)
101 optparser.add_option('', '--pack-to', type='string',
102 dest='pack_to', default=None,
103 help='Pack the images together into the specified'
104 ' achive, extension supported: .zip, .tar, '
105 '.tar.gz, .tar.bz2, etc. by default, .tar '
106 'will be used')
107 optparser.add_option('', '--copy-kernel', action='store_true',
108 dest='copy_kernel',
109 help='Copy kernel files from image /boot directory'
110 ' to the image output directory.')
111 optparser.add_option('', '--install-pkgs', type='string', action='store',
112 dest='install_pkgs', default=None,
113 help='Specify what type of packages to be installed,'
114 ' valid: source, debuginfo, debugsource')
115 optparser.add_option('', '--tmpfs', action='store_true', dest='enabletmpfs',
116 help='Setup tmpdir as tmpfs to accelerate, experimental'
117 ' feature, use it if you have more than 4G memory')
118 optparser.add_option('', '--repourl', action='append',
119 dest='repourl', default=[],
120 help=SUPPRESS_HELP)
121 return optparser
122
123 def preoptparse(self, argv):
124 optparser = self.get_optparser()
125
126 largs = []
127 rargs = []
128 while argv:
129 arg = argv.pop(0)
130
131 if arg in ('-h', '--help'):
132 rargs.append(arg)
133
134 elif optparser.has_option(arg):
135 largs.append(arg)
136
137 if optparser.get_option(arg).takes_value():
138 try:
139 largs.append(argv.pop(0))
140 except IndexError:
141 raise errors.Usage("option %s requires arguments" % arg)
142
143 else:
144 if arg.startswith("--"):
145 if "=" in arg:
146 opt = arg.split("=")[0]
147 else:
148 opt = None
149 elif arg.startswith("-") and len(arg) > 2:
150 opt = arg[0:2]
151 else:
152 opt = None
153
154 if opt and optparser.has_option(opt):
155 largs.append(arg)
156 else:
157 rargs.append(arg)
158
159 return largs + rargs
160
161 def postoptparse(self):
162 abspath = lambda pth: os.path.abspath(os.path.expanduser(pth))
163
164 if self.options.verbose:
165 msger.set_loglevel('verbose')
166 if self.options.debug:
167 msger.set_loglevel('debug')
168
169 if self.options.logfile:
170 logfile_abs_path = abspath(self.options.logfile)
171 if os.path.isdir(logfile_abs_path):
172 raise errors.Usage("logfile's path %s should be file"
173 % self.options.logfile)
174 if not os.path.exists(os.path.dirname(logfile_abs_path)):
175 os.makedirs(os.path.dirname(logfile_abs_path))
176 msger.set_interactive(False)
177 msger.set_logfile(logfile_abs_path)
178 configmgr.create['logfile'] = self.options.logfile
179
180 if self.options.config:
181 configmgr.reset()
182 configmgr._siteconf = self.options.config
183
184 if self.options.outdir is not None:
185 configmgr.create['outdir'] = abspath(self.options.outdir)
186 if self.options.cachedir is not None:
187 configmgr.create['cachedir'] = abspath(self.options.cachedir)
188 os.environ['ZYPP_LOCKFILE_ROOT'] = configmgr.create['cachedir']
189
190 for cdir in ('outdir', 'cachedir'):
191 if os.path.exists(configmgr.create[cdir]) \
192 and not os.path.isdir(configmgr.create[cdir]):
193 msger.error('Invalid directory specified: %s' \
194 % configmgr.create[cdir])
195
196 if self.options.local_pkgs_path is not None:
197 if not os.path.exists(self.options.local_pkgs_path):
198 msger.error('Local pkgs directory: \'%s\' not exist' \
199 % self.options.local_pkgs_path)
200 configmgr.create['local_pkgs_path'] = self.options.local_pkgs_path
201
202 if self.options.release:
203 configmgr.create['release'] = self.options.release.rstrip('/')
204
205 if self.options.record_pkgs:
206 configmgr.create['record_pkgs'] = []
207 for infotype in self.options.record_pkgs.split(','):
208 if infotype not in ('name', 'content', 'license', 'vcs'):
209 raise errors.Usage('Invalid pkg recording: %s, valid ones:'
210 ' "name", "content", "license", "vcs"' \
211 % infotype)
212
213 configmgr.create['record_pkgs'].append(infotype)
214
215 if self.options.arch is not None:
216 supported_arch = sorted(rpmmisc.archPolicies.keys(), reverse=True)
217 if self.options.arch in supported_arch:
218 configmgr.create['arch'] = self.options.arch
219 else:
220 raise errors.Usage('Invalid architecture: "%s".\n'
221 ' Supported architectures are: \n'
222 ' %s' % (self.options.arch,
223 ', '.join(supported_arch)))
224
225 if self.options.pkgmgr is not None:
226 configmgr.create['pkgmgr'] = self.options.pkgmgr
227
228 if self.options.runtime:
229 configmgr.set_runtime(self.options.runtime)
230
231 if self.options.pack_to is not None:
232 configmgr.create['pack_to'] = self.options.pack_to
233
234 if self.options.copy_kernel:
235 configmgr.create['copy_kernel'] = self.options.copy_kernel
236
237 if self.options.install_pkgs:
238 configmgr.create['install_pkgs'] = []
239 for pkgtype in self.options.install_pkgs.split(','):
240 if pkgtype not in ('source', 'debuginfo', 'debugsource'):
241 raise errors.Usage('Invalid parameter specified: "%s", '
242 'valid values: source, debuginfo, '
243 'debusource' % pkgtype)
244
245 configmgr.create['install_pkgs'].append(pkgtype)
246
247 if self.options.enabletmpfs:
248 configmgr.create['enabletmpfs'] = self.options.enabletmpfs
249
250 if self.options.repourl:
251 for item in self.options.repourl:
252 try:
253 key, val = item.split('=')
254 except:
255 continue
256 configmgr.create['repourl'][key] = val
257
258 def main(self, argv=None):
259 if argv is None:
260 argv = sys.argv
261 else:
262 argv = argv[:] # don't modify caller's list
263
264 self.optparser = self.get_optparser()
265 if self.optparser:
266 try:
267 argv = self.preoptparse(argv)
268 self.options, args = self.optparser.parse_args(argv)
269
270 except cmdln.CmdlnUserError, ex:
271 msg = "%s: %s\nTry '%s help' for info.\n"\
272 % (self.name, ex, self.name)
273 msger.error(msg)
274
275 except cmdln.StopOptionProcessing, ex:
276 return 0
277 else:
278 # optparser=None means no process for opts
279 self.options, args = None, argv[1:]
280
281 if not args:
282 return self.emptyline()
283
284 self.postoptparse()
285
286 return self.cmd(args)
287
288 def precmd(self, argv): # check help before cmd
289
290 if '-h' in argv or '?' in argv or '--help' in argv or 'help' in argv:
291 return argv
292
293 if len(argv) == 1:
294 return ['help', argv[0]]
295
296 if os.geteuid() != 0:
297 raise msger.error("Root permission is required, abort")
298
299 return argv
300
301 def do_auto(self, subcmd, opts, *args):
302 """${cmd_name}: auto detect image type from magic header
303
304 Usage:
305 ${name} ${cmd_name} <ksfile>
306
307 ${cmd_option_list}
308 """
309 def parse_magic_line(re_str, pstr, ptype='mic'):
310 ptn = re.compile(re_str)
311 m = ptn.match(pstr)
312 if not m or not m.groups():
313 return None
314
315 inline_argv = m.group(1).strip()
316 if ptype == 'mic':
317 m2 = re.search('(?P<format>\w+)', inline_argv)
318 elif ptype == 'mic2':
319 m2 = re.search('(-f|--format(=)?)\s*(?P<format>\w+)',
320 inline_argv)
321 else:
322 return None
323
324 if m2:
325 cmdname = m2.group('format')
326 inline_argv = inline_argv.replace(m2.group(0), '')
327 return (cmdname, inline_argv)
328
329 return None
330
331 if len(args) != 1:
332 raise errors.Usage("Extra arguments given")
333
334 if not os.path.exists(args[0]):
335 raise errors.CreatorError("Can't find the file: %s" % args[0])
336
337 with open(args[0], 'r') as rf:
338 first_line = rf.readline()
339
340 mic_re = '^#\s*-\*-mic-options-\*-\s+(.*)\s+-\*-mic-options-\*-'
341 mic2_re = '^#\s*-\*-mic2-options-\*-\s+(.*)\s+-\*-mic2-options-\*-'
342
343 result = parse_magic_line(mic_re, first_line, 'mic') \
344 or parse_magic_line(mic2_re, first_line, 'mic2')
345 if not result:
346 raise errors.KsError("Invalid magic line in file: %s" % args[0])
347
348 if result[0] not in self._subcmds:
349 raise errors.KsError("Unsupport format '%s' in %s"
350 % (result[0], args[0]))
351
352 argv = ' '.join(result + args).split()
353 self.main(argv)
354
diff --git a/scripts/lib/mic/imager/__init__.py b/scripts/lib/mic/imager/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/scripts/lib/mic/imager/__init__.py
diff --git a/scripts/lib/mic/imager/baseimager.py b/scripts/lib/mic/imager/baseimager.py
new file mode 100644
index 0000000000..6efc6c1294
--- /dev/null
+++ b/scripts/lib/mic/imager/baseimager.py
@@ -0,0 +1,1335 @@
1
2#!/usr/bin/python -tt
3#
4# Copyright (c) 2007 Red Hat Inc.
5# Copyright (c) 2009, 2010, 2011 Intel, Inc.
6#
7# This program is free software; you can redistribute it and/or modify it
8# under the terms of the GNU General Public License as published by the Free
9# Software Foundation; version 2 of the License
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14# for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc., 59
18# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20from __future__ import with_statement
21import os, sys
22import stat
23import tempfile
24import shutil
25import subprocess
26import re
27import tarfile
28import glob
29
30import rpm
31
32from mic import kickstart
33from mic import msger
34from mic.utils.errors import CreatorError, Abort
35from mic.utils import misc, grabber, runner, fs_related as fs
36
37class BaseImageCreator(object):
38 """Installs a system to a chroot directory.
39
40 ImageCreator is the simplest creator class available; it will install and
41 configure a system image according to the supplied kickstart file.
42
43 e.g.
44
45 import mic.imgcreate as imgcreate
46 ks = imgcreate.read_kickstart("foo.ks")
47 imgcreate.ImageCreator(ks, "foo").create()
48
49 """
50
51 def __del__(self):
52 self.cleanup()
53
54 def __init__(self, createopts = None, pkgmgr = None):
55 """Initialize an ImageCreator instance.
56
57 ks -- a pykickstart.KickstartParser instance; this instance will be
58 used to drive the install by e.g. providing the list of packages
59 to be installed, the system configuration and %post scripts
60
61 name -- a name for the image; used for e.g. image filenames or
62 filesystem labels
63 """
64
65 self.pkgmgr = pkgmgr
66
67 self.__builddir = None
68 self.__bindmounts = []
69
70 self.ks = None
71 self.name = "target"
72 self.tmpdir = "/var/tmp/mic"
73 self.cachedir = "/var/tmp/mic/cache"
74 self.workdir = "/var/tmp/mic/build"
75 self.destdir = "."
76 self.installerfw_prefix = "INSTALLERFW_"
77 self.target_arch = "noarch"
78 self._local_pkgs_path = None
79 self.pack_to = None
80 self.repourl = {}
81
82 # If the kernel is save to the destdir when copy_kernel cmd is called.
83 self._need_copy_kernel = False
84 # setup tmpfs tmpdir when enabletmpfs is True
85 self.enabletmpfs = False
86
87 if createopts:
88 # Mapping table for variables that have different names.
89 optmap = {"pkgmgr" : "pkgmgr_name",
90 "outdir" : "destdir",
91 "arch" : "target_arch",
92 "local_pkgs_path" : "_local_pkgs_path",
93 "copy_kernel" : "_need_copy_kernel",
94 }
95
96 # update setting from createopts
97 for key in createopts.keys():
98 if key in optmap:
99 option = optmap[key]
100 else:
101 option = key
102 setattr(self, option, createopts[key])
103
104 self.destdir = os.path.abspath(os.path.expanduser(self.destdir))
105
106 if 'release' in createopts and createopts['release']:
107 self.name = createopts['release'] + '_' + self.name
108
109 if self.pack_to:
110 if '@NAME@' in self.pack_to:
111 self.pack_to = self.pack_to.replace('@NAME@', self.name)
112 (tar, ext) = os.path.splitext(self.pack_to)
113 if ext in (".gz", ".bz2") and tar.endswith(".tar"):
114 ext = ".tar" + ext
115 if ext not in misc.pack_formats:
116 self.pack_to += ".tar"
117
118 self._dep_checks = ["ls", "bash", "cp", "echo", "modprobe"]
119
120 # Output image file names
121 self.outimage = []
122
123 # A flag to generate checksum
124 self._genchecksum = False
125
126 self._alt_initrd_name = None
127
128 self._recording_pkgs = []
129
130 # available size in root fs, init to 0
131 self._root_fs_avail = 0
132
133 # Name of the disk image file that is created.
134 self._img_name = None
135
136 self.image_format = None
137
138 # Save qemu emulator file name in order to clean up it finally
139 self.qemu_emulator = None
140
141 # No ks provided when called by convertor, so skip the dependency check
142 if self.ks:
143 # If we have btrfs partition we need to check necessary tools
144 for part in self.ks.handler.partition.partitions:
145 if part.fstype and part.fstype == "btrfs":
146 self._dep_checks.append("mkfs.btrfs")
147 break
148
149 if self.target_arch and self.target_arch.startswith("arm"):
150 for dep in self._dep_checks:
151 if dep == "extlinux":
152 self._dep_checks.remove(dep)
153
154 if not os.path.exists("/usr/bin/qemu-arm") or \
155 not misc.is_statically_linked("/usr/bin/qemu-arm"):
156 self._dep_checks.append("qemu-arm-static")
157
158 if os.path.exists("/proc/sys/vm/vdso_enabled"):
159 vdso_fh = open("/proc/sys/vm/vdso_enabled","r")
160 vdso_value = vdso_fh.read().strip()
161 vdso_fh.close()
162 if (int)(vdso_value) == 1:
163 msger.warning("vdso is enabled on your host, which might "
164 "cause problems with arm emulations.\n"
165 "\tYou can disable vdso with following command before "
166 "starting image build:\n"
167 "\techo 0 | sudo tee /proc/sys/vm/vdso_enabled")
168
169 # make sure the specified tmpdir and cachedir exist
170 if not os.path.exists(self.tmpdir):
171 os.makedirs(self.tmpdir)
172 if not os.path.exists(self.cachedir):
173 os.makedirs(self.cachedir)
174
175
176 #
177 # Properties
178 #
179 def __get_instroot(self):
180 if self.__builddir is None:
181 raise CreatorError("_instroot is not valid before calling mount()")
182 return self.__builddir + "/install_root"
183 _instroot = property(__get_instroot)
184 """The location of the install root directory.
185
186 This is the directory into which the system is installed. Subclasses may
187 mount a filesystem image here or copy files to/from here.
188
189 Note, this directory does not exist before ImageCreator.mount() is called.
190
191 Note also, this is a read-only attribute.
192
193 """
194
195 def __get_outdir(self):
196 if self.__builddir is None:
197 raise CreatorError("_outdir is not valid before calling mount()")
198 return self.__builddir + "/out"
199 _outdir = property(__get_outdir)
200 """The staging location for the final image.
201
202 This is where subclasses should stage any files that are part of the final
203 image. ImageCreator.package() will copy any files found here into the
204 requested destination directory.
205
206 Note, this directory does not exist before ImageCreator.mount() is called.
207
208 Note also, this is a read-only attribute.
209
210 """
211
212
213 #
214 # Hooks for subclasses
215 #
216 def _mount_instroot(self, base_on = None):
217 """Mount or prepare the install root directory.
218
219 This is the hook where subclasses may prepare the install root by e.g.
220 mounting creating and loopback mounting a filesystem image to
221 _instroot.
222
223 There is no default implementation.
224
225 base_on -- this is the value passed to mount() and can be interpreted
226 as the subclass wishes; it might e.g. be the location of
227 a previously created ISO containing a system image.
228
229 """
230 pass
231
232 def _unmount_instroot(self):
233 """Undo anything performed in _mount_instroot().
234
235 This is the hook where subclasses must undo anything which was done
236 in _mount_instroot(). For example, if a filesystem image was mounted
237 onto _instroot, it should be unmounted here.
238
239 There is no default implementation.
240
241 """
242 pass
243
244 def _create_bootconfig(self):
245 """Configure the image so that it's bootable.
246
247 This is the hook where subclasses may prepare the image for booting by
248 e.g. creating an initramfs and bootloader configuration.
249
250 This hook is called while the install root is still mounted, after the
251 packages have been installed and the kickstart configuration has been
252 applied, but before the %post scripts have been executed.
253
254 There is no default implementation.
255
256 """
257 pass
258
259 def _stage_final_image(self):
260 """Stage the final system image in _outdir.
261
262 This is the hook where subclasses should place the image in _outdir
263 so that package() can copy it to the requested destination directory.
264
265 By default, this moves the install root into _outdir.
266
267 """
268 shutil.move(self._instroot, self._outdir + "/" + self.name)
269
270 def get_installed_packages(self):
271 return self._pkgs_content.keys()
272
273 def _save_recording_pkgs(self, destdir):
274 """Save the list or content of installed packages to file.
275 """
276 pkgs = self._pkgs_content.keys()
277 pkgs.sort() # inplace op
278
279 if not os.path.exists(destdir):
280 os.makedirs(destdir)
281
282 content = None
283 if 'vcs' in self._recording_pkgs:
284 vcslst = ["%s %s" % (k, v) for (k, v) in self._pkgs_vcsinfo.items()]
285 content = '\n'.join(sorted(vcslst))
286 elif 'name' in self._recording_pkgs:
287 content = '\n'.join(pkgs)
288 if content:
289 namefile = os.path.join(destdir, self.name + '.packages')
290 f = open(namefile, "w")
291 f.write(content)
292 f.close()
293 self.outimage.append(namefile);
294
295 # if 'content', save more details
296 if 'content' in self._recording_pkgs:
297 contfile = os.path.join(destdir, self.name + '.files')
298 f = open(contfile, "w")
299
300 for pkg in pkgs:
301 content = pkg + '\n'
302
303 pkgcont = self._pkgs_content[pkg]
304 content += ' '
305 content += '\n '.join(pkgcont)
306 content += '\n'
307
308 content += '\n'
309 f.write(content)
310 f.close()
311 self.outimage.append(contfile)
312
313 if 'license' in self._recording_pkgs:
314 licensefile = os.path.join(destdir, self.name + '.license')
315 f = open(licensefile, "w")
316
317 f.write('Summary:\n')
318 for license in reversed(sorted(self._pkgs_license, key=\
319 lambda license: len(self._pkgs_license[license]))):
320 f.write(" - %s: %s\n" \
321 % (license, len(self._pkgs_license[license])))
322
323 f.write('\nDetails:\n')
324 for license in reversed(sorted(self._pkgs_license, key=\
325 lambda license: len(self._pkgs_license[license]))):
326 f.write(" - %s:\n" % (license))
327 for pkg in sorted(self._pkgs_license[license]):
328 f.write(" - %s\n" % (pkg))
329 f.write('\n')
330
331 f.close()
332 self.outimage.append(licensefile)
333
334 def _get_required_packages(self):
335 """Return a list of required packages.
336
337 This is the hook where subclasses may specify a set of packages which
338 it requires to be installed.
339
340 This returns an empty list by default.
341
342 Note, subclasses should usually chain up to the base class
343 implementation of this hook.
344
345 """
346 return []
347
348 def _get_excluded_packages(self):
349 """Return a list of excluded packages.
350
351 This is the hook where subclasses may specify a set of packages which
352 it requires _not_ to be installed.
353
354 This returns an empty list by default.
355
356 Note, subclasses should usually chain up to the base class
357 implementation of this hook.
358
359 """
360 return []
361
362 def _get_local_packages(self):
363 """Return a list of rpm path to be local installed.
364
365 This is the hook where subclasses may specify a set of rpms which
366 it requires to be installed locally.
367
368 This returns an empty list by default.
369
370 Note, subclasses should usually chain up to the base class
371 implementation of this hook.
372
373 """
374 if self._local_pkgs_path:
375 if os.path.isdir(self._local_pkgs_path):
376 return glob.glob(
377 os.path.join(self._local_pkgs_path, '*.rpm'))
378 elif os.path.splitext(self._local_pkgs_path)[-1] == '.rpm':
379 return [self._local_pkgs_path]
380
381 return []
382
383 def _get_fstab(self):
384 """Return the desired contents of /etc/fstab.
385
386 This is the hook where subclasses may specify the contents of
387 /etc/fstab by returning a string containing the desired contents.
388
389 A sensible default implementation is provided.
390
391 """
392 s = "/dev/root / %s %s 0 0\n" \
393 % (self._fstype,
394 "defaults,noatime" if not self._fsopts else self._fsopts)
395 s += self._get_fstab_special()
396 return s
397
398 def _get_fstab_special(self):
399 s = "devpts /dev/pts devpts gid=5,mode=620 0 0\n"
400 s += "tmpfs /dev/shm tmpfs defaults 0 0\n"
401 s += "proc /proc proc defaults 0 0\n"
402 s += "sysfs /sys sysfs defaults 0 0\n"
403 return s
404
405 def _set_part_env(self, pnum, prop, value):
406 """ This is a helper function which generates an environment variable
407 for a property "prop" with value "value" of a partition number "pnum".
408
409 The naming convention is:
410 * Variables start with INSTALLERFW_PART
411 * Then goes the partition number, the order is the same as
412 specified in the KS file
413 * Then goes the property name
414 """
415
416 if value == None:
417 value = ""
418 else:
419 value = str(value)
420
421 name = self.installerfw_prefix + ("PART%d_" % pnum) + prop
422 return { name : value }
423
424 def _get_post_scripts_env(self, in_chroot):
425 """Return an environment dict for %post scripts.
426
427 This is the hook where subclasses may specify some environment
428 variables for %post scripts by return a dict containing the desired
429 environment.
430
431 in_chroot -- whether this %post script is to be executed chroot()ed
432 into _instroot.
433 """
434
435 env = {}
436 pnum = 0
437
438 for p in kickstart.get_partitions(self.ks):
439 env.update(self._set_part_env(pnum, "SIZE", p.size))
440 env.update(self._set_part_env(pnum, "MOUNTPOINT", p.mountpoint))
441 env.update(self._set_part_env(pnum, "FSTYPE", p.fstype))
442 env.update(self._set_part_env(pnum, "LABEL", p.label))
443 env.update(self._set_part_env(pnum, "FSOPTS", p.fsopts))
444 env.update(self._set_part_env(pnum, "BOOTFLAG", p.active))
445 env.update(self._set_part_env(pnum, "ALIGN", p.align))
446 env.update(self._set_part_env(pnum, "TYPE_ID", p.part_type))
447 env.update(self._set_part_env(pnum, "DEVNODE",
448 "/dev/%s%d" % (p.disk, pnum + 1)))
449 pnum += 1
450
451 # Count of paritions
452 env[self.installerfw_prefix + "PART_COUNT"] = str(pnum)
453
454 # Partition table format
455 ptable_format = self.ks.handler.bootloader.ptable
456 env[self.installerfw_prefix + "PTABLE_FORMAT"] = ptable_format
457
458 # The kerned boot parameters
459 kernel_opts = self.ks.handler.bootloader.appendLine
460 env[self.installerfw_prefix + "KERNEL_OPTS"] = kernel_opts
461
462 # Name of the distribution
463 env[self.installerfw_prefix + "DISTRO_NAME"] = self.distro_name
464
465 # Name of the image creation tool
466 env[self.installerfw_prefix + "INSTALLER_NAME"] = "mic"
467
468 # The real current location of the mounted file-systems
469 if in_chroot:
470 mount_prefix = "/"
471 else:
472 mount_prefix = self._instroot
473 env[self.installerfw_prefix + "MOUNT_PREFIX"] = mount_prefix
474
475 # These are historical variables which lack the common name prefix
476 if not in_chroot:
477 env["INSTALL_ROOT"] = self._instroot
478 env["IMG_NAME"] = self._name
479
480 return env
481
482 def __get_imgname(self):
483 return self.name
484 _name = property(__get_imgname)
485 """The name of the image file.
486
487 """
488
489 def _get_kernel_versions(self):
490 """Return a dict detailing the available kernel types/versions.
491
492 This is the hook where subclasses may override what kernel types and
493 versions should be available for e.g. creating the booloader
494 configuration.
495
496 A dict should be returned mapping the available kernel types to a list
497 of the available versions for those kernels.
498
499 The default implementation uses rpm to iterate over everything
500 providing 'kernel', finds /boot/vmlinuz-* and returns the version
501 obtained from the vmlinuz filename. (This can differ from the kernel
502 RPM's n-v-r in the case of e.g. xen)
503
504 """
505 def get_kernel_versions(instroot):
506 ret = {}
507 versions = set()
508 files = glob.glob(instroot + "/boot/vmlinuz-*")
509 for file in files:
510 version = os.path.basename(file)[8:]
511 if version is None:
512 continue
513 versions.add(version)
514 ret["kernel"] = list(versions)
515 return ret
516
517 def get_version(header):
518 version = None
519 for f in header['filenames']:
520 if f.startswith('/boot/vmlinuz-'):
521 version = f[14:]
522 return version
523
524 if self.ks is None:
525 return get_kernel_versions(self._instroot)
526
527 ts = rpm.TransactionSet(self._instroot)
528
529 ret = {}
530 for header in ts.dbMatch('provides', 'kernel'):
531 version = get_version(header)
532 if version is None:
533 continue
534
535 name = header['name']
536 if not name in ret:
537 ret[name] = [version]
538 elif not version in ret[name]:
539 ret[name].append(version)
540
541 return ret
542
543
544 #
545 # Helpers for subclasses
546 #
547 def _do_bindmounts(self):
548 """Mount various system directories onto _instroot.
549
550 This method is called by mount(), but may also be used by subclasses
551 in order to re-mount the bindmounts after modifying the underlying
552 filesystem.
553
554 """
555 for b in self.__bindmounts:
556 b.mount()
557
558 def _undo_bindmounts(self):
559 """Unmount the bind-mounted system directories from _instroot.
560
561 This method is usually only called by unmount(), but may also be used
562 by subclasses in order to gain access to the filesystem obscured by
563 the bindmounts - e.g. in order to create device nodes on the image
564 filesystem.
565
566 """
567 self.__bindmounts.reverse()
568 for b in self.__bindmounts:
569 b.unmount()
570
571 def _chroot(self):
572 """Chroot into the install root.
573
574 This method may be used by subclasses when executing programs inside
575 the install root e.g.
576
577 subprocess.call(["/bin/ls"], preexec_fn = self.chroot)
578
579 """
580 os.chroot(self._instroot)
581 os.chdir("/")
582
583 def _mkdtemp(self, prefix = "tmp-"):
584 """Create a temporary directory.
585
586 This method may be used by subclasses to create a temporary directory
587 for use in building the final image - e.g. a subclass might create
588 a temporary directory in order to bundle a set of files into a package.
589
590 The subclass may delete this directory if it wishes, but it will be
591 automatically deleted by cleanup().
592
593 The absolute path to the temporary directory is returned.
594
595 Note, this method should only be called after mount() has been called.
596
597 prefix -- a prefix which should be used when creating the directory;
598 defaults to "tmp-".
599
600 """
601 self.__ensure_builddir()
602 return tempfile.mkdtemp(dir = self.__builddir, prefix = prefix)
603
604 def _mkstemp(self, prefix = "tmp-"):
605 """Create a temporary file.
606
607 This method may be used by subclasses to create a temporary file
608 for use in building the final image - e.g. a subclass might need
609 a temporary location to unpack a compressed file.
610
611 The subclass may delete this file if it wishes, but it will be
612 automatically deleted by cleanup().
613
614 A tuple containing a file descriptor (returned from os.open() and the
615 absolute path to the temporary directory is returned.
616
617 Note, this method should only be called after mount() has been called.
618
619 prefix -- a prefix which should be used when creating the file;
620 defaults to "tmp-".
621
622 """
623 self.__ensure_builddir()
624 return tempfile.mkstemp(dir = self.__builddir, prefix = prefix)
625
626 def _mktemp(self, prefix = "tmp-"):
627 """Create a temporary file.
628
629 This method simply calls _mkstemp() and closes the returned file
630 descriptor.
631
632 The absolute path to the temporary file is returned.
633
634 Note, this method should only be called after mount() has been called.
635
636 prefix -- a prefix which should be used when creating the file;
637 defaults to "tmp-".
638
639 """
640
641 (f, path) = self._mkstemp(prefix)
642 os.close(f)
643 return path
644
645
646 #
647 # Actual implementation
648 #
649 def __ensure_builddir(self):
650 if not self.__builddir is None:
651 return
652
653 try:
654 self.workdir = os.path.join(self.tmpdir, "build")
655 if not os.path.exists(self.workdir):
656 os.makedirs(self.workdir)
657 self.__builddir = tempfile.mkdtemp(dir = self.workdir,
658 prefix = "imgcreate-")
659 except OSError, (err, msg):
660 raise CreatorError("Failed create build directory in %s: %s" %
661 (self.tmpdir, msg))
662
663 def get_cachedir(self, cachedir = None):
664 if self.cachedir:
665 return self.cachedir
666
667 self.__ensure_builddir()
668 if cachedir:
669 self.cachedir = cachedir
670 else:
671 self.cachedir = self.__builddir + "/mic-cache"
672 fs.makedirs(self.cachedir)
673 return self.cachedir
674
675 def __sanity_check(self):
676 """Ensure that the config we've been given is sane."""
677 if not (kickstart.get_packages(self.ks) or
678 kickstart.get_groups(self.ks)):
679 raise CreatorError("No packages or groups specified")
680
681 kickstart.convert_method_to_repo(self.ks)
682
683 if not kickstart.get_repos(self.ks):
684 raise CreatorError("No repositories specified")
685
686 def __write_fstab(self):
687 fstab_contents = self._get_fstab()
688 if fstab_contents:
689 fstab = open(self._instroot + "/etc/fstab", "w")
690 fstab.write(fstab_contents)
691 fstab.close()
692
693 def __create_minimal_dev(self):
694 """Create a minimal /dev so that we don't corrupt the host /dev"""
695 origumask = os.umask(0000)
696 devices = (('null', 1, 3, 0666),
697 ('urandom',1, 9, 0666),
698 ('random', 1, 8, 0666),
699 ('full', 1, 7, 0666),
700 ('ptmx', 5, 2, 0666),
701 ('tty', 5, 0, 0666),
702 ('zero', 1, 5, 0666))
703
704 links = (("/proc/self/fd", "/dev/fd"),
705 ("/proc/self/fd/0", "/dev/stdin"),
706 ("/proc/self/fd/1", "/dev/stdout"),
707 ("/proc/self/fd/2", "/dev/stderr"))
708
709 for (node, major, minor, perm) in devices:
710 if not os.path.exists(self._instroot + "/dev/" + node):
711 os.mknod(self._instroot + "/dev/" + node,
712 perm | stat.S_IFCHR,
713 os.makedev(major,minor))
714
715 for (src, dest) in links:
716 if not os.path.exists(self._instroot + dest):
717 os.symlink(src, self._instroot + dest)
718
719 os.umask(origumask)
720
721 def __setup_tmpdir(self):
722 if not self.enabletmpfs:
723 return
724
725 runner.show('mount -t tmpfs -o size=4G tmpfs %s' % self.workdir)
726
727 def __clean_tmpdir(self):
728 if not self.enabletmpfs:
729 return
730
731 runner.show('umount -l %s' % self.workdir)
732
733 def mount(self, base_on = None, cachedir = None):
734 """Setup the target filesystem in preparation for an install.
735
736 This function sets up the filesystem which the ImageCreator will
737 install into and configure. The ImageCreator class merely creates an
738 install root directory, bind mounts some system directories (e.g. /dev)
739 and writes out /etc/fstab. Other subclasses may also e.g. create a
740 sparse file, format it and loopback mount it to the install root.
741
742 base_on -- a previous install on which to base this install; defaults
743 to None, causing a new image to be created
744
745 cachedir -- a directory in which to store the Yum cache; defaults to
746 None, causing a new cache to be created; by setting this
747 to another directory, the same cache can be reused across
748 multiple installs.
749
750 """
751 self.__setup_tmpdir()
752 self.__ensure_builddir()
753
754 # prevent popup dialog in Ubuntu(s)
755 misc.hide_loopdev_presentation()
756
757 fs.makedirs(self._instroot)
758 fs.makedirs(self._outdir)
759
760 self._mount_instroot(base_on)
761
762 for d in ("/dev/pts",
763 "/etc",
764 "/boot",
765 "/var/log",
766 "/sys",
767 "/proc",
768 "/usr/bin"):
769 fs.makedirs(self._instroot + d)
770
771 if self.target_arch and self.target_arch.startswith("arm"):
772 self.qemu_emulator = misc.setup_qemu_emulator(self._instroot,
773 self.target_arch)
774
775
776 self.get_cachedir(cachedir)
777
778 # bind mount system directories into _instroot
779 for (f, dest) in [("/sys", None),
780 ("/proc", None),
781 ("/proc/sys/fs/binfmt_misc", None),
782 ("/dev/pts", None)]:
783 self.__bindmounts.append(
784 fs.BindChrootMount(
785 f, self._instroot, dest))
786
787 self._do_bindmounts()
788
789 self.__create_minimal_dev()
790
791 if os.path.exists(self._instroot + "/etc/mtab"):
792 os.unlink(self._instroot + "/etc/mtab")
793 os.symlink("../proc/mounts", self._instroot + "/etc/mtab")
794
795 self.__write_fstab()
796
797 # get size of available space in 'instroot' fs
798 self._root_fs_avail = misc.get_filesystem_avail(self._instroot)
799
800 def unmount(self):
801 """Unmounts the target filesystem.
802
803 The ImageCreator class detaches the system from the install root, but
804 other subclasses may also detach the loopback mounted filesystem image
805 from the install root.
806
807 """
808 try:
809 mtab = self._instroot + "/etc/mtab"
810 if not os.path.islink(mtab):
811 os.unlink(self._instroot + "/etc/mtab")
812
813 if self.qemu_emulator:
814 os.unlink(self._instroot + self.qemu_emulator)
815 except OSError:
816 pass
817
818 self._undo_bindmounts()
819
820 """ Clean up yum garbage """
821 try:
822 instroot_pdir = os.path.dirname(self._instroot + self._instroot)
823 if os.path.exists(instroot_pdir):
824 shutil.rmtree(instroot_pdir, ignore_errors = True)
825 yumlibdir = self._instroot + "/var/lib/yum"
826 if os.path.exists(yumlibdir):
827 shutil.rmtree(yumlibdir, ignore_errors = True)
828 except OSError:
829 pass
830
831 self._unmount_instroot()
832
833 # reset settings of popup dialog in Ubuntu(s)
834 misc.unhide_loopdev_presentation()
835
836
837 def cleanup(self):
838 """Unmounts the target filesystem and deletes temporary files.
839
840 This method calls unmount() and then deletes any temporary files and
841 directories that were created on the host system while building the
842 image.
843
844 Note, make sure to call this method once finished with the creator
845 instance in order to ensure no stale files are left on the host e.g.:
846
847 creator = ImageCreator(ks, name)
848 try:
849 creator.create()
850 finally:
851 creator.cleanup()
852
853 """
854 if not self.__builddir:
855 return
856
857 self.unmount()
858
859 shutil.rmtree(self.__builddir, ignore_errors = True)
860 self.__builddir = None
861
862 self.__clean_tmpdir()
863
864 def __is_excluded_pkg(self, pkg):
865 if pkg in self._excluded_pkgs:
866 self._excluded_pkgs.remove(pkg)
867 return True
868
869 for xpkg in self._excluded_pkgs:
870 if xpkg.endswith('*'):
871 if pkg.startswith(xpkg[:-1]):
872 return True
873 elif xpkg.startswith('*'):
874 if pkg.endswith(xpkg[1:]):
875 return True
876
877 return None
878
879 def __select_packages(self, pkg_manager):
880 skipped_pkgs = []
881 for pkg in self._required_pkgs:
882 e = pkg_manager.selectPackage(pkg)
883 if e:
884 if kickstart.ignore_missing(self.ks):
885 skipped_pkgs.append(pkg)
886 elif self.__is_excluded_pkg(pkg):
887 skipped_pkgs.append(pkg)
888 else:
889 raise CreatorError("Failed to find package '%s' : %s" %
890 (pkg, e))
891
892 for pkg in skipped_pkgs:
893 msger.warning("Skipping missing package '%s'" % (pkg,))
894
895 def __select_groups(self, pkg_manager):
896 skipped_groups = []
897 for group in self._required_groups:
898 e = pkg_manager.selectGroup(group.name, group.include)
899 if e:
900 if kickstart.ignore_missing(self.ks):
901 skipped_groups.append(group)
902 else:
903 raise CreatorError("Failed to find group '%s' : %s" %
904 (group.name, e))
905
906 for group in skipped_groups:
907 msger.warning("Skipping missing group '%s'" % (group.name,))
908
909 def __deselect_packages(self, pkg_manager):
910 for pkg in self._excluded_pkgs:
911 pkg_manager.deselectPackage(pkg)
912
913 def __localinst_packages(self, pkg_manager):
914 for rpm_path in self._get_local_packages():
915 pkg_manager.installLocal(rpm_path)
916
917 def __preinstall_packages(self, pkg_manager):
918 if not self.ks:
919 return
920
921 self._preinstall_pkgs = kickstart.get_pre_packages(self.ks)
922 for pkg in self._preinstall_pkgs:
923 pkg_manager.preInstall(pkg)
924
925 def __attachment_packages(self, pkg_manager):
926 if not self.ks:
927 return
928
929 self._attachment = []
930 for item in kickstart.get_attachment(self.ks):
931 if item.startswith('/'):
932 fpaths = os.path.join(self._instroot, item.lstrip('/'))
933 for fpath in glob.glob(fpaths):
934 self._attachment.append(fpath)
935 continue
936
937 filelist = pkg_manager.getFilelist(item)
938 if filelist:
939 # found rpm in rootfs
940 for pfile in pkg_manager.getFilelist(item):
941 fpath = os.path.join(self._instroot, pfile.lstrip('/'))
942 self._attachment.append(fpath)
943 continue
944
945 # try to retrieve rpm file
946 (url, proxies) = pkg_manager.package_url(item)
947 if not url:
948 msger.warning("Can't get url from repo for %s" % item)
949 continue
950 fpath = os.path.join(self.cachedir, os.path.basename(url))
951 if not os.path.exists(fpath):
952 # download pkgs
953 try:
954 fpath = grabber.myurlgrab(url, fpath, proxies, None)
955 except CreatorError:
956 raise
957
958 tmpdir = self._mkdtemp()
959 misc.extract_rpm(fpath, tmpdir)
960 for (root, dirs, files) in os.walk(tmpdir):
961 for fname in files:
962 fpath = os.path.join(root, fname)
963 self._attachment.append(fpath)
964
965 def install(self, repo_urls=None):
966 """Install packages into the install root.
967
968 This function installs the packages listed in the supplied kickstart
969 into the install root. By default, the packages are installed from the
970 repository URLs specified in the kickstart.
971
972 repo_urls -- a dict which maps a repository name to a repository URL;
973 if supplied, this causes any repository URLs specified in
974 the kickstart to be overridden.
975
976 """
977
978 # initialize pkg list to install
979 if self.ks:
980 self.__sanity_check()
981
982 self._required_pkgs = \
983 kickstart.get_packages(self.ks, self._get_required_packages())
984 self._excluded_pkgs = \
985 kickstart.get_excluded(self.ks, self._get_excluded_packages())
986 self._required_groups = kickstart.get_groups(self.ks)
987 else:
988 self._required_pkgs = None
989 self._excluded_pkgs = None
990 self._required_groups = None
991
992 pkg_manager = self.get_pkg_manager()
993 pkg_manager.setup()
994
995 if hasattr(self, 'install_pkgs') and self.install_pkgs:
996 if 'debuginfo' in self.install_pkgs:
997 pkg_manager.install_debuginfo = True
998
999 for repo in kickstart.get_repos(self.ks, repo_urls):
1000 (name, baseurl, mirrorlist, inc, exc,
1001 proxy, proxy_username, proxy_password, debuginfo,
1002 source, gpgkey, disable, ssl_verify, nocache,
1003 cost, priority) = repo
1004
1005 yr = pkg_manager.addRepository(name, baseurl, mirrorlist, proxy,
1006 proxy_username, proxy_password, inc, exc, ssl_verify,
1007 nocache, cost, priority)
1008
1009 if kickstart.exclude_docs(self.ks):
1010 rpm.addMacro("_excludedocs", "1")
1011 rpm.addMacro("_dbpath", "/var/lib/rpm")
1012 rpm.addMacro("__file_context_path", "%{nil}")
1013 if kickstart.inst_langs(self.ks) != None:
1014 rpm.addMacro("_install_langs", kickstart.inst_langs(self.ks))
1015
1016 try:
1017 self.__preinstall_packages(pkg_manager)
1018 self.__select_packages(pkg_manager)
1019 self.__select_groups(pkg_manager)
1020 self.__deselect_packages(pkg_manager)
1021 self.__localinst_packages(pkg_manager)
1022
1023 BOOT_SAFEGUARD = 256L * 1024 * 1024 # 256M
1024 checksize = self._root_fs_avail
1025 if checksize:
1026 checksize -= BOOT_SAFEGUARD
1027 if self.target_arch:
1028 pkg_manager._add_prob_flags(rpm.RPMPROB_FILTER_IGNOREARCH)
1029 pkg_manager.runInstall(checksize)
1030 except CreatorError, e:
1031 raise
1032 except KeyboardInterrupt:
1033 raise
1034 else:
1035 self._pkgs_content = pkg_manager.getAllContent()
1036 self._pkgs_license = pkg_manager.getPkgsLicense()
1037 self._pkgs_vcsinfo = pkg_manager.getVcsInfo()
1038 self.__attachment_packages(pkg_manager)
1039 finally:
1040 pkg_manager.close()
1041
1042 # hook post install
1043 self.postinstall()
1044
1045 # do some clean up to avoid lvm info leakage. this sucks.
1046 for subdir in ("cache", "backup", "archive"):
1047 lvmdir = self._instroot + "/etc/lvm/" + subdir
1048 try:
1049 for f in os.listdir(lvmdir):
1050 os.unlink(lvmdir + "/" + f)
1051 except:
1052 pass
1053
1054 def postinstall(self):
1055 self.copy_attachment()
1056
1057 def __run_post_scripts(self):
1058 msger.info("Running scripts ...")
1059 if os.path.exists(self._instroot + "/tmp"):
1060 shutil.rmtree(self._instroot + "/tmp")
1061 os.mkdir (self._instroot + "/tmp", 0755)
1062 for s in kickstart.get_post_scripts(self.ks):
1063 (fd, path) = tempfile.mkstemp(prefix = "ks-script-",
1064 dir = self._instroot + "/tmp")
1065
1066 s.script = s.script.replace("\r", "")
1067 os.write(fd, s.script)
1068 os.close(fd)
1069 os.chmod(path, 0700)
1070
1071 env = self._get_post_scripts_env(s.inChroot)
1072
1073 if not s.inChroot:
1074 preexec = None
1075 script = path
1076 else:
1077 preexec = self._chroot
1078 script = "/tmp/" + os.path.basename(path)
1079
1080 try:
1081 try:
1082 subprocess.call([s.interp, script],
1083 preexec_fn = preexec,
1084 env = env,
1085 stdout = sys.stdout,
1086 stderr = sys.stderr)
1087 except OSError, (err, msg):
1088 raise CreatorError("Failed to execute %%post script "
1089 "with '%s' : %s" % (s.interp, msg))
1090 finally:
1091 os.unlink(path)
1092
1093 def __save_repo_keys(self, repodata):
1094 if not repodata:
1095 return None
1096
1097 gpgkeydir = "/etc/pki/rpm-gpg"
1098 fs.makedirs(self._instroot + gpgkeydir)
1099 for repo in repodata:
1100 if repo["repokey"]:
1101 repokey = gpgkeydir + "/RPM-GPG-KEY-%s" % repo["name"]
1102 shutil.copy(repo["repokey"], self._instroot + repokey)
1103
1104 def configure(self, repodata = None):
1105 """Configure the system image according to the kickstart.
1106
1107 This method applies the (e.g. keyboard or network) configuration
1108 specified in the kickstart and executes the kickstart %post scripts.
1109
1110 If necessary, it also prepares the image to be bootable by e.g.
1111 creating an initrd and bootloader configuration.
1112
1113 """
1114 ksh = self.ks.handler
1115
1116 msger.info('Applying configurations ...')
1117 try:
1118 kickstart.LanguageConfig(self._instroot).apply(ksh.lang)
1119 kickstart.KeyboardConfig(self._instroot).apply(ksh.keyboard)
1120 kickstart.TimezoneConfig(self._instroot).apply(ksh.timezone)
1121 #kickstart.AuthConfig(self._instroot).apply(ksh.authconfig)
1122 kickstart.FirewallConfig(self._instroot).apply(ksh.firewall)
1123 kickstart.RootPasswordConfig(self._instroot).apply(ksh.rootpw)
1124 kickstart.UserConfig(self._instroot).apply(ksh.user)
1125 kickstart.ServicesConfig(self._instroot).apply(ksh.services)
1126 kickstart.XConfig(self._instroot).apply(ksh.xconfig)
1127 kickstart.NetworkConfig(self._instroot).apply(ksh.network)
1128 kickstart.RPMMacroConfig(self._instroot).apply(self.ks)
1129 kickstart.DesktopConfig(self._instroot).apply(ksh.desktop)
1130 self.__save_repo_keys(repodata)
1131 kickstart.MoblinRepoConfig(self._instroot).apply(ksh.repo, repodata, self.repourl)
1132 except:
1133 msger.warning("Failed to apply configuration to image")
1134 raise
1135
1136 self._create_bootconfig()
1137 self.__run_post_scripts()
1138
1139 def launch_shell(self, launch):
1140 """Launch a shell in the install root.
1141
1142 This method is launches a bash shell chroot()ed in the install root;
1143 this can be useful for debugging.
1144
1145 """
1146 if launch:
1147 msger.info("Launching shell. Exit to continue.")
1148 subprocess.call(["/bin/bash"], preexec_fn = self._chroot)
1149
1150 def do_genchecksum(self, image_name):
1151 if not self._genchecksum:
1152 return
1153
1154 md5sum = misc.get_md5sum(image_name)
1155 with open(image_name + ".md5sum", "w") as f:
1156 f.write("%s %s" % (md5sum, os.path.basename(image_name)))
1157 self.outimage.append(image_name+".md5sum")
1158
1159 def package(self, destdir = "."):
1160 """Prepares the created image for final delivery.
1161
1162 In its simplest form, this method merely copies the install root to the
1163 supplied destination directory; other subclasses may choose to package
1164 the image by e.g. creating a bootable ISO containing the image and
1165 bootloader configuration.
1166
1167 destdir -- the directory into which the final image should be moved;
1168 this defaults to the current directory.
1169
1170 """
1171 self._stage_final_image()
1172
1173 if not os.path.exists(destdir):
1174 fs.makedirs(destdir)
1175
1176 if self._recording_pkgs:
1177 self._save_recording_pkgs(destdir)
1178
1179 # For image formats with two or multiple image files, it will be
1180 # better to put them under a directory
1181 if self.image_format in ("raw", "vmdk", "vdi", "nand", "mrstnand"):
1182 destdir = os.path.join(destdir, "%s-%s" \
1183 % (self.name, self.image_format))
1184 msger.debug("creating destination dir: %s" % destdir)
1185 fs.makedirs(destdir)
1186
1187 # Ensure all data is flushed to _outdir
1188 runner.quiet('sync')
1189
1190 misc.check_space_pre_cp(self._outdir, destdir)
1191 for f in os.listdir(self._outdir):
1192 shutil.move(os.path.join(self._outdir, f),
1193 os.path.join(destdir, f))
1194 self.outimage.append(os.path.join(destdir, f))
1195 self.do_genchecksum(os.path.join(destdir, f))
1196
1197 def print_outimage_info(self):
1198 msg = "The new image can be found here:\n"
1199 self.outimage.sort()
1200 for file in self.outimage:
1201 msg += ' %s\n' % os.path.abspath(file)
1202
1203 msger.info(msg)
1204
1205 def check_depend_tools(self):
1206 for tool in self._dep_checks:
1207 fs.find_binary_path(tool)
1208
1209 def package_output(self, image_format, destdir = ".", package="none"):
1210 if not package or package == "none":
1211 return
1212
1213 destdir = os.path.abspath(os.path.expanduser(destdir))
1214 (pkg, comp) = os.path.splitext(package)
1215 if comp:
1216 comp=comp.lstrip(".")
1217
1218 if pkg == "tar":
1219 if comp:
1220 dst = "%s/%s-%s.tar.%s" %\
1221 (destdir, self.name, image_format, comp)
1222 else:
1223 dst = "%s/%s-%s.tar" %\
1224 (destdir, self.name, image_format)
1225
1226 msger.info("creating %s" % dst)
1227 tar = tarfile.open(dst, "w:" + comp)
1228
1229 for file in self.outimage:
1230 msger.info("adding %s to %s" % (file, dst))
1231 tar.add(file,
1232 arcname=os.path.join("%s-%s" \
1233 % (self.name, image_format),
1234 os.path.basename(file)))
1235 if os.path.isdir(file):
1236 shutil.rmtree(file, ignore_errors = True)
1237 else:
1238 os.remove(file)
1239
1240 tar.close()
1241
1242 '''All the file in outimage has been packaged into tar.* file'''
1243 self.outimage = [dst]
1244
1245 def release_output(self, config, destdir, release):
1246 """ Create release directory and files
1247 """
1248
1249 def _rpath(fn):
1250 """ release path """
1251 return os.path.join(destdir, fn)
1252
1253 outimages = self.outimage
1254
1255 # new ks
1256 new_kspath = _rpath(self.name+'.ks')
1257 with open(config) as fr:
1258 with open(new_kspath, "w") as wf:
1259 # When building a release we want to make sure the .ks
1260 # file generates the same build even when --release not used.
1261 wf.write(fr.read().replace("@BUILD_ID@", release))
1262 outimages.append(new_kspath)
1263
1264 # save log file, logfile is only available in creator attrs
1265 if hasattr(self, 'logfile') and not self.logfile:
1266 log_path = _rpath(self.name + ".log")
1267 # touch the log file, else outimages will filter it out
1268 with open(log_path, 'w') as wf:
1269 wf.write('')
1270 msger.set_logfile(log_path)
1271 outimages.append(_rpath(self.name + ".log"))
1272
1273 # rename iso and usbimg
1274 for f in os.listdir(destdir):
1275 if f.endswith(".iso"):
1276 newf = f[:-4] + '.img'
1277 elif f.endswith(".usbimg"):
1278 newf = f[:-7] + '.img'
1279 else:
1280 continue
1281 os.rename(_rpath(f), _rpath(newf))
1282 outimages.append(_rpath(newf))
1283
1284 # generate MD5SUMS
1285 with open(_rpath("MD5SUMS"), "w") as wf:
1286 for f in os.listdir(destdir):
1287 if f == "MD5SUMS":
1288 continue
1289
1290 if os.path.isdir(os.path.join(destdir, f)):
1291 continue
1292
1293 md5sum = misc.get_md5sum(_rpath(f))
1294 # There needs to be two spaces between the sum and
1295 # filepath to match the syntax with md5sum.
1296 # This way also md5sum -c MD5SUMS can be used by users
1297 wf.write("%s *%s\n" % (md5sum, f))
1298
1299 outimages.append("%s/MD5SUMS" % destdir)
1300
1301 # Filter out the nonexist file
1302 for fp in outimages[:]:
1303 if not os.path.exists("%s" % fp):
1304 outimages.remove(fp)
1305
1306 def copy_kernel(self):
1307 """ Copy kernel files to the outimage directory.
1308 NOTE: This needs to be called before unmounting the instroot.
1309 """
1310
1311 if not self._need_copy_kernel:
1312 return
1313
1314 if not os.path.exists(self.destdir):
1315 os.makedirs(self.destdir)
1316
1317 for kernel in glob.glob("%s/boot/vmlinuz-*" % self._instroot):
1318 kernelfilename = "%s/%s-%s" % (self.destdir,
1319 self.name,
1320 os.path.basename(kernel))
1321 msger.info('copy kernel file %s as %s' % (os.path.basename(kernel),
1322 kernelfilename))
1323 shutil.copy(kernel, kernelfilename)
1324 self.outimage.append(kernelfilename)
1325
1326 def copy_attachment(self):
1327 """ Subclass implement it to handle attachment files
1328 NOTE: This needs to be called before unmounting the instroot.
1329 """
1330 pass
1331
1332 def get_pkg_manager(self):
1333 return self.pkgmgr(target_arch = self.target_arch,
1334 instroot = self._instroot,
1335 cachedir = self.cachedir)
diff --git a/scripts/lib/mic/imager/fs.py b/scripts/lib/mic/imager/fs.py
new file mode 100644
index 0000000000..d53b29cb47
--- /dev/null
+++ b/scripts/lib/mic/imager/fs.py
@@ -0,0 +1,99 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19
20from mic import msger
21from mic.utils import runner, misc
22from mic.utils.errors import CreatorError
23from mic.utils.fs_related import find_binary_path
24from mic.imager.baseimager import BaseImageCreator
25
26class FsImageCreator(BaseImageCreator):
27 def __init__(self, cfgmgr = None, pkgmgr = None):
28 self.zips = {
29 "tar.bz2" : ""
30 }
31 BaseImageCreator.__init__(self, cfgmgr, pkgmgr)
32 self._fstype = None
33 self._fsopts = None
34 self._include_src = False
35
36 def package(self, destdir = "."):
37
38 ignores = ["/dev/fd",
39 "/dev/stdin",
40 "/dev/stdout",
41 "/dev/stderr",
42 "/etc/mtab"]
43
44 if not os.path.exists(destdir):
45 os.makedirs(destdir)
46
47 if self._recording_pkgs:
48 self._save_recording_pkgs(destdir)
49
50 if not self.pack_to:
51 fsdir = os.path.join(destdir, self.name)
52
53 misc.check_space_pre_cp(self._instroot, destdir)
54 msger.info("Copying %s to %s ..." % (self._instroot, fsdir))
55 runner.show(['cp', "-af", self._instroot, fsdir])
56
57 for exclude in ignores:
58 if os.path.exists(fsdir + exclude):
59 os.unlink(fsdir + exclude)
60
61 self.outimage.append(fsdir)
62
63 else:
64 (tar, comp) = os.path.splitext(self.pack_to)
65 try:
66 tarcreat = {'.tar': '-cf',
67 '.gz': '-czf',
68 '.bz2': '-cjf',
69 '.tgz': '-czf',
70 '.tbz': '-cjf'}[comp]
71 except KeyError:
72 raise CreatorError("Unsupported comression for this image type:"
73 " '%s', try '.tar', '.tar.gz', etc" % comp)
74
75 dst = os.path.join(destdir, self.pack_to)
76 msger.info("Pack rootfs to %s. Please wait..." % dst)
77
78 tar = find_binary_path('tar')
79 tar_cmdline = [tar, "--numeric-owner",
80 "--preserve-permissions",
81 "--preserve-order",
82 "--one-file-system",
83 "--directory",
84 self._instroot]
85 for ignore_entry in ignores:
86 if ignore_entry.startswith('/'):
87 ignore_entry = ignore_entry[1:]
88
89 tar_cmdline.append("--exclude=%s" % (ignore_entry))
90
91 tar_cmdline.extend([tarcreat, dst, "."])
92
93 rc = runner.show(tar_cmdline)
94 if rc:
95 raise CreatorError("Failed compress image with tar.bz2. "
96 "Cmdline: %s" % (" ".join(tar_cmdline)))
97
98 self.outimage.append(dst)
99
diff --git a/scripts/lib/mic/imager/livecd.py b/scripts/lib/mic/imager/livecd.py
new file mode 100644
index 0000000000..a992ee0706
--- /dev/null
+++ b/scripts/lib/mic/imager/livecd.py
@@ -0,0 +1,750 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os, sys
19import glob
20import shutil
21
22from mic import kickstart, msger
23from mic.utils import fs_related, rpmmisc, runner, misc
24from mic.utils.errors import CreatorError
25from mic.imager.loop import LoopImageCreator
26
27
28class LiveImageCreatorBase(LoopImageCreator):
29 """A base class for LiveCD image creators.
30
31 This class serves as a base class for the architecture-specific LiveCD
32 image creator subclass, LiveImageCreator.
33
34 LiveImageCreator creates a bootable ISO containing the system image,
35 bootloader, bootloader configuration, kernel and initramfs.
36 """
37
38 def __init__(self, creatoropts = None, pkgmgr = None):
39 """Initialise a LiveImageCreator instance.
40
41 This method takes the same arguments as ImageCreator.__init__().
42 """
43 LoopImageCreator.__init__(self, creatoropts, pkgmgr)
44
45 #Controls whether to use squashfs to compress the image.
46 self.skip_compression = False
47
48 #Controls whether an image minimizing snapshot should be created.
49 #
50 #This snapshot can be used when copying the system image from the ISO in
51 #order to minimize the amount of data that needs to be copied; simply,
52 #it makes it possible to create a version of the image's filesystem with
53 #no spare space.
54 self.skip_minimize = False
55
56 #A flag which indicates i act as a convertor default false
57 self.actasconvertor = False
58
59 #The bootloader timeout from kickstart.
60 if self.ks:
61 self._timeout = kickstart.get_timeout(self.ks, 10)
62 else:
63 self._timeout = 10
64
65 #The default kernel type from kickstart.
66 if self.ks:
67 self._default_kernel = kickstart.get_default_kernel(self.ks,
68 "kernel")
69 else:
70 self._default_kernel = None
71
72 if self.ks:
73 parts = kickstart.get_partitions(self.ks)
74 if len(parts) > 1:
75 raise CreatorError("Can't support multi partitions in ks file "
76 "for this image type")
77 # FIXME: rename rootfs img to self.name,
78 # else can't find files when create iso
79 self._instloops[0]['name'] = self.name + ".img"
80
81 self.__isodir = None
82
83 self.__modules = ["=ata",
84 "sym53c8xx",
85 "aic7xxx",
86 "=usb",
87 "=firewire",
88 "=mmc",
89 "=pcmcia",
90 "mptsas"]
91 if self.ks:
92 self.__modules.extend(kickstart.get_modules(self.ks))
93
94 self._dep_checks.extend(["isohybrid",
95 "unsquashfs",
96 "mksquashfs",
97 "dd",
98 "genisoimage"])
99
100 #
101 # Hooks for subclasses
102 #
103 def _configure_bootloader(self, isodir):
104 """Create the architecture specific booloader configuration.
105
106 This is the hook where subclasses must create the booloader
107 configuration in order to allow a bootable ISO to be built.
108
109 isodir -- the directory where the contents of the ISO are to
110 be staged
111 """
112 raise CreatorError("Bootloader configuration is arch-specific, "
113 "but not implemented for this arch!")
114 def _get_menu_options(self):
115 """Return a menu options string for syslinux configuration.
116 """
117 if self.ks is None:
118 return "liveinst autoinst"
119 r = kickstart.get_menu_args(self.ks)
120 return r
121
122 def _get_kernel_options(self):
123 """Return a kernel options string for bootloader configuration.
124
125 This is the hook where subclasses may specify a set of kernel
126 options which should be included in the images bootloader
127 configuration.
128
129 A sensible default implementation is provided.
130 """
131
132 if self.ks is None:
133 r = "ro rd.live.image"
134 else:
135 r = kickstart.get_kernel_args(self.ks)
136
137 return r
138
139 def _get_mkisofs_options(self, isodir):
140 """Return the architecture specific mkisosfs options.
141
142 This is the hook where subclasses may specify additional arguments
143 to mkisofs, e.g. to enable a bootable ISO to be built.
144
145 By default, an empty list is returned.
146 """
147 return []
148
149 #
150 # Helpers for subclasses
151 #
152 def _has_checkisomd5(self):
153 """Check whether checkisomd5 is available in the install root."""
154 def _exists(path):
155 return os.path.exists(self._instroot + path)
156
157 if _exists("/usr/bin/checkisomd5") and os.path.exists("/usr/bin/implantisomd5"):
158 return True
159
160 return False
161
162 def __restore_file(self,path):
163 try:
164 os.unlink(path)
165 except:
166 pass
167 if os.path.exists(path + '.rpmnew'):
168 os.rename(path + '.rpmnew', path)
169
170 def _mount_instroot(self, base_on = None):
171 LoopImageCreator._mount_instroot(self, base_on)
172 self.__write_initrd_conf(self._instroot + "/etc/sysconfig/mkinitrd")
173 self.__write_dracut_conf(self._instroot + "/etc/dracut.conf.d/02livecd.conf")
174
175 def _unmount_instroot(self):
176 self.__restore_file(self._instroot + "/etc/sysconfig/mkinitrd")
177 self.__restore_file(self._instroot + "/etc/dracut.conf.d/02livecd.conf")
178 LoopImageCreator._unmount_instroot(self)
179
180 def __ensure_isodir(self):
181 if self.__isodir is None:
182 self.__isodir = self._mkdtemp("iso-")
183 return self.__isodir
184
185 def _get_isodir(self):
186 return self.__ensure_isodir()
187
188 def _set_isodir(self, isodir = None):
189 self.__isodir = isodir
190
191 def _create_bootconfig(self):
192 """Configure the image so that it's bootable."""
193 self._configure_bootloader(self.__ensure_isodir())
194
195 def _get_post_scripts_env(self, in_chroot):
196 env = LoopImageCreator._get_post_scripts_env(self, in_chroot)
197
198 if not in_chroot:
199 env["LIVE_ROOT"] = self.__ensure_isodir()
200
201 return env
202 def __write_dracut_conf(self, path):
203 if not os.path.exists(os.path.dirname(path)):
204 fs_related.makedirs(os.path.dirname(path))
205 f = open(path, "a")
206 f.write('add_dracutmodules+=" dmsquash-live pollcdrom "')
207 f.close()
208
209 def __write_initrd_conf(self, path):
210 content = ""
211 if not os.path.exists(os.path.dirname(path)):
212 fs_related.makedirs(os.path.dirname(path))
213 f = open(path, "w")
214
215 content += 'LIVEOS="yes"\n'
216 content += 'PROBE="no"\n'
217 content += 'MODULES+="squashfs ext3 ext2 vfat msdos "\n'
218 content += 'MODULES+="sr_mod sd_mod ide-cd cdrom "\n'
219
220 for module in self.__modules:
221 if module == "=usb":
222 content += 'MODULES+="ehci_hcd uhci_hcd ohci_hcd "\n'
223 content += 'MODULES+="usb_storage usbhid "\n'
224 elif module == "=firewire":
225 content += 'MODULES+="firewire-sbp2 firewire-ohci "\n'
226 content += 'MODULES+="sbp2 ohci1394 ieee1394 "\n'
227 elif module == "=mmc":
228 content += 'MODULES+="mmc_block sdhci sdhci-pci "\n'
229 elif module == "=pcmcia":
230 content += 'MODULES+="pata_pcmcia "\n'
231 else:
232 content += 'MODULES+="' + module + ' "\n'
233 f.write(content)
234 f.close()
235
236 def __create_iso(self, isodir):
237 iso = self._outdir + "/" + self.name + ".iso"
238 genisoimage = fs_related.find_binary_path("genisoimage")
239 args = [genisoimage,
240 "-J", "-r",
241 "-hide-rr-moved", "-hide-joliet-trans-tbl",
242 "-V", self.fslabel,
243 "-o", iso]
244
245 args.extend(self._get_mkisofs_options(isodir))
246
247 args.append(isodir)
248
249 if runner.show(args) != 0:
250 raise CreatorError("ISO creation failed!")
251
252 """ It should be ok still even if you haven't isohybrid """
253 isohybrid = None
254 try:
255 isohybrid = fs_related.find_binary_path("isohybrid")
256 except:
257 pass
258
259 if isohybrid:
260 args = [isohybrid, "-partok", iso ]
261 if runner.show(args) != 0:
262 raise CreatorError("Hybrid ISO creation failed!")
263
264 self.__implant_md5sum(iso)
265
266 def __implant_md5sum(self, iso):
267 """Implant an isomd5sum."""
268 if os.path.exists("/usr/bin/implantisomd5"):
269 implantisomd5 = "/usr/bin/implantisomd5"
270 else:
271 msger.warning("isomd5sum not installed; not setting up mediacheck")
272 implantisomd5 = ""
273 return
274
275 runner.show([implantisomd5, iso])
276
277 def _stage_final_image(self):
278 try:
279 fs_related.makedirs(self.__ensure_isodir() + "/LiveOS")
280
281 minimal_size = self._resparse()
282
283 if not self.skip_minimize:
284 fs_related.create_image_minimizer(self.__isodir + \
285 "/LiveOS/osmin.img",
286 self._image,
287 minimal_size)
288
289 if self.skip_compression:
290 shutil.move(self._image, self.__isodir + "/LiveOS/ext3fs.img")
291 else:
292 fs_related.makedirs(os.path.join(
293 os.path.dirname(self._image),
294 "LiveOS"))
295 shutil.move(self._image,
296 os.path.join(os.path.dirname(self._image),
297 "LiveOS", "ext3fs.img"))
298 fs_related.mksquashfs(os.path.dirname(self._image),
299 self.__isodir + "/LiveOS/squashfs.img")
300
301 self.__create_iso(self.__isodir)
302
303 if self.pack_to:
304 isoimg = os.path.join(self._outdir, self.name + ".iso")
305 packimg = os.path.join(self._outdir, self.pack_to)
306 misc.packing(packimg, isoimg)
307 os.unlink(isoimg)
308
309 finally:
310 shutil.rmtree(self.__isodir, ignore_errors = True)
311 self.__isodir = None
312
313class x86LiveImageCreator(LiveImageCreatorBase):
314 """ImageCreator for x86 machines"""
315 def _get_mkisofs_options(self, isodir):
316 return [ "-b", "isolinux/isolinux.bin",
317 "-c", "isolinux/boot.cat",
318 "-no-emul-boot", "-boot-info-table",
319 "-boot-load-size", "4" ]
320
321 def _get_required_packages(self):
322 return ["syslinux", "syslinux-extlinux"] + \
323 LiveImageCreatorBase._get_required_packages(self)
324
325 def _get_isolinux_stanzas(self, isodir):
326 return ""
327
328 def __find_syslinux_menu(self):
329 for menu in ["vesamenu.c32", "menu.c32"]:
330 if os.path.isfile(self._instroot + "/usr/share/syslinux/" + menu):
331 return menu
332
333 raise CreatorError("syslinux not installed : "
334 "no suitable /usr/share/syslinux/*menu.c32 found")
335
336 def __find_syslinux_mboot(self):
337 #
338 # We only need the mboot module if we have any xen hypervisors
339 #
340 if not glob.glob(self._instroot + "/boot/xen.gz*"):
341 return None
342
343 return "mboot.c32"
344
345 def __copy_syslinux_files(self, isodir, menu, mboot = None):
346 files = ["isolinux.bin", menu]
347 if mboot:
348 files += [mboot]
349
350 for f in files:
351 path = self._instroot + "/usr/share/syslinux/" + f
352
353 if not os.path.isfile(path):
354 raise CreatorError("syslinux not installed : "
355 "%s not found" % path)
356
357 shutil.copy(path, isodir + "/isolinux/")
358
359 def __copy_syslinux_background(self, isodest):
360 background_path = self._instroot + \
361 "/usr/share/branding/default/syslinux/syslinux-vesa-splash.jpg"
362
363 if not os.path.exists(background_path):
364 return False
365
366 shutil.copyfile(background_path, isodest)
367
368 return True
369
370 def __copy_kernel_and_initramfs(self, isodir, version, index):
371 bootdir = self._instroot + "/boot"
372 isDracut = False
373
374 if self._alt_initrd_name:
375 src_initrd_path = os.path.join(bootdir, self._alt_initrd_name)
376 else:
377 if os.path.exists(bootdir + "/initramfs-" + version + ".img"):
378 src_initrd_path = os.path.join(bootdir, "initramfs-" +version+ ".img")
379 isDracut = True
380 else:
381 src_initrd_path = os.path.join(bootdir, "initrd-" +version+ ".img")
382
383 try:
384 msger.debug("copy %s to %s" % (bootdir + "/vmlinuz-" + version, isodir + "/isolinux/vmlinuz" + index))
385 shutil.copyfile(bootdir + "/vmlinuz-" + version,
386 isodir + "/isolinux/vmlinuz" + index)
387
388 msger.debug("copy %s to %s" % (src_initrd_path, isodir + "/isolinux/initrd" + index + ".img"))
389 shutil.copyfile(src_initrd_path,
390 isodir + "/isolinux/initrd" + index + ".img")
391 except:
392 raise CreatorError("Unable to copy valid kernels or initrds, "
393 "please check the repo.")
394
395 is_xen = False
396 if os.path.exists(bootdir + "/xen.gz-" + version[:-3]):
397 shutil.copyfile(bootdir + "/xen.gz-" + version[:-3],
398 isodir + "/isolinux/xen" + index + ".gz")
399 is_xen = True
400
401 return (is_xen,isDracut)
402
403 def __is_default_kernel(self, kernel, kernels):
404 if len(kernels) == 1:
405 return True
406
407 if kernel == self._default_kernel:
408 return True
409
410 if kernel.startswith("kernel-") and kernel[7:] == self._default_kernel:
411 return True
412
413 return False
414
415 def __get_basic_syslinux_config(self, **args):
416 return """
417default %(menu)s
418timeout %(timeout)d
419
420%(background)s
421menu title Welcome to %(distroname)s!
422menu color border 0 #ffffffff #00000000
423menu color sel 7 #ff000000 #ffffffff
424menu color title 0 #ffffffff #00000000
425menu color tabmsg 0 #ffffffff #00000000
426menu color unsel 0 #ffffffff #00000000
427menu color hotsel 0 #ff000000 #ffffffff
428menu color hotkey 7 #ffffffff #ff000000
429menu color timeout_msg 0 #ffffffff #00000000
430menu color timeout 0 #ffffffff #00000000
431menu color cmdline 0 #ffffffff #00000000
432menu hidden
433menu clear
434""" % args
435
436 def __get_image_stanza(self, is_xen, isDracut, **args):
437 if isDracut:
438 args["rootlabel"] = "live:CDLABEL=%(fslabel)s" % args
439 else:
440 args["rootlabel"] = "CDLABEL=%(fslabel)s" % args
441 if not is_xen:
442 template = """label %(short)s
443 menu label %(long)s
444 kernel vmlinuz%(index)s
445 append initrd=initrd%(index)s.img root=%(rootlabel)s rootfstype=iso9660 %(liveargs)s %(extra)s
446"""
447 else:
448 template = """label %(short)s
449 menu label %(long)s
450 kernel mboot.c32
451 append xen%(index)s.gz --- vmlinuz%(index)s root=%(rootlabel)s rootfstype=iso9660 %(liveargs)s %(extra)s --- initrd%(index)s.img
452"""
453 return template % args
454
455 def __get_image_stanzas(self, isodir):
456 versions = []
457 kernels = self._get_kernel_versions()
458 for kernel in kernels:
459 for version in kernels[kernel]:
460 versions.append(version)
461
462 if not versions:
463 raise CreatorError("Unable to find valid kernels, "
464 "please check the repo")
465
466 kernel_options = self._get_kernel_options()
467
468 """ menu can be customized highly, the format is:
469
470 short_name1:long_name1:extra_opts1;short_name2:long_name2:extra_opts2
471
472 e.g.: autoinst:InstallationOnly:systemd.unit=installer-graphical.service
473 but in order to keep compatible with old format, these are still ok:
474
475 liveinst autoinst
476 liveinst;autoinst
477 liveinst::;autoinst::
478 """
479 oldmenus = {"basic": {
480 "short": "basic",
481 "long": "Installation Only (Text based)",
482 "extra": "basic nosplash 4"
483 },
484 "liveinst": {
485 "short": "liveinst",
486 "long": "Installation Only",
487 "extra": "liveinst nosplash 4"
488 },
489 "autoinst": {
490 "short": "autoinst",
491 "long": "Autoinstall (Deletes all existing content)",
492 "extra": "autoinst nosplash 4"
493 },
494 "netinst": {
495 "short": "netinst",
496 "long": "Network Installation",
497 "extra": "netinst 4"
498 },
499 "verify": {
500 "short": "check",
501 "long": "Verify and",
502 "extra": "check"
503 }
504 }
505 menu_options = self._get_menu_options()
506 menus = menu_options.split(";")
507 for i in range(len(menus)):
508 menus[i] = menus[i].split(":")
509 if len(menus) == 1 and len(menus[0]) == 1:
510 """ Keep compatible with the old usage way """
511 menus = menu_options.split()
512 for i in range(len(menus)):
513 menus[i] = [menus[i]]
514
515 cfg = ""
516
517 default_version = None
518 default_index = None
519 index = "0"
520 netinst = None
521 for version in versions:
522 (is_xen, isDracut) = self.__copy_kernel_and_initramfs(isodir, version, index)
523 if index == "0":
524 self._isDracut = isDracut
525
526 default = self.__is_default_kernel(kernel, kernels)
527
528 if default:
529 long = "Boot %s" % self.distro_name
530 elif kernel.startswith("kernel-"):
531 long = "Boot %s(%s)" % (self.name, kernel[7:])
532 else:
533 long = "Boot %s(%s)" % (self.name, kernel)
534
535 oldmenus["verify"]["long"] = "%s %s" % (oldmenus["verify"]["long"],
536 long)
537 # tell dracut not to ask for LUKS passwords or activate mdraid sets
538 if isDracut:
539 kern_opts = kernel_options + " rd.luks=0 rd.md=0 rd.dm=0"
540 else:
541 kern_opts = kernel_options
542
543 cfg += self.__get_image_stanza(is_xen, isDracut,
544 fslabel = self.fslabel,
545 liveargs = kern_opts,
546 long = long,
547 short = "linux" + index,
548 extra = "",
549 index = index)
550
551 if default:
552 cfg += "menu default\n"
553 default_version = version
554 default_index = index
555
556 for menu in menus:
557 if not menu[0]:
558 continue
559 short = menu[0] + index
560
561 if len(menu) >= 2:
562 long = menu[1]
563 else:
564 if menu[0] in oldmenus.keys():
565 if menu[0] == "verify" and not self._has_checkisomd5():
566 continue
567 if menu[0] == "netinst":
568 netinst = oldmenus[menu[0]]
569 continue
570 long = oldmenus[menu[0]]["long"]
571 extra = oldmenus[menu[0]]["extra"]
572 else:
573 long = short.upper() + " X" + index
574 extra = ""
575
576 if len(menu) >= 3:
577 extra = menu[2]
578
579 cfg += self.__get_image_stanza(is_xen, isDracut,
580 fslabel = self.fslabel,
581 liveargs = kernel_options,
582 long = long,
583 short = short,
584 extra = extra,
585 index = index)
586
587 index = str(int(index) + 1)
588
589 if not default_version:
590 default_version = versions[0]
591 if not default_index:
592 default_index = "0"
593
594 if netinst:
595 cfg += self.__get_image_stanza(is_xen, isDracut,
596 fslabel = self.fslabel,
597 liveargs = kernel_options,
598 long = netinst["long"],
599 short = netinst["short"],
600 extra = netinst["extra"],
601 index = default_index)
602
603 return cfg
604
605 def __get_memtest_stanza(self, isodir):
606 memtest = glob.glob(self._instroot + "/boot/memtest86*")
607 if not memtest:
608 return ""
609
610 shutil.copyfile(memtest[0], isodir + "/isolinux/memtest")
611
612 return """label memtest
613 menu label Memory Test
614 kernel memtest
615"""
616
617 def __get_local_stanza(self, isodir):
618 return """label local
619 menu label Boot from local drive
620 localboot 0xffff
621"""
622
623 def _configure_syslinux_bootloader(self, isodir):
624 """configure the boot loader"""
625 fs_related.makedirs(isodir + "/isolinux")
626
627 menu = self.__find_syslinux_menu()
628
629 self.__copy_syslinux_files(isodir, menu,
630 self.__find_syslinux_mboot())
631
632 background = ""
633 if self.__copy_syslinux_background(isodir + "/isolinux/splash.jpg"):
634 background = "menu background splash.jpg"
635
636 cfg = self.__get_basic_syslinux_config(menu = menu,
637 background = background,
638 name = self.name,
639 timeout = self._timeout * 10,
640 distroname = self.distro_name)
641
642 cfg += self.__get_image_stanzas(isodir)
643 cfg += self.__get_memtest_stanza(isodir)
644 cfg += self.__get_local_stanza(isodir)
645 cfg += self._get_isolinux_stanzas(isodir)
646
647 cfgf = open(isodir + "/isolinux/isolinux.cfg", "w")
648 cfgf.write(cfg)
649 cfgf.close()
650
651 def __copy_efi_files(self, isodir):
652 if not os.path.exists(self._instroot + "/boot/efi/EFI/redhat/grub.efi"):
653 return False
654 shutil.copy(self._instroot + "/boot/efi/EFI/redhat/grub.efi",
655 isodir + "/EFI/boot/grub.efi")
656 shutil.copy(self._instroot + "/boot/grub/splash.xpm.gz",
657 isodir + "/EFI/boot/splash.xpm.gz")
658
659 return True
660
661 def __get_basic_efi_config(self, **args):
662 return """
663default=0
664splashimage=/EFI/boot/splash.xpm.gz
665timeout %(timeout)d
666hiddenmenu
667
668""" %args
669
670 def __get_efi_image_stanza(self, **args):
671 return """title %(long)s
672 kernel /EFI/boot/vmlinuz%(index)s root=CDLABEL=%(fslabel)s rootfstype=iso9660 %(liveargs)s %(extra)s
673 initrd /EFI/boot/initrd%(index)s.img
674""" %args
675
676 def __get_efi_image_stanzas(self, isodir, name):
677 # FIXME: this only supports one kernel right now...
678
679 kernel_options = self._get_kernel_options()
680 checkisomd5 = self._has_checkisomd5()
681
682 cfg = ""
683
684 for index in range(0, 9):
685 # we don't support xen kernels
686 if os.path.exists("%s/EFI/boot/xen%d.gz" %(isodir, index)):
687 continue
688 cfg += self.__get_efi_image_stanza(fslabel = self.fslabel,
689 liveargs = kernel_options,
690 long = name,
691 extra = "", index = index)
692 if checkisomd5:
693 cfg += self.__get_efi_image_stanza(
694 fslabel = self.fslabel,
695 liveargs = kernel_options,
696 long = "Verify and Boot " + name,
697 extra = "check",
698 index = index)
699 break
700
701 return cfg
702
703 def _configure_efi_bootloader(self, isodir):
704 """Set up the configuration for an EFI bootloader"""
705 fs_related.makedirs(isodir + "/EFI/boot")
706
707 if not self.__copy_efi_files(isodir):
708 shutil.rmtree(isodir + "/EFI")
709 return
710
711 for f in os.listdir(isodir + "/isolinux"):
712 os.link("%s/isolinux/%s" %(isodir, f),
713 "%s/EFI/boot/%s" %(isodir, f))
714
715
716 cfg = self.__get_basic_efi_config(name = self.name,
717 timeout = self._timeout)
718 cfg += self.__get_efi_image_stanzas(isodir, self.name)
719
720 cfgf = open(isodir + "/EFI/boot/grub.conf", "w")
721 cfgf.write(cfg)
722 cfgf.close()
723
724 # first gen mactel machines get the bootloader name wrong apparently
725 if rpmmisc.getBaseArch() == "i386":
726 os.link(isodir + "/EFI/boot/grub.efi",
727 isodir + "/EFI/boot/boot.efi")
728 os.link(isodir + "/EFI/boot/grub.conf",
729 isodir + "/EFI/boot/boot.conf")
730
731 # for most things, we want them named boot$efiarch
732 efiarch = {"i386": "ia32", "x86_64": "x64"}
733 efiname = efiarch[rpmmisc.getBaseArch()]
734 os.rename(isodir + "/EFI/boot/grub.efi",
735 isodir + "/EFI/boot/boot%s.efi" %(efiname,))
736 os.link(isodir + "/EFI/boot/grub.conf",
737 isodir + "/EFI/boot/boot%s.conf" %(efiname,))
738
739
740 def _configure_bootloader(self, isodir):
741 self._configure_syslinux_bootloader(isodir)
742 self._configure_efi_bootloader(isodir)
743
744arch = rpmmisc.getBaseArch()
745if arch in ("i386", "x86_64"):
746 LiveCDImageCreator = x86LiveImageCreator
747elif arch.startswith("arm"):
748 LiveCDImageCreator = LiveImageCreatorBase
749else:
750 raise CreatorError("Architecture not supported!")
diff --git a/scripts/lib/mic/imager/liveusb.py b/scripts/lib/mic/imager/liveusb.py
new file mode 100644
index 0000000000..a909928a4c
--- /dev/null
+++ b/scripts/lib/mic/imager/liveusb.py
@@ -0,0 +1,308 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import shutil
20import re
21
22from mic import msger
23from mic.utils import misc, fs_related, runner
24from mic.utils.errors import CreatorError, MountError
25from mic.utils.partitionedfs import PartitionedMount
26from mic.imager.livecd import LiveCDImageCreator
27
28
29class LiveUSBImageCreator(LiveCDImageCreator):
30 def __init__(self, *args):
31 LiveCDImageCreator.__init__(self, *args)
32
33 self._dep_checks.extend(["kpartx", "parted"])
34
35 # remove dependency of genisoimage in parent class
36 if "genisoimage" in self._dep_checks:
37 self._dep_checks.remove("genisoimage")
38
39 def _create_usbimg(self, isodir):
40 overlaysizemb = 64 #default
41 #skipcompress = self.skip_compression?
42 fstype = "vfat"
43 homesizemb=0
44 swapsizemb=0
45 homefile="home.img"
46 plussize=128
47 kernelargs=None
48
49 if fstype == 'vfat':
50 if overlaysizemb > 2047:
51 raise CreatorError("Can't have an overlay of 2048MB or "
52 "greater on VFAT")
53
54 if homesizemb > 2047:
55 raise CreatorError("Can't have an home overlay of 2048MB or "
56 "greater on VFAT")
57
58 if swapsizemb > 2047:
59 raise CreatorError("Can't have an swap overlay of 2048MB or "
60 "greater on VFAT")
61
62 livesize = misc.get_file_size(isodir + "/LiveOS")
63
64 usbimgsize = (overlaysizemb + \
65 homesizemb + \
66 swapsizemb + \
67 livesize + \
68 plussize) * 1024L * 1024L
69
70 disk = fs_related.SparseLoopbackDisk("%s/%s.usbimg" \
71 % (self._outdir, self.name),
72 usbimgsize)
73 usbmnt = self._mkdtemp("usb-mnt")
74 usbloop = PartitionedMount(usbmnt)
75 usbloop.add_disk('/dev/sdb', disk)
76
77 usbloop.add_partition(usbimgsize/1024/1024,
78 "/dev/sdb",
79 "/",
80 fstype,
81 boot=True)
82
83 usbloop.mount()
84
85 try:
86 fs_related.makedirs(usbmnt + "/LiveOS")
87
88 if os.path.exists(isodir + "/LiveOS/squashfs.img"):
89 shutil.copyfile(isodir + "/LiveOS/squashfs.img",
90 usbmnt + "/LiveOS/squashfs.img")
91 else:
92 fs_related.mksquashfs(os.path.dirname(self._image),
93 usbmnt + "/LiveOS/squashfs.img")
94
95 if os.path.exists(isodir + "/LiveOS/osmin.img"):
96 shutil.copyfile(isodir + "/LiveOS/osmin.img",
97 usbmnt + "/LiveOS/osmin.img")
98
99 if fstype == "vfat" or fstype == "msdos":
100 uuid = usbloop.partitions[0]['mount'].uuid
101 label = usbloop.partitions[0]['mount'].fslabel
102 usblabel = "UUID=%s-%s" % (uuid[0:4], uuid[4:8])
103 overlaysuffix = "-%s-%s-%s" % (label, uuid[0:4], uuid[4:8])
104 else:
105 diskmount = usbloop.partitions[0]['mount']
106 usblabel = "UUID=%s" % diskmount.uuid
107 overlaysuffix = "-%s-%s" % (diskmount.fslabel, diskmount.uuid)
108
109 args = ['cp', "-Rf", isodir + "/isolinux", usbmnt + "/syslinux"]
110 rc = runner.show(args)
111 if rc:
112 raise CreatorError("Can't copy isolinux directory %s" \
113 % (isodir + "/isolinux/*"))
114
115 if os.path.isfile("/usr/share/syslinux/isolinux.bin"):
116 syslinux_path = "/usr/share/syslinux"
117 elif os.path.isfile("/usr/lib/syslinux/isolinux.bin"):
118 syslinux_path = "/usr/lib/syslinux"
119 else:
120 raise CreatorError("syslinux not installed : "
121 "cannot find syslinux installation path")
122
123 for f in ("isolinux.bin", "vesamenu.c32"):
124 path = os.path.join(syslinux_path, f)
125 if os.path.isfile(path):
126 args = ['cp', path, usbmnt + "/syslinux/"]
127 rc = runner.show(args)
128 if rc:
129 raise CreatorError("Can't copy syslinux file " + path)
130 else:
131 raise CreatorError("syslinux not installed: "
132 "syslinux file %s not found" % path)
133
134 fd = open(isodir + "/isolinux/isolinux.cfg", "r")
135 text = fd.read()
136 fd.close()
137 pattern = re.compile('CDLABEL=[^ ]*')
138 text = pattern.sub(usblabel, text)
139 pattern = re.compile('rootfstype=[^ ]*')
140 text = pattern.sub("rootfstype=" + fstype, text)
141 if kernelargs:
142 text = text.replace("rd.live.image", "rd.live.image " + kernelargs)
143
144 if overlaysizemb > 0:
145 msger.info("Initializing persistent overlay file")
146 overfile = "overlay" + overlaysuffix
147 if fstype == "vfat":
148 args = ['dd',
149 "if=/dev/zero",
150 "of=" + usbmnt + "/LiveOS/" + overfile,
151 "count=%d" % overlaysizemb,
152 "bs=1M"]
153 else:
154 args = ['dd',
155 "if=/dev/null",
156 "of=" + usbmnt + "/LiveOS/" + overfile,
157 "count=1",
158 "bs=1M",
159 "seek=%d" % overlaysizemb]
160 rc = runner.show(args)
161 if rc:
162 raise CreatorError("Can't create overlay file")
163 text = text.replace("rd.live.image", "rd.live.image rd.live.overlay=" + usblabel)
164 text = text.replace(" ro ", " rw ")
165
166 if swapsizemb > 0:
167 msger.info("Initializing swap file")
168 swapfile = usbmnt + "/LiveOS/" + "swap.img"
169 args = ['dd',
170 "if=/dev/zero",
171 "of=" + swapfile,
172 "count=%d" % swapsizemb,
173 "bs=1M"]
174 rc = runner.show(args)
175 if rc:
176 raise CreatorError("Can't create swap file")
177 args = ["mkswap", "-f", swapfile]
178 rc = runner.show(args)
179 if rc:
180 raise CreatorError("Can't mkswap on swap file")
181
182 if homesizemb > 0:
183 msger.info("Initializing persistent /home")
184 homefile = usbmnt + "/LiveOS/" + homefile
185 if fstype == "vfat":
186 args = ['dd',
187 "if=/dev/zero",
188 "of=" + homefile,
189 "count=%d" % homesizemb,
190 "bs=1M"]
191 else:
192 args = ['dd',
193 "if=/dev/null",
194 "of=" + homefile,
195 "count=1",
196 "bs=1M",
197 "seek=%d" % homesizemb]
198 rc = runner.show(args)
199 if rc:
200 raise CreatorError("Can't create home file")
201
202 mkfscmd = fs_related.find_binary_path("/sbin/mkfs." + fstype)
203 if fstype == "ext2" or fstype == "ext3":
204 args = [mkfscmd, "-F", "-j", homefile]
205 else:
206 args = [mkfscmd, homefile]
207 rc = runner.show(args)
208 if rc:
209 raise CreatorError("Can't mke2fs home file")
210 if fstype == "ext2" or fstype == "ext3":
211 tune2fs = fs_related.find_binary_path("tune2fs")
212 args = [tune2fs,
213 "-c0",
214 "-i0",
215 "-ouser_xattr,acl",
216 homefile]
217 rc = runner.show(args)
218 if rc:
219 raise CreatorError("Can't tune2fs home file")
220
221 if fstype == "vfat" or fstype == "msdos":
222 syslinuxcmd = fs_related.find_binary_path("syslinux")
223 syslinuxcfg = usbmnt + "/syslinux/syslinux.cfg"
224 args = [syslinuxcmd,
225 "-d",
226 "syslinux",
227 usbloop.partitions[0]["device"]]
228
229 elif fstype == "ext2" or fstype == "ext3":
230 extlinuxcmd = fs_related.find_binary_path("extlinux")
231 syslinuxcfg = usbmnt + "/syslinux/extlinux.conf"
232 args = [extlinuxcmd,
233 "-i",
234 usbmnt + "/syslinux"]
235
236 else:
237 raise CreatorError("Invalid file system type: %s" % (fstype))
238
239 os.unlink(usbmnt + "/syslinux/isolinux.cfg")
240 fd = open(syslinuxcfg, "w")
241 fd.write(text)
242 fd.close()
243 rc = runner.show(args)
244 if rc:
245 raise CreatorError("Can't install boot loader.")
246
247 finally:
248 usbloop.unmount()
249 usbloop.cleanup()
250
251 # Need to do this after image is unmounted and device mapper is closed
252 msger.info("set MBR")
253 mbrfile = "/usr/lib/syslinux/mbr.bin"
254 if not os.path.exists(mbrfile):
255 mbrfile = "/usr/share/syslinux/mbr.bin"
256 if not os.path.exists(mbrfile):
257 raise CreatorError("mbr.bin file didn't exist.")
258 mbrsize = os.path.getsize(mbrfile)
259 outimg = "%s/%s.usbimg" % (self._outdir, self.name)
260
261 args = ['dd',
262 "if=" + mbrfile,
263 "of=" + outimg,
264 "seek=0",
265 "conv=notrunc",
266 "bs=1",
267 "count=%d" % (mbrsize)]
268 rc = runner.show(args)
269 if rc:
270 raise CreatorError("Can't set MBR.")
271
272 def _stage_final_image(self):
273 try:
274 isodir = self._get_isodir()
275 fs_related.makedirs(isodir + "/LiveOS")
276
277 minimal_size = self._resparse()
278
279 if not self.skip_minimize:
280 fs_related.create_image_minimizer(isodir + "/LiveOS/osmin.img",
281 self._image,
282 minimal_size)
283
284 if self.skip_compression:
285 shutil.move(self._image,
286 isodir + "/LiveOS/ext3fs.img")
287 else:
288 fs_related.makedirs(os.path.join(
289 os.path.dirname(self._image),
290 "LiveOS"))
291 shutil.move(self._image,
292 os.path.join(os.path.dirname(self._image),
293 "LiveOS", "ext3fs.img"))
294 fs_related.mksquashfs(os.path.dirname(self._image),
295 isodir + "/LiveOS/squashfs.img")
296
297 self._create_usbimg(isodir)
298
299 if self.pack_to:
300 usbimg = os.path.join(self._outdir, self.name + ".usbimg")
301 packimg = os.path.join(self._outdir, self.pack_to)
302 misc.packing(packimg, usbimg)
303 os.unlink(usbimg)
304
305 finally:
306 shutil.rmtree(isodir, ignore_errors = True)
307 self._set_isodir(None)
308
diff --git a/scripts/lib/mic/imager/loop.py b/scripts/lib/mic/imager/loop.py
new file mode 100644
index 0000000000..4d05ef271d
--- /dev/null
+++ b/scripts/lib/mic/imager/loop.py
@@ -0,0 +1,418 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import glob
20import shutil
21
22from mic import kickstart, msger
23from mic.utils.errors import CreatorError, MountError
24from mic.utils import misc, runner, fs_related as fs
25from mic.imager.baseimager import BaseImageCreator
26
27
28# The maximum string length supported for LoopImageCreator.fslabel
29FSLABEL_MAXLEN = 32
30
31
32def save_mountpoints(fpath, loops, arch = None):
33 """Save mount points mapping to file
34
35 :fpath, the xml file to store partition info
36 :loops, dict of partition info
37 :arch, image arch
38 """
39
40 if not fpath or not loops:
41 return
42
43 from xml.dom import minidom
44 doc = minidom.Document()
45 imgroot = doc.createElement("image")
46 doc.appendChild(imgroot)
47 if arch:
48 imgroot.setAttribute('arch', arch)
49 for loop in loops:
50 part = doc.createElement("partition")
51 imgroot.appendChild(part)
52 for (key, val) in loop.items():
53 if isinstance(val, fs.Mount):
54 continue
55 part.setAttribute(key, str(val))
56
57 with open(fpath, 'w') as wf:
58 wf.write(doc.toprettyxml(indent=' '))
59
60 return
61
62def load_mountpoints(fpath):
63 """Load mount points mapping from file
64
65 :fpath, file path to load
66 """
67
68 if not fpath:
69 return
70
71 from xml.dom import minidom
72 mount_maps = []
73 with open(fpath, 'r') as rf:
74 dom = minidom.parse(rf)
75 imgroot = dom.documentElement
76 for part in imgroot.getElementsByTagName("partition"):
77 p = dict(part.attributes.items())
78
79 try:
80 mp = (p['mountpoint'], p['label'], p['name'],
81 int(p['size']), p['fstype'])
82 except KeyError:
83 msger.warning("Wrong format line in file: %s" % fpath)
84 except ValueError:
85 msger.warning("Invalid size '%s' in file: %s" % (p['size'], fpath))
86 else:
87 mount_maps.append(mp)
88
89 return mount_maps
90
91class LoopImageCreator(BaseImageCreator):
92 """Installs a system into a loopback-mountable filesystem image.
93
94 LoopImageCreator is a straightforward ImageCreator subclass; the system
95 is installed into an ext3 filesystem on a sparse file which can be
96 subsequently loopback-mounted.
97
98 When specifying multiple partitions in kickstart file, each partition
99 will be created as a separated loop image.
100 """
101
102 def __init__(self, creatoropts=None, pkgmgr=None,
103 compress_image=None,
104 shrink_image=False):
105 """Initialize a LoopImageCreator instance.
106
107 This method takes the same arguments as ImageCreator.__init__()
108 with the addition of:
109
110 fslabel -- A string used as a label for any filesystems created.
111 """
112
113 BaseImageCreator.__init__(self, creatoropts, pkgmgr)
114
115 self.compress_image = compress_image
116 self.shrink_image = shrink_image
117
118 self.__fslabel = None
119 self.fslabel = self.name
120
121 self.__blocksize = 4096
122 if self.ks:
123 self.__fstype = kickstart.get_image_fstype(self.ks,
124 "ext3")
125 self.__fsopts = kickstart.get_image_fsopts(self.ks,
126 "defaults,noatime")
127
128 allloops = []
129 for part in sorted(kickstart.get_partitions(self.ks),
130 key=lambda p: p.mountpoint):
131 if part.fstype == "swap":
132 continue
133
134 label = part.label
135 mp = part.mountpoint
136 if mp == '/':
137 # the base image
138 if not label:
139 label = self.name
140 else:
141 mp = mp.rstrip('/')
142 if not label:
143 msger.warning('no "label" specified for loop img at %s'
144 ', use the mountpoint as the name' % mp)
145 label = mp.split('/')[-1]
146
147 imgname = misc.strip_end(label, '.img') + '.img'
148 allloops.append({
149 'mountpoint': mp,
150 'label': label,
151 'name': imgname,
152 'size': part.size or 4096L * 1024 * 1024,
153 'fstype': part.fstype or 'ext3',
154 'extopts': part.extopts or None,
155 'loop': None, # to be created in _mount_instroot
156 })
157 self._instloops = allloops
158
159 else:
160 self.__fstype = None
161 self.__fsopts = None
162 self._instloops = []
163
164 self.__imgdir = None
165
166 if self.ks:
167 self.__image_size = kickstart.get_image_size(self.ks,
168 4096L * 1024 * 1024)
169 else:
170 self.__image_size = 0
171
172 self._img_name = self.name + ".img"
173
174 def get_image_names(self):
175 if not self._instloops:
176 return None
177
178 return [lo['name'] for lo in self._instloops]
179
180 def _set_fstype(self, fstype):
181 self.__fstype = fstype
182
183 def _set_image_size(self, imgsize):
184 self.__image_size = imgsize
185
186
187 #
188 # Properties
189 #
190 def __get_fslabel(self):
191 if self.__fslabel is None:
192 return self.name
193 else:
194 return self.__fslabel
195 def __set_fslabel(self, val):
196 if val is None:
197 self.__fslabel = None
198 else:
199 self.__fslabel = val[:FSLABEL_MAXLEN]
200 #A string used to label any filesystems created.
201 #
202 #Some filesystems impose a constraint on the maximum allowed size of the
203 #filesystem label. In the case of ext3 it's 16 characters, but in the case
204 #of ISO9660 it's 32 characters.
205 #
206 #mke2fs silently truncates the label, but mkisofs aborts if the label is
207 #too long. So, for convenience sake, any string assigned to this attribute
208 #is silently truncated to FSLABEL_MAXLEN (32) characters.
209 fslabel = property(__get_fslabel, __set_fslabel)
210
211 def __get_image(self):
212 if self.__imgdir is None:
213 raise CreatorError("_image is not valid before calling mount()")
214 return os.path.join(self.__imgdir, self._img_name)
215 #The location of the image file.
216 #
217 #This is the path to the filesystem image. Subclasses may use this path
218 #in order to package the image in _stage_final_image().
219 #
220 #Note, this directory does not exist before ImageCreator.mount() is called.
221 #
222 #Note also, this is a read-only attribute.
223 _image = property(__get_image)
224
225 def __get_blocksize(self):
226 return self.__blocksize
227 def __set_blocksize(self, val):
228 if self._instloops:
229 raise CreatorError("_blocksize must be set before calling mount()")
230 try:
231 self.__blocksize = int(val)
232 except ValueError:
233 raise CreatorError("'%s' is not a valid integer value "
234 "for _blocksize" % val)
235 #The block size used by the image's filesystem.
236 #
237 #This is the block size used when creating the filesystem image. Subclasses
238 #may change this if they wish to use something other than a 4k block size.
239 #
240 #Note, this attribute may only be set before calling mount().
241 _blocksize = property(__get_blocksize, __set_blocksize)
242
243 def __get_fstype(self):
244 return self.__fstype
245 def __set_fstype(self, val):
246 if val != "ext2" and val != "ext3":
247 raise CreatorError("Unknown _fstype '%s' supplied" % val)
248 self.__fstype = val
249 #The type of filesystem used for the image.
250 #
251 #This is the filesystem type used when creating the filesystem image.
252 #Subclasses may change this if they wish to use something other ext3.
253 #
254 #Note, only ext2 and ext3 are currently supported.
255 #
256 #Note also, this attribute may only be set before calling mount().
257 _fstype = property(__get_fstype, __set_fstype)
258
259 def __get_fsopts(self):
260 return self.__fsopts
261 def __set_fsopts(self, val):
262 self.__fsopts = val
263 #Mount options of filesystem used for the image.
264 #
265 #This can be specified by --fsoptions="xxx,yyy" in part command in
266 #kickstart file.
267 _fsopts = property(__get_fsopts, __set_fsopts)
268
269
270 #
271 # Helpers for subclasses
272 #
273 def _resparse(self, size=None):
274 """Rebuild the filesystem image to be as sparse as possible.
275
276 This method should be used by subclasses when staging the final image
277 in order to reduce the actual space taken up by the sparse image file
278 to be as little as possible.
279
280 This is done by resizing the filesystem to the minimal size (thereby
281 eliminating any space taken up by deleted files) and then resizing it
282 back to the supplied size.
283
284 size -- the size in, in bytes, which the filesystem image should be
285 resized to after it has been minimized; this defaults to None,
286 causing the original size specified by the kickstart file to
287 be used (or 4GiB if not specified in the kickstart).
288 """
289 minsize = 0
290 for item in self._instloops:
291 if item['name'] == self._img_name:
292 minsize = item['loop'].resparse(size)
293 else:
294 item['loop'].resparse(size)
295
296 return minsize
297
298 def _base_on(self, base_on=None):
299 if base_on and self._image != base_on:
300 shutil.copyfile(base_on, self._image)
301
302 def _check_imgdir(self):
303 if self.__imgdir is None:
304 self.__imgdir = self._mkdtemp()
305
306
307 #
308 # Actual implementation
309 #
310 def _mount_instroot(self, base_on=None):
311
312 if base_on and os.path.isfile(base_on):
313 self.__imgdir = os.path.dirname(base_on)
314 imgname = os.path.basename(base_on)
315 self._base_on(base_on)
316 self._set_image_size(misc.get_file_size(self._image))
317
318 # here, self._instloops must be []
319 self._instloops.append({
320 "mountpoint": "/",
321 "label": self.name,
322 "name": imgname,
323 "size": self.__image_size or 4096L,
324 "fstype": self.__fstype or "ext3",
325 "extopts": None,
326 "loop": None
327 })
328
329 self._check_imgdir()
330
331 for loop in self._instloops:
332 fstype = loop['fstype']
333 mp = os.path.join(self._instroot, loop['mountpoint'].lstrip('/'))
334 size = loop['size'] * 1024L * 1024L
335 imgname = loop['name']
336
337 if fstype in ("ext2", "ext3", "ext4"):
338 MyDiskMount = fs.ExtDiskMount
339 elif fstype == "btrfs":
340 MyDiskMount = fs.BtrfsDiskMount
341 elif fstype in ("vfat", "msdos"):
342 MyDiskMount = fs.VfatDiskMount
343 else:
344 msger.error('Cannot support fstype: %s' % fstype)
345
346 loop['loop'] = MyDiskMount(fs.SparseLoopbackDisk(
347 os.path.join(self.__imgdir, imgname),
348 size),
349 mp,
350 fstype,
351 self._blocksize,
352 loop['label'])
353
354 if fstype in ("ext2", "ext3", "ext4"):
355 loop['loop'].extopts = loop['extopts']
356
357 try:
358 msger.verbose('Mounting image "%s" on "%s"' % (imgname, mp))
359 fs.makedirs(mp)
360 loop['loop'].mount()
361 except MountError, e:
362 raise
363
364 def _unmount_instroot(self):
365 for item in reversed(self._instloops):
366 try:
367 item['loop'].cleanup()
368 except:
369 pass
370
371 def _stage_final_image(self):
372
373 if self.pack_to or self.shrink_image:
374 self._resparse(0)
375 else:
376 self._resparse()
377
378 for item in self._instloops:
379 imgfile = os.path.join(self.__imgdir, item['name'])
380 if item['fstype'] == "ext4":
381 runner.show('/sbin/tune2fs -O ^huge_file,extents,uninit_bg %s '
382 % imgfile)
383 if self.compress_image:
384 misc.compressing(imgfile, self.compress_image)
385
386 if not self.pack_to:
387 for item in os.listdir(self.__imgdir):
388 shutil.move(os.path.join(self.__imgdir, item),
389 os.path.join(self._outdir, item))
390 else:
391 msger.info("Pack all loop images together to %s" % self.pack_to)
392 dstfile = os.path.join(self._outdir, self.pack_to)
393 misc.packing(dstfile, self.__imgdir)
394
395 if self.pack_to:
396 mountfp_xml = os.path.splitext(self.pack_to)[0]
397 mountfp_xml = misc.strip_end(mountfp_xml, '.tar') + ".xml"
398 else:
399 mountfp_xml = self.name + ".xml"
400 # save mount points mapping file to xml
401 save_mountpoints(os.path.join(self._outdir, mountfp_xml),
402 self._instloops,
403 self.target_arch)
404
405 def copy_attachment(self):
406 if not hasattr(self, '_attachment') or not self._attachment:
407 return
408
409 self._check_imgdir()
410
411 msger.info("Copying attachment files...")
412 for item in self._attachment:
413 if not os.path.exists(item):
414 continue
415 dpath = os.path.join(self.__imgdir, os.path.basename(item))
416 msger.verbose("Copy attachment %s to %s" % (item, dpath))
417 shutil.copy(item, dpath)
418
diff --git a/scripts/lib/mic/imager/raw.py b/scripts/lib/mic/imager/raw.py
new file mode 100644
index 0000000000..838191a6f1
--- /dev/null
+++ b/scripts/lib/mic/imager/raw.py
@@ -0,0 +1,501 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import stat
20import shutil
21
22from mic import kickstart, msger
23from mic.utils import fs_related, runner, misc
24from mic.utils.partitionedfs import PartitionedMount
25from mic.utils.errors import CreatorError, MountError
26from mic.imager.baseimager import BaseImageCreator
27
28
29class RawImageCreator(BaseImageCreator):
30 """Installs a system into a file containing a partitioned disk image.
31
32 ApplianceImageCreator is an advanced ImageCreator subclass; a sparse file
33 is formatted with a partition table, each partition loopback mounted
34 and the system installed into an virtual disk. The disk image can
35 subsequently be booted in a virtual machine or accessed with kpartx
36 """
37
38 def __init__(self, creatoropts=None, pkgmgr=None, compress_image=None, generate_bmap=None, fstab_entry="uuid"):
39 """Initialize a ApplianceImageCreator instance.
40
41 This method takes the same arguments as ImageCreator.__init__()
42 """
43 BaseImageCreator.__init__(self, creatoropts, pkgmgr)
44
45 self.__instloop = None
46 self.__imgdir = None
47 self.__disks = {}
48 self.__disk_format = "raw"
49 self._disk_names = []
50 self._ptable_format = self.ks.handler.bootloader.ptable
51 self.vmem = 512
52 self.vcpu = 1
53 self.checksum = False
54 self.use_uuid = fstab_entry == "uuid"
55 self.appliance_version = None
56 self.appliance_release = None
57 self.compress_image = compress_image
58 self.bmap_needed = generate_bmap
59 self._need_extlinux = not kickstart.use_installerfw(self.ks, "extlinux")
60 #self.getsource = False
61 #self.listpkg = False
62
63 self._dep_checks.extend(["sync", "kpartx", "parted"])
64 if self._need_extlinux:
65 self._dep_checks.extend(["extlinux"])
66
67 def configure(self, repodata = None):
68 import subprocess
69 def chroot():
70 os.chroot(self._instroot)
71 os.chdir("/")
72
73 if os.path.exists(self._instroot + "/usr/bin/Xorg"):
74 subprocess.call(["/bin/chmod", "u+s", "/usr/bin/Xorg"],
75 preexec_fn = chroot)
76
77 BaseImageCreator.configure(self, repodata)
78
79 def _get_fstab(self):
80 if kickstart.use_installerfw(self.ks, "fstab"):
81 # The fstab file will be generated by installer framework scripts
82 # instead.
83 return None
84
85 s = ""
86 for mp in self.__instloop.mountOrder:
87 p = None
88 for p1 in self.__instloop.partitions:
89 if p1['mountpoint'] == mp:
90 p = p1
91 break
92
93 if self.use_uuid and p['uuid']:
94 device = "UUID=%s" % p['uuid']
95 else:
96 device = "/dev/%s%-d" % (p['disk_name'], p['num'])
97
98 s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % {
99 'device': device,
100 'mountpoint': p['mountpoint'],
101 'fstype': p['fstype'],
102 'fsopts': "defaults,noatime" if not p['fsopts'] else p['fsopts']}
103
104 if p['mountpoint'] == "/":
105 for subvol in self.__instloop.subvolumes:
106 if subvol['mountpoint'] == "/":
107 continue
108 s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % {
109 'device': "/dev/%s%-d" % (p['disk_name'], p['num']),
110 'mountpoint': subvol['mountpoint'],
111 'fstype': p['fstype'],
112 'fsopts': "defaults,noatime" if not subvol['fsopts'] else subvol['fsopts']}
113
114 s += "devpts /dev/pts devpts gid=5,mode=620 0 0\n"
115 s += "tmpfs /dev/shm tmpfs defaults 0 0\n"
116 s += "proc /proc proc defaults 0 0\n"
117 s += "sysfs /sys sysfs defaults 0 0\n"
118 return s
119
120 def _create_mkinitrd_config(self):
121 """write to tell which modules to be included in initrd"""
122
123 mkinitrd = ""
124 mkinitrd += "PROBE=\"no\"\n"
125 mkinitrd += "MODULES+=\"ext3 ata_piix sd_mod libata scsi_mod\"\n"
126 mkinitrd += "rootfs=\"ext3\"\n"
127 mkinitrd += "rootopts=\"defaults\"\n"
128
129 msger.debug("Writing mkinitrd config %s/etc/sysconfig/mkinitrd" \
130 % self._instroot)
131 os.makedirs(self._instroot + "/etc/sysconfig/",mode=644)
132 cfg = open(self._instroot + "/etc/sysconfig/mkinitrd", "w")
133 cfg.write(mkinitrd)
134 cfg.close()
135
136 def _get_parts(self):
137 if not self.ks:
138 raise CreatorError("Failed to get partition info, "
139 "please check your kickstart setting.")
140
141 # Set a default partition if no partition is given out
142 if not self.ks.handler.partition.partitions:
143 partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
144 args = partstr.split()
145 pd = self.ks.handler.partition.parse(args[1:])
146 if pd not in self.ks.handler.partition.partitions:
147 self.ks.handler.partition.partitions.append(pd)
148
149 # partitions list from kickstart file
150 return kickstart.get_partitions(self.ks)
151
152 def get_disk_names(self):
153 """ Returns a list of physical target disk names (e.g., 'sdb') which
154 will be created. """
155
156 if self._disk_names:
157 return self._disk_names
158
159 #get partition info from ks handler
160 parts = self._get_parts()
161
162 for i in range(len(parts)):
163 if parts[i].disk:
164 disk_name = parts[i].disk
165 else:
166 raise CreatorError("Failed to create disks, no --ondisk "
167 "specified in partition line of ks file")
168
169 if parts[i].mountpoint and not parts[i].fstype:
170 raise CreatorError("Failed to create disks, no --fstype "
171 "specified for partition with mountpoint "
172 "'%s' in the ks file")
173
174 self._disk_names.append(disk_name)
175
176 return self._disk_names
177
178 def _full_name(self, name, extention):
179 """ Construct full file name for a file we generate. """
180 return "%s-%s.%s" % (self.name, name, extention)
181
182 def _full_path(self, path, name, extention):
183 """ Construct full file path to a file we generate. """
184 return os.path.join(path, self._full_name(name, extention))
185
186 #
187 # Actual implemention
188 #
189 def _mount_instroot(self, base_on = None):
190 parts = self._get_parts()
191 self.__instloop = PartitionedMount(self._instroot)
192
193 for p in parts:
194 self.__instloop.add_partition(int(p.size),
195 p.disk,
196 p.mountpoint,
197 p.fstype,
198 p.label,
199 fsopts = p.fsopts,
200 boot = p.active,
201 align = p.align,
202 part_type = p.part_type)
203
204 self.__instloop.layout_partitions(self._ptable_format)
205
206 # Create the disks
207 self.__imgdir = self._mkdtemp()
208 for disk_name, disk in self.__instloop.disks.items():
209 full_path = self._full_path(self.__imgdir, disk_name, "raw")
210 msger.debug("Adding disk %s as %s with size %s bytes" \
211 % (disk_name, full_path, disk['min_size']))
212
213 disk_obj = fs_related.SparseLoopbackDisk(full_path,
214 disk['min_size'])
215 self.__disks[disk_name] = disk_obj
216 self.__instloop.add_disk(disk_name, disk_obj)
217
218 self.__instloop.mount()
219 self._create_mkinitrd_config()
220
221 def _get_required_packages(self):
222 required_packages = BaseImageCreator._get_required_packages(self)
223 if self._need_extlinux:
224 if not self.target_arch or not self.target_arch.startswith("arm"):
225 required_packages += ["syslinux", "syslinux-extlinux"]
226 return required_packages
227
228 def _get_excluded_packages(self):
229 return BaseImageCreator._get_excluded_packages(self)
230
231 def _get_syslinux_boot_config(self):
232 rootdev = None
233 root_part_uuid = None
234 for p in self.__instloop.partitions:
235 if p['mountpoint'] == "/":
236 rootdev = "/dev/%s%-d" % (p['disk_name'], p['num'])
237 root_part_uuid = p['partuuid']
238
239 return (rootdev, root_part_uuid)
240
241 def _create_syslinux_config(self):
242
243 splash = os.path.join(self._instroot, "boot/extlinux")
244 if os.path.exists(splash):
245 splashline = "menu background splash.jpg"
246 else:
247 splashline = ""
248
249 (rootdev, root_part_uuid) = self._get_syslinux_boot_config()
250 options = self.ks.handler.bootloader.appendLine
251
252 #XXX don't hardcode default kernel - see livecd code
253 syslinux_conf = ""
254 syslinux_conf += "prompt 0\n"
255 syslinux_conf += "timeout 1\n"
256 syslinux_conf += "\n"
257 syslinux_conf += "default vesamenu.c32\n"
258 syslinux_conf += "menu autoboot Starting %s...\n" % self.distro_name
259 syslinux_conf += "menu hidden\n"
260 syslinux_conf += "\n"
261 syslinux_conf += "%s\n" % splashline
262 syslinux_conf += "menu title Welcome to %s!\n" % self.distro_name
263 syslinux_conf += "menu color border 0 #ffffffff #00000000\n"
264 syslinux_conf += "menu color sel 7 #ffffffff #ff000000\n"
265 syslinux_conf += "menu color title 0 #ffffffff #00000000\n"
266 syslinux_conf += "menu color tabmsg 0 #ffffffff #00000000\n"
267 syslinux_conf += "menu color unsel 0 #ffffffff #00000000\n"
268 syslinux_conf += "menu color hotsel 0 #ff000000 #ffffffff\n"
269 syslinux_conf += "menu color hotkey 7 #ffffffff #ff000000\n"
270 syslinux_conf += "menu color timeout_msg 0 #ffffffff #00000000\n"
271 syslinux_conf += "menu color timeout 0 #ffffffff #00000000\n"
272 syslinux_conf += "menu color cmdline 0 #ffffffff #00000000\n"
273
274 versions = []
275 kernels = self._get_kernel_versions()
276 symkern = "%s/boot/vmlinuz" % self._instroot
277
278 if os.path.lexists(symkern):
279 v = os.path.realpath(symkern).replace('%s-' % symkern, "")
280 syslinux_conf += "label %s\n" % self.distro_name.lower()
281 syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v)
282 syslinux_conf += "\tlinux ../vmlinuz\n"
283 if self._ptable_format == 'msdos':
284 rootstr = rootdev
285 else:
286 if not root_part_uuid:
287 raise MountError("Cannot find the root GPT partition UUID")
288 rootstr = "PARTUUID=%s" % root_part_uuid
289 syslinux_conf += "\tappend ro root=%s %s\n" % (rootstr, options)
290 syslinux_conf += "\tmenu default\n"
291 else:
292 for kernel in kernels:
293 for version in kernels[kernel]:
294 versions.append(version)
295
296 footlabel = 0
297 for v in versions:
298 syslinux_conf += "label %s%d\n" \
299 % (self.distro_name.lower(), footlabel)
300 syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v)
301 syslinux_conf += "\tlinux ../vmlinuz-%s\n" % v
302 syslinux_conf += "\tappend ro root=%s %s\n" \
303 % (rootdev, options)
304 if footlabel == 0:
305 syslinux_conf += "\tmenu default\n"
306 footlabel += 1;
307
308 msger.debug("Writing syslinux config %s/boot/extlinux/extlinux.conf" \
309 % self._instroot)
310 cfg = open(self._instroot + "/boot/extlinux/extlinux.conf", "w")
311 cfg.write(syslinux_conf)
312 cfg.close()
313
314 def _install_syslinux(self):
315 for name in self.__disks.keys():
316 loopdev = self.__disks[name].device
317
318 # Set MBR
319 mbrfile = "%s/usr/share/syslinux/" % self._instroot
320 if self._ptable_format == 'gpt':
321 mbrfile += "gptmbr.bin"
322 else:
323 mbrfile += "mbr.bin"
324
325 msger.debug("Installing syslinux bootloader '%s' to %s" % \
326 (mbrfile, loopdev))
327
328 mbrsize = os.stat(mbrfile)[stat.ST_SIZE]
329 rc = runner.show(['dd', 'if=%s' % mbrfile, 'of=' + loopdev])
330 if rc != 0:
331 raise MountError("Unable to set MBR to %s" % loopdev)
332
333
334 # Ensure all data is flushed to disk before doing syslinux install
335 runner.quiet('sync')
336
337 fullpathsyslinux = fs_related.find_binary_path("extlinux")
338 rc = runner.show([fullpathsyslinux,
339 "-i",
340 "%s/boot/extlinux" % self._instroot])
341 if rc != 0:
342 raise MountError("Unable to install syslinux bootloader to %s" \
343 % loopdev)
344
345 def _create_bootconfig(self):
346 #If syslinux is available do the required configurations.
347 if self._need_extlinux \
348 and os.path.exists("%s/usr/share/syslinux/" % (self._instroot)) \
349 and os.path.exists("%s/boot/extlinux/" % (self._instroot)):
350 self._create_syslinux_config()
351 self._install_syslinux()
352
353 def _unmount_instroot(self):
354 if not self.__instloop is None:
355 try:
356 self.__instloop.cleanup()
357 except MountError, err:
358 msger.warning("%s" % err)
359
360 def _resparse(self, size = None):
361 return self.__instloop.resparse(size)
362
363 def _get_post_scripts_env(self, in_chroot):
364 env = BaseImageCreator._get_post_scripts_env(self, in_chroot)
365
366 # Export the file-system UUIDs and partition UUIDs (AKA PARTUUIDs)
367 for p in self.__instloop.partitions:
368 env.update(self._set_part_env(p['ks_pnum'], "UUID", p['uuid']))
369 env.update(self._set_part_env(p['ks_pnum'], "PARTUUID", p['partuuid']))
370
371 return env
372
373 def _stage_final_image(self):
374 """Stage the final system image in _outdir.
375 write meta data
376 """
377 self._resparse()
378
379 if self.compress_image:
380 for imgfile in os.listdir(self.__imgdir):
381 if imgfile.endswith('.raw') or imgfile.endswith('bin'):
382 imgpath = os.path.join(self.__imgdir, imgfile)
383 misc.compressing(imgpath, self.compress_image)
384
385 if self.pack_to:
386 dst = os.path.join(self._outdir, self.pack_to)
387 msger.info("Pack all raw images to %s" % dst)
388 misc.packing(dst, self.__imgdir)
389 else:
390 msger.debug("moving disks to stage location")
391 for imgfile in os.listdir(self.__imgdir):
392 src = os.path.join(self.__imgdir, imgfile)
393 dst = os.path.join(self._outdir, imgfile)
394 msger.debug("moving %s to %s" % (src,dst))
395 shutil.move(src,dst)
396 self._write_image_xml()
397
398 def _write_image_xml(self):
399 imgarch = "i686"
400 if self.target_arch and self.target_arch.startswith("arm"):
401 imgarch = "arm"
402 xml = "<image>\n"
403
404 name_attributes = ""
405 if self.appliance_version:
406 name_attributes += " version='%s'" % self.appliance_version
407 if self.appliance_release:
408 name_attributes += " release='%s'" % self.appliance_release
409 xml += " <name%s>%s</name>\n" % (name_attributes, self.name)
410 xml += " <domain>\n"
411 # XXX don't hardcode - determine based on the kernel we installed for
412 # grub baremetal vs xen
413 xml += " <boot type='hvm'>\n"
414 xml += " <guest>\n"
415 xml += " <arch>%s</arch>\n" % imgarch
416 xml += " </guest>\n"
417 xml += " <os>\n"
418 xml += " <loader dev='hd'/>\n"
419 xml += " </os>\n"
420
421 i = 0
422 for name in self.__disks.keys():
423 full_name = self._full_name(name, self.__disk_format)
424 xml += " <drive disk='%s' target='hd%s'/>\n" \
425 % (full_name, chr(ord('a') + i))
426 i = i + 1
427
428 xml += " </boot>\n"
429 xml += " <devices>\n"
430 xml += " <vcpu>%s</vcpu>\n" % self.vcpu
431 xml += " <memory>%d</memory>\n" %(self.vmem * 1024)
432 for network in self.ks.handler.network.network:
433 xml += " <interface/>\n"
434 xml += " <graphics/>\n"
435 xml += " </devices>\n"
436 xml += " </domain>\n"
437 xml += " <storage>\n"
438
439 if self.checksum is True:
440 for name in self.__disks.keys():
441 diskpath = self._full_path(self._outdir, name, \
442 self.__disk_format)
443 full_name = self._full_name(name, self.__disk_format)
444
445 msger.debug("Generating disk signature for %s" % full_name)
446
447 xml += " <disk file='%s' use='system' format='%s'>\n" \
448 % (full_name, self.__disk_format)
449
450 hashes = misc.calc_hashes(diskpath, ('sha1', 'sha256'))
451
452 xml += " <checksum type='sha1'>%s</checksum>\n" \
453 % hashes[0]
454 xml += " <checksum type='sha256'>%s</checksum>\n" \
455 % hashes[1]
456 xml += " </disk>\n"
457 else:
458 for name in self.__disks.keys():
459 full_name = self._full_name(name, self.__disk_format)
460 xml += " <disk file='%s' use='system' format='%s'/>\n" \
461 % (full_name, self.__disk_format)
462
463 xml += " </storage>\n"
464 xml += "</image>\n"
465
466 msger.debug("writing image XML to %s/%s.xml" %(self._outdir, self.name))
467 cfg = open("%s/%s.xml" % (self._outdir, self.name), "w")
468 cfg.write(xml)
469 cfg.close()
470
471 def generate_bmap(self):
472 """ Generate block map file for the image. The idea is that while disk
473 images we generate may be large (e.g., 4GiB), they may actually contain
474 only little real data, e.g., 512MiB. This data are files, directories,
475 file-system meta-data, partition table, etc. In other words, when
476 flashing the image to the target device, you do not have to copy all the
477 4GiB of data, you can copy only 512MiB of it, which is 4 times faster.
478
479 This function generates the block map file for an arbitrary image that
480 mic has generated. The block map file is basically an XML file which
481 contains a list of blocks which have to be copied to the target device.
482 The other blocks are not used and there is no need to copy them. """
483
484 if self.bmap_needed is None:
485 return
486
487 from mic.utils import BmapCreate
488 msger.info("Generating the map file(s)")
489
490 for name in self.__disks.keys():
491 image = self._full_path(self.__imgdir, name, self.__disk_format)
492 bmap_file = self._full_path(self._outdir, name, "bmap")
493
494 msger.debug("Generating block map file '%s'" % bmap_file)
495
496 try:
497 creator = BmapCreate.BmapCreate(image, bmap_file)
498 creator.generate()
499 del creator
500 except BmapCreate.Error as err:
501 raise CreatorError("Failed to create bmap file: %s" % str(err))
diff --git a/scripts/lib/mic/kickstart/__init__.py b/scripts/lib/mic/kickstart/__init__.py
new file mode 100644
index 0000000000..f9a53343d1
--- /dev/null
+++ b/scripts/lib/mic/kickstart/__init__.py
@@ -0,0 +1,892 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2007 Red Hat, Inc.
4# Copyright (c) 2009, 2010, 2011 Intel, Inc.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the Free
8# Software Foundation; version 2 of the License
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13# for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc., 59
17# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19import os, sys, re
20import shutil
21import subprocess
22import string
23
24import pykickstart.sections as kssections
25import pykickstart.commands as kscommands
26import pykickstart.constants as ksconstants
27import pykickstart.errors as kserrors
28import pykickstart.parser as ksparser
29import pykickstart.version as ksversion
30from pykickstart.handlers.control import commandMap
31from pykickstart.handlers.control import dataMap
32
33from mic import msger
34from mic.utils import errors, misc, runner, fs_related as fs
35from custom_commands import desktop, micrepo, micboot, partition, installerfw
36
37
38AUTH_URL_PTN = r"(?P<scheme>.*)://(?P<username>.*)(:?P<password>.*)?@(?P<url>.*)"
39
40
41class PrepackageSection(kssections.Section):
42 sectionOpen = "%prepackages"
43
44 def handleLine(self, line):
45 if not self.handler:
46 return
47
48 (h, s, t) = line.partition('#')
49 line = h.rstrip()
50
51 self.handler.prepackages.add([line])
52
53 def handleHeader(self, lineno, args):
54 kssections.Section.handleHeader(self, lineno, args)
55
56class AttachmentSection(kssections.Section):
57 sectionOpen = "%attachment"
58
59 def handleLine(self, line):
60 if not self.handler:
61 return
62
63 (h, s, t) = line.partition('#')
64 line = h.rstrip()
65
66 self.handler.attachment.add([line])
67
68 def handleHeader(self, lineno, args):
69 kssections.Section.handleHeader(self, lineno, args)
70
71def apply_wrapper(func):
72 def wrapper(*kargs, **kwargs):
73 try:
74 func(*kargs, **kwargs)
75 except (OSError, IOError, errors.KsError), err:
76 cfgcls = kargs[0].__class__.__name__
77 if msger.ask("Failed to apply %s, skip and continue?" % cfgcls):
78 msger.warning("%s" % err)
79 pass
80 else:
81 # just throw out the exception
82 raise
83 return wrapper
84
85def read_kickstart(path):
86 """Parse a kickstart file and return a KickstartParser instance.
87
88 This is a simple utility function which takes a path to a kickstart file,
89 parses it and returns a pykickstart KickstartParser instance which can
90 be then passed to an ImageCreator constructor.
91
92 If an error occurs, a CreatorError exception is thrown.
93 """
94
95 #version = ksversion.makeVersion()
96 #ks = ksparser.KickstartParser(version)
97
98 using_version = ksversion.DEVEL
99 commandMap[using_version]["desktop"] = desktop.Mic_Desktop
100 commandMap[using_version]["repo"] = micrepo.Mic_Repo
101 commandMap[using_version]["bootloader"] = micboot.Mic_Bootloader
102 commandMap[using_version]["part"] = partition.Mic_Partition
103 commandMap[using_version]["partition"] = partition.Mic_Partition
104 commandMap[using_version]["installerfw"] = installerfw.Mic_installerfw
105 dataMap[using_version]["RepoData"] = micrepo.Mic_RepoData
106 dataMap[using_version]["PartData"] = partition.Mic_PartData
107 superclass = ksversion.returnClassForVersion(version=using_version)
108
109 class KSHandlers(superclass):
110 def __init__(self):
111 superclass.__init__(self, mapping=commandMap[using_version])
112 self.prepackages = ksparser.Packages()
113 self.attachment = ksparser.Packages()
114
115 ks = ksparser.KickstartParser(KSHandlers(), errorsAreFatal=False)
116 ks.registerSection(PrepackageSection(ks.handler))
117 ks.registerSection(AttachmentSection(ks.handler))
118
119 try:
120 ks.readKickstart(path)
121 except (kserrors.KickstartParseError, kserrors.KickstartError), err:
122 if msger.ask("Errors occured on kickstart file, skip and continue?"):
123 msger.warning("%s" % err)
124 pass
125 else:
126 raise errors.KsError("%s" % err)
127
128 return ks
129
130class KickstartConfig(object):
131 """A base class for applying kickstart configurations to a system."""
132 def __init__(self, instroot):
133 self.instroot = instroot
134
135 def path(self, subpath):
136 return self.instroot + subpath
137
138 def _check_sysconfig(self):
139 if not os.path.exists(self.path("/etc/sysconfig")):
140 fs.makedirs(self.path("/etc/sysconfig"))
141
142 def chroot(self):
143 os.chroot(self.instroot)
144 os.chdir("/")
145
146 def call(self, args):
147 if not os.path.exists("%s/%s" %(self.instroot, args[0])):
148 raise errors.KsError("Can't find %s in chroot" % args[0])
149 subprocess.call(args, preexec_fn = self.chroot)
150
151 def apply(self):
152 pass
153
154class LanguageConfig(KickstartConfig):
155 """A class to apply a kickstart language configuration to a system."""
156 @apply_wrapper
157 def apply(self, kslang):
158 self._check_sysconfig()
159 if kslang.lang:
160 f = open(self.path("/etc/sysconfig/i18n"), "w+")
161 f.write("LANG=\"" + kslang.lang + "\"\n")
162 f.close()
163
164class KeyboardConfig(KickstartConfig):
165 """A class to apply a kickstart keyboard configuration to a system."""
166 @apply_wrapper
167 def apply(self, kskeyboard):
168 #
169 # FIXME:
170 # should this impact the X keyboard config too?
171 # or do we want to make X be able to do this mapping?
172 #
173 #k = rhpl.keyboard.Keyboard()
174 #if kskeyboard.keyboard:
175 # k.set(kskeyboard.keyboard)
176 #k.write(self.instroot)
177 pass
178
179class TimezoneConfig(KickstartConfig):
180 """A class to apply a kickstart timezone configuration to a system."""
181 @apply_wrapper
182 def apply(self, kstimezone):
183 self._check_sysconfig()
184 tz = kstimezone.timezone or "America/New_York"
185 utc = str(kstimezone.isUtc)
186
187 f = open(self.path("/etc/sysconfig/clock"), "w+")
188 f.write("ZONE=\"" + tz + "\"\n")
189 f.write("UTC=" + utc + "\n")
190 f.close()
191 tz_source = "/usr/share/zoneinfo/%s" % (tz)
192 tz_dest = "/etc/localtime"
193 try:
194 cpcmd = fs.find_binary_inchroot('cp', self.instroot)
195 if cpcmd:
196 self.call([cpcmd, "-f", tz_source, tz_dest])
197 else:
198 cpcmd = fs.find_binary_path('cp')
199 subprocess.call([cpcmd, "-f",
200 self.path(tz_source),
201 self.path(tz_dest)])
202 except (IOError, OSError), (errno, msg):
203 raise errors.KsError("Timezone setting error: %s" % msg)
204
205class AuthConfig(KickstartConfig):
206 """A class to apply a kickstart authconfig configuration to a system."""
207 @apply_wrapper
208 def apply(self, ksauthconfig):
209 auth = ksauthconfig.authconfig or "--useshadow --enablemd5"
210 args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"]
211 self.call(args + auth.split())
212
213class FirewallConfig(KickstartConfig):
214 """A class to apply a kickstart firewall configuration to a system."""
215 @apply_wrapper
216 def apply(self, ksfirewall):
217 #
218 # FIXME: should handle the rest of the options
219 #
220 if not os.path.exists(self.path("/usr/sbin/lokkit")):
221 return
222 if ksfirewall.enabled:
223 status = "--enabled"
224 else:
225 status = "--disabled"
226
227 self.call(["/usr/sbin/lokkit",
228 "-f", "--quiet", "--nostart", status])
229
230class RootPasswordConfig(KickstartConfig):
231 """A class to apply a kickstart root password configuration to a system."""
232 def unset(self):
233 self.call(["/usr/bin/passwd", "-d", "root"])
234
235 def set_encrypted(self, password):
236 self.call(["/usr/sbin/usermod", "-p", password, "root"])
237
238 def set_unencrypted(self, password):
239 for p in ("/bin/echo", "/usr/sbin/chpasswd"):
240 if not os.path.exists("%s/%s" %(self.instroot, p)):
241 raise errors.KsError("Unable to set unencrypted password due "
242 "to lack of %s" % p)
243
244 p1 = subprocess.Popen(["/bin/echo", "root:%s" %password],
245 stdout = subprocess.PIPE,
246 preexec_fn = self.chroot)
247 p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
248 stdin = p1.stdout,
249 stdout = subprocess.PIPE,
250 preexec_fn = self.chroot)
251 p2.communicate()
252
253 @apply_wrapper
254 def apply(self, ksrootpw):
255 if ksrootpw.isCrypted:
256 self.set_encrypted(ksrootpw.password)
257 elif ksrootpw.password != "":
258 self.set_unencrypted(ksrootpw.password)
259 else:
260 self.unset()
261
262class UserConfig(KickstartConfig):
263 def set_empty_passwd(self, user):
264 self.call(["/usr/bin/passwd", "-d", user])
265
266 def set_encrypted_passwd(self, user, password):
267 self.call(["/usr/sbin/usermod", "-p", "%s" % password, user])
268
269 def set_unencrypted_passwd(self, user, password):
270 for p in ("/bin/echo", "/usr/sbin/chpasswd"):
271 if not os.path.exists("%s/%s" %(self.instroot, p)):
272 raise errors.KsError("Unable to set unencrypted password due "
273 "to lack of %s" % p)
274
275 p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)],
276 stdout = subprocess.PIPE,
277 preexec_fn = self.chroot)
278 p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
279 stdin = p1.stdout,
280 stdout = subprocess.PIPE,
281 preexec_fn = self.chroot)
282 p2.communicate()
283
284 def addUser(self, userconfig):
285 args = [ "/usr/sbin/useradd" ]
286 if userconfig.groups:
287 args += [ "--groups", string.join(userconfig.groups, ",") ]
288 if userconfig.name:
289 args += [ "-m"]
290 args += [ "-d", "/home/%s" % userconfig.name ]
291 args.append(userconfig.name)
292 try:
293 dev_null = os.open("/dev/null", os.O_WRONLY)
294 msger.debug('adding user with %s' % args)
295 subprocess.call(args,
296 stdout = dev_null,
297 stderr = dev_null,
298 preexec_fn = self.chroot)
299 os.close(dev_null)
300 except:
301 msger.warning('Cannot add user using "useradd"')
302
303 if userconfig.password not in (None, ""):
304 if userconfig.isCrypted:
305 self.set_encrypted_passwd(userconfig.name,
306 userconfig.password)
307 else:
308 self.set_unencrypted_passwd(userconfig.name,
309 userconfig.password)
310 else:
311 self.set_empty_passwd(userconfig.name)
312 else:
313 raise errors.KsError("Invalid kickstart command: %s" \
314 % userconfig.__str__())
315
316 @apply_wrapper
317 def apply(self, user):
318 for userconfig in user.userList:
319 self.addUser(userconfig)
320
321class ServicesConfig(KickstartConfig):
322 """A class to apply a kickstart services configuration to a system."""
323 @apply_wrapper
324 def apply(self, ksservices):
325 if not os.path.exists(self.path("/sbin/chkconfig")):
326 return
327 for s in ksservices.enabled:
328 self.call(["/sbin/chkconfig", s, "on"])
329 for s in ksservices.disabled:
330 self.call(["/sbin/chkconfig", s, "off"])
331
332class XConfig(KickstartConfig):
333 """A class to apply a kickstart X configuration to a system."""
334 @apply_wrapper
335 def apply(self, ksxconfig):
336 if ksxconfig.startX and os.path.exists(self.path("/etc/inittab")):
337 f = open(self.path("/etc/inittab"), "rw+")
338 buf = f.read()
339 buf = buf.replace("id:3:initdefault", "id:5:initdefault")
340 f.seek(0)
341 f.write(buf)
342 f.close()
343 if ksxconfig.defaultdesktop:
344 self._check_sysconfig()
345 f = open(self.path("/etc/sysconfig/desktop"), "w")
346 f.write("DESKTOP="+ksxconfig.defaultdesktop+"\n")
347 f.close()
348
349class DesktopConfig(KickstartConfig):
350 """A class to apply a kickstart desktop configuration to a system."""
351 @apply_wrapper
352 def apply(self, ksdesktop):
353 if ksdesktop.defaultdesktop:
354 self._check_sysconfig()
355 f = open(self.path("/etc/sysconfig/desktop"), "w")
356 f.write("DESKTOP="+ksdesktop.defaultdesktop+"\n")
357 f.close()
358 if os.path.exists(self.path("/etc/gdm/custom.conf")):
359 f = open(self.path("/etc/skel/.dmrc"), "w")
360 f.write("[Desktop]\n")
361 f.write("Session="+ksdesktop.defaultdesktop.lower()+"\n")
362 f.close()
363 if ksdesktop.session:
364 if os.path.exists(self.path("/etc/sysconfig/uxlaunch")):
365 f = open(self.path("/etc/sysconfig/uxlaunch"), "a+")
366 f.write("session="+ksdesktop.session.lower()+"\n")
367 f.close()
368 if ksdesktop.autologinuser:
369 self._check_sysconfig()
370 f = open(self.path("/etc/sysconfig/desktop"), "a+")
371 f.write("AUTOLOGIN_USER=" + ksdesktop.autologinuser + "\n")
372 f.close()
373 if os.path.exists(self.path("/etc/gdm/custom.conf")):
374 f = open(self.path("/etc/gdm/custom.conf"), "w")
375 f.write("[daemon]\n")
376 f.write("AutomaticLoginEnable=true\n")
377 f.write("AutomaticLogin=" + ksdesktop.autologinuser + "\n")
378 f.close()
379
380class MoblinRepoConfig(KickstartConfig):
381 """A class to apply a kickstart desktop configuration to a system."""
382 def __create_repo_section(self, repo, type, fd):
383 baseurl = None
384 mirrorlist = None
385 reposuffix = {"base":"", "debuginfo":"-debuginfo", "source":"-source"}
386 reponame = repo.name + reposuffix[type]
387 if type == "base":
388 if repo.baseurl:
389 baseurl = repo.baseurl
390 if repo.mirrorlist:
391 mirrorlist = repo.mirrorlist
392
393 elif type == "debuginfo":
394 if repo.baseurl:
395 if repo.baseurl.endswith("/"):
396 baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
397 else:
398 baseurl = os.path.dirname(repo.baseurl)
399 baseurl += "/debug"
400
401 if repo.mirrorlist:
402 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
403 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
404 mirrorlist += "debug" + "-" + variant
405
406 elif type == "source":
407 if repo.baseurl:
408 if repo.baseurl.endswith("/"):
409 baseurl = os.path.dirname(
410 os.path.dirname(
411 os.path.dirname(repo.baseurl)))
412 else:
413 baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
414 baseurl += "/source"
415
416 if repo.mirrorlist:
417 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
418 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
419 mirrorlist += "source" + "-" + variant
420
421 fd.write("[" + reponame + "]\n")
422 fd.write("name=" + reponame + "\n")
423 fd.write("failovermethod=priority\n")
424 if baseurl:
425 auth_url = re.compile(AUTH_URL_PTN)
426 m = auth_url.match(baseurl)
427 if m:
428 baseurl = "%s://%s" % (m.group('scheme'), m.group('url'))
429 fd.write("baseurl=" + baseurl + "\n")
430 if mirrorlist:
431 fd.write("mirrorlist=" + mirrorlist + "\n")
432 """ Skip saving proxy settings """
433 #if repo.proxy:
434 # fd.write("proxy=" + repo.proxy + "\n")
435 #if repo.proxy_username:
436 # fd.write("proxy_username=" + repo.proxy_username + "\n")
437 #if repo.proxy_password:
438 # fd.write("proxy_password=" + repo.proxy_password + "\n")
439 if repo.gpgkey:
440 fd.write("gpgkey=" + repo.gpgkey + "\n")
441 fd.write("gpgcheck=1\n")
442 else:
443 fd.write("gpgcheck=0\n")
444 if type == "source" or type == "debuginfo" or repo.disable:
445 fd.write("enabled=0\n")
446 else:
447 fd.write("enabled=1\n")
448 fd.write("\n")
449
450 def __create_repo_file(self, repo, repodir):
451 fs.makedirs(self.path(repodir))
452 f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
453 self.__create_repo_section(repo, "base", f)
454 if repo.debuginfo:
455 self.__create_repo_section(repo, "debuginfo", f)
456 if repo.source:
457 self.__create_repo_section(repo, "source", f)
458 f.close()
459
460 @apply_wrapper
461 def apply(self, ksrepo, repodata, repourl):
462 for repo in ksrepo.repoList:
463 if repo.name in repourl:
464 repo.baseurl = repourl[repo.name]
465 if repo.save:
466 #self.__create_repo_file(repo, "/etc/yum.repos.d")
467 self.__create_repo_file(repo, "/etc/zypp/repos.d")
468 """ Import repo gpg keys """
469 if repodata:
470 for repo in repodata:
471 if repo['repokey']:
472 runner.quiet(['rpm',
473 "--root=%s" % self.instroot,
474 "--import",
475 repo['repokey']])
476
477class RPMMacroConfig(KickstartConfig):
478 """A class to apply the specified rpm macros to the filesystem"""
479 @apply_wrapper
480 def apply(self, ks):
481 if not ks:
482 return
483 if not os.path.exists(self.path("/etc/rpm")):
484 os.mkdir(self.path("/etc/rpm"))
485 f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
486 if exclude_docs(ks):
487 f.write("%_excludedocs 1\n")
488 f.write("%__file_context_path %{nil}\n")
489 if inst_langs(ks) != None:
490 f.write("%_install_langs ")
491 f.write(inst_langs(ks))
492 f.write("\n")
493 f.close()
494
495class NetworkConfig(KickstartConfig):
496 """A class to apply a kickstart network configuration to a system."""
497 def write_ifcfg(self, network):
498 p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
499
500 f = file(p, "w+")
501 os.chmod(p, 0644)
502
503 f.write("DEVICE=%s\n" % network.device)
504 f.write("BOOTPROTO=%s\n" % network.bootProto)
505
506 if network.bootProto.lower() == "static":
507 if network.ip:
508 f.write("IPADDR=%s\n" % network.ip)
509 if network.netmask:
510 f.write("NETMASK=%s\n" % network.netmask)
511
512 if network.onboot:
513 f.write("ONBOOT=on\n")
514 else:
515 f.write("ONBOOT=off\n")
516
517 if network.essid:
518 f.write("ESSID=%s\n" % network.essid)
519
520 if network.ethtool:
521 if network.ethtool.find("autoneg") == -1:
522 network.ethtool = "autoneg off " + network.ethtool
523 f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
524
525 if network.bootProto.lower() == "dhcp":
526 if network.hostname:
527 f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
528 if network.dhcpclass:
529 f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
530
531 if network.mtu:
532 f.write("MTU=%s\n" % network.mtu)
533
534 f.close()
535
536 def write_wepkey(self, network):
537 if not network.wepkey:
538 return
539
540 p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
541 f = file(p, "w+")
542 os.chmod(p, 0600)
543 f.write("KEY=%s\n" % network.wepkey)
544 f.close()
545
546 def write_sysconfig(self, useipv6, hostname, gateway):
547 path = self.path("/etc/sysconfig/network")
548 f = file(path, "w+")
549 os.chmod(path, 0644)
550
551 f.write("NETWORKING=yes\n")
552
553 if useipv6:
554 f.write("NETWORKING_IPV6=yes\n")
555 else:
556 f.write("NETWORKING_IPV6=no\n")
557
558 if hostname:
559 f.write("HOSTNAME=%s\n" % hostname)
560 else:
561 f.write("HOSTNAME=localhost.localdomain\n")
562
563 if gateway:
564 f.write("GATEWAY=%s\n" % gateway)
565
566 f.close()
567
568 def write_hosts(self, hostname):
569 localline = ""
570 if hostname and hostname != "localhost.localdomain":
571 localline += hostname + " "
572 l = hostname.split(".")
573 if len(l) > 1:
574 localline += l[0] + " "
575 localline += "localhost.localdomain localhost"
576
577 path = self.path("/etc/hosts")
578 f = file(path, "w+")
579 os.chmod(path, 0644)
580 f.write("127.0.0.1\t\t%s\n" % localline)
581 f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
582 f.close()
583
584 def write_resolv(self, nodns, nameservers):
585 if nodns or not nameservers:
586 return
587
588 path = self.path("/etc/resolv.conf")
589 f = file(path, "w+")
590 os.chmod(path, 0644)
591
592 for ns in (nameservers):
593 if ns:
594 f.write("nameserver %s\n" % ns)
595
596 f.close()
597
598 @apply_wrapper
599 def apply(self, ksnet):
600 fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
601
602 useipv6 = False
603 nodns = False
604 hostname = None
605 gateway = None
606 nameservers = None
607
608 for network in ksnet.network:
609 if not network.device:
610 raise errors.KsError("No --device specified with "
611 "network kickstart command")
612
613 if (network.onboot and network.bootProto.lower() != "dhcp" and
614 not (network.ip and network.netmask)):
615 raise errors.KsError("No IP address and/or netmask "
616 "specified with static "
617 "configuration for '%s'" %
618 network.device)
619
620 self.write_ifcfg(network)
621 self.write_wepkey(network)
622
623 if network.ipv6:
624 useipv6 = True
625 if network.nodns:
626 nodns = True
627
628 if network.hostname:
629 hostname = network.hostname
630 if network.gateway:
631 gateway = network.gateway
632
633 if network.nameserver:
634 nameservers = network.nameserver.split(",")
635
636 self.write_sysconfig(useipv6, hostname, gateway)
637 self.write_hosts(hostname)
638 self.write_resolv(nodns, nameservers)
639
640def use_installerfw(ks, feature):
641 """ Check if the installer framework has to be used for a feature
642 "feature". """
643
644 features = ks.handler.installerfw.features
645 if features:
646 if feature in features or "all" in features:
647 return True
648 return False
649
650def get_image_size(ks, default = None):
651 __size = 0
652 for p in ks.handler.partition.partitions:
653 if p.mountpoint == "/" and p.size:
654 __size = p.size
655 if __size > 0:
656 return int(__size) * 1024L * 1024L
657 else:
658 return default
659
660def get_image_fstype(ks, default = None):
661 for p in ks.handler.partition.partitions:
662 if p.mountpoint == "/" and p.fstype:
663 return p.fstype
664 return default
665
666def get_image_fsopts(ks, default = None):
667 for p in ks.handler.partition.partitions:
668 if p.mountpoint == "/" and p.fsopts:
669 return p.fsopts
670 return default
671
672def get_modules(ks):
673 devices = []
674 if isinstance(ks.handler.device, kscommands.device.FC3_Device):
675 devices.append(ks.handler.device)
676 else:
677 devices.extend(ks.handler.device.deviceList)
678
679 modules = []
680 for device in devices:
681 if not device.moduleName:
682 continue
683 modules.extend(device.moduleName.split(":"))
684
685 return modules
686
687def get_timeout(ks, default = None):
688 if not hasattr(ks.handler.bootloader, "timeout"):
689 return default
690 if ks.handler.bootloader.timeout is None:
691 return default
692 return int(ks.handler.bootloader.timeout)
693
694def get_kernel_args(ks, default = "ro rd.live.image"):
695 if not hasattr(ks.handler.bootloader, "appendLine"):
696 return default
697 if ks.handler.bootloader.appendLine is None:
698 return default
699 return "%s %s" %(default, ks.handler.bootloader.appendLine)
700
701def get_menu_args(ks, default = ""):
702 if not hasattr(ks.handler.bootloader, "menus"):
703 return default
704 if ks.handler.bootloader.menus in (None, ""):
705 return default
706 return "%s" % ks.handler.bootloader.menus
707
708def get_default_kernel(ks, default = None):
709 if not hasattr(ks.handler.bootloader, "default"):
710 return default
711 if not ks.handler.bootloader.default:
712 return default
713 return ks.handler.bootloader.default
714
715def get_repos(ks, repo_urls=None):
716 repos = {}
717 for repo in ks.handler.repo.repoList:
718 inc = []
719 if hasattr(repo, "includepkgs"):
720 inc.extend(repo.includepkgs)
721
722 exc = []
723 if hasattr(repo, "excludepkgs"):
724 exc.extend(repo.excludepkgs)
725
726 baseurl = repo.baseurl
727 mirrorlist = repo.mirrorlist
728
729 if repo_urls and repo.name in repo_urls:
730 baseurl = repo_urls[repo.name]
731 mirrorlist = None
732
733 if repos.has_key(repo.name):
734 msger.warning("Overriding already specified repo %s" %(repo.name,))
735
736 proxy = None
737 if hasattr(repo, "proxy"):
738 proxy = repo.proxy
739 proxy_username = None
740 if hasattr(repo, "proxy_username"):
741 proxy_username = repo.proxy_username
742 proxy_password = None
743 if hasattr(repo, "proxy_password"):
744 proxy_password = repo.proxy_password
745 if hasattr(repo, "debuginfo"):
746 debuginfo = repo.debuginfo
747 if hasattr(repo, "source"):
748 source = repo.source
749 if hasattr(repo, "gpgkey"):
750 gpgkey = repo.gpgkey
751 if hasattr(repo, "disable"):
752 disable = repo.disable
753 ssl_verify = True
754 if hasattr(repo, "ssl_verify"):
755 ssl_verify = repo.ssl_verify == "yes"
756 nocache = False
757 if hasattr(repo, "nocache"):
758 nocache = repo.nocache
759 cost = None
760 if hasattr(repo, "cost"):
761 cost = repo.cost
762 priority = None
763 if hasattr(repo, "priority"):
764 priority = repo.priority
765
766 repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc,
767 proxy, proxy_username, proxy_password, debuginfo,
768 source, gpgkey, disable, ssl_verify, nocache,
769 cost, priority)
770
771 return repos.values()
772
773def convert_method_to_repo(ks):
774 try:
775 ks.handler.repo.methodToRepo()
776 except (AttributeError, kserrors.KickstartError):
777 pass
778
779def get_attachment(ks, required=()):
780 return ks.handler.attachment.packageList + list(required)
781
782def get_pre_packages(ks, required=()):
783 return ks.handler.prepackages.packageList + list(required)
784
785def get_packages(ks, required=()):
786 return ks.handler.packages.packageList + list(required)
787
788def get_groups(ks, required=()):
789 return ks.handler.packages.groupList + list(required)
790
791def get_excluded(ks, required=()):
792 return ks.handler.packages.excludedList + list(required)
793
794def get_partitions(ks):
795 return ks.handler.partition.partitions
796
797def ignore_missing(ks):
798 return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
799
800def exclude_docs(ks):
801 return ks.handler.packages.excludeDocs
802
803def inst_langs(ks):
804 if hasattr(ks.handler.packages, "instLange"):
805 return ks.handler.packages.instLange
806 elif hasattr(ks.handler.packages, "instLangs"):
807 return ks.handler.packages.instLangs
808 return ""
809
810def get_post_scripts(ks):
811 scripts = []
812 for s in ks.handler.scripts:
813 if s.type != ksparser.KS_SCRIPT_POST:
814 continue
815 scripts.append(s)
816 return scripts
817
818def add_repo(ks, repostr):
819 args = repostr.split()
820 repoobj = ks.handler.repo.parse(args[1:])
821 if repoobj and repoobj not in ks.handler.repo.repoList:
822 ks.handler.repo.repoList.append(repoobj)
823
824def remove_all_repos(ks):
825 while len(ks.handler.repo.repoList) != 0:
826 del ks.handler.repo.repoList[0]
827
828def remove_duplicate_repos(ks):
829 i = 0
830 j = i + 1
831 while True:
832 if len(ks.handler.repo.repoList) < 2:
833 break
834 if i >= len(ks.handler.repo.repoList) - 1:
835 break
836 name = ks.handler.repo.repoList[i].name
837 baseurl = ks.handler.repo.repoList[i].baseurl
838 if j < len(ks.handler.repo.repoList):
839 if (ks.handler.repo.repoList[j].name == name or \
840 ks.handler.repo.repoList[j].baseurl == baseurl):
841 del ks.handler.repo.repoList[j]
842 else:
843 j += 1
844 if j >= len(ks.handler.repo.repoList):
845 i += 1
846 j = i + 1
847 else:
848 i += 1
849 j = i + 1
850
851def resolve_groups(creatoropts, repometadata):
852 iszypp = False
853 if 'zypp' == creatoropts['pkgmgr']:
854 iszypp = True
855 ks = creatoropts['ks']
856
857 for repo in repometadata:
858 """ Mustn't replace group with package list if repo is ready for the
859 corresponding package manager.
860 """
861
862 if iszypp and repo["patterns"]:
863 continue
864 if not iszypp and repo["comps"]:
865 continue
866
867 # But we also must handle such cases, use zypp but repo only has comps,
868 # use yum but repo only has patterns, use zypp but use_comps is true,
869 # use yum but use_comps is false.
870 groupfile = None
871 if iszypp and repo["comps"]:
872 groupfile = repo["comps"]
873 get_pkglist_handler = misc.get_pkglist_in_comps
874 if not iszypp and repo["patterns"]:
875 groupfile = repo["patterns"]
876 get_pkglist_handler = misc.get_pkglist_in_patterns
877
878 if groupfile:
879 i = 0
880 while True:
881 if i >= len(ks.handler.packages.groupList):
882 break
883 pkglist = get_pkglist_handler(
884 ks.handler.packages.groupList[i].name,
885 groupfile)
886 if pkglist:
887 del ks.handler.packages.groupList[i]
888 for pkg in pkglist:
889 if pkg not in ks.handler.packages.packageList:
890 ks.handler.packages.packageList.append(pkg)
891 else:
892 i = i + 1
diff --git a/scripts/lib/mic/kickstart/custom_commands/__init__.py b/scripts/lib/mic/kickstart/custom_commands/__init__.py
new file mode 100644
index 0000000000..5f4c440369
--- /dev/null
+++ b/scripts/lib/mic/kickstart/custom_commands/__init__.py
@@ -0,0 +1,12 @@
1from desktop import Mic_Desktop
2from micrepo import Mic_Repo, Mic_RepoData
3from partition import Mic_Partition
4from installerfw import Mic_installerfw
5
6__all__ = (
7 "Mic_Desktop",
8 "Mic_Repo",
9 "Mic_RepoData",
10 "Mic_Partition",
11 "Mic_installerfw",
12)
diff --git a/scripts/lib/mic/kickstart/custom_commands/desktop.py b/scripts/lib/mic/kickstart/custom_commands/desktop.py
new file mode 100644
index 0000000000..c8bd647ae3
--- /dev/null
+++ b/scripts/lib/mic/kickstart/custom_commands/desktop.py
@@ -0,0 +1,95 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2008, 2009, 2010 Intel, Inc.
4#
5# Yi Yang <yi.y.yang@intel.com>
6#
7# This program is free software; you can redistribute it and/or modify it
8# under the terms of the GNU General Public License as published by the Free
9# Software Foundation; version 2 of the License
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14# for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc., 59
18# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23
24class Mic_Desktop(KickstartCommand):
25 def __init__(self, writePriority=0,
26 defaultdesktop=None,
27 defaultdm=None,
28 autologinuser=None,
29 session=None):
30
31 KickstartCommand.__init__(self, writePriority)
32
33 self.__new_version = False
34 self.op = self._getParser()
35
36 self.defaultdesktop = defaultdesktop
37 self.autologinuser = autologinuser
38 self.defaultdm = defaultdm
39 self.session = session
40
41 def __str__(self):
42 retval = ""
43
44 if self.defaultdesktop != None:
45 retval += " --defaultdesktop=%s" % self.defaultdesktop
46 if self.session != None:
47 retval += " --session=\"%s\"" % self.session
48 if self.autologinuser != None:
49 retval += " --autologinuser=%s" % self.autologinuser
50 if self.defaultdm != None:
51 retval += " --defaultdm=%s" % self.defaultdm
52
53 if retval != "":
54 retval = "# Default Desktop Settings\ndesktop %s\n" % retval
55
56 return retval
57
58 def _getParser(self):
59 try:
60 op = KSOptionParser(lineno=self.lineno)
61 except TypeError:
62 # the latest version has not lineno argument
63 op = KSOptionParser()
64 self.__new_version = True
65
66 op.add_option("--defaultdesktop", dest="defaultdesktop",
67 action="store",
68 type="string",
69 nargs=1)
70 op.add_option("--autologinuser", dest="autologinuser",
71 action="store",
72 type="string",
73 nargs=1)
74 op.add_option("--defaultdm", dest="defaultdm",
75 action="store",
76 type="string",
77 nargs=1)
78 op.add_option("--session", dest="session",
79 action="store",
80 type="string",
81 nargs=1)
82 return op
83
84 def parse(self, args):
85 if self.__new_version:
86 (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
87 else:
88 (opts, extra) = self.op.parse_args(args=args)
89
90 if extra:
91 m = _("Unexpected arguments to %(command)s command: %(options)s") \
92 % {"command": "desktop", "options": extra}
93 raise KickstartValueError, formatErrorMsg(self.lineno, msg=m)
94
95 self._setToSelf(self.op, opts)
diff --git a/scripts/lib/mic/kickstart/custom_commands/installerfw.py b/scripts/lib/mic/kickstart/custom_commands/installerfw.py
new file mode 100644
index 0000000000..2466f1dc07
--- /dev/null
+++ b/scripts/lib/mic/kickstart/custom_commands/installerfw.py
@@ -0,0 +1,63 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2013 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18from pykickstart.base import *
19from pykickstart.options import *
20
21class Mic_installerfw(KickstartCommand):
22 """ This class implements the "installerfw" KS option. The argument
23 of the option is a comman-separated list of MIC features which have to be
24 disabled and instead, will be done in the installer. For example,
25 "installerfw=extlinux" disables all the MIC code which installs extlinux to
26 the target images, and instead, the extlinux or whatever boot-loader will
27 be installed by the installer instead.
28
29 The installer is a tool which is external to MIC, it comes from the
30 installation repositories and can be executed by MIC in order to perform
31 various configuration actions. The main point here is to make sure MIC has
32 no hard-wired knoledge about the target OS configuration. """
33
34 removedKeywords = KickstartCommand.removedKeywords
35 removedAttrs = KickstartCommand.removedAttrs
36
37 def __init__(self, *args, **kwargs):
38 KickstartCommand.__init__(self, *args, **kwargs)
39 self.op = self._getParser()
40 self.features = kwargs.get("installerfw", None)
41
42 def __str__(self):
43 retval = KickstartCommand.__str__(self)
44
45 if self.features:
46 retval += "# Enable installer framework features\ninstallerfw\n"
47
48 return retval
49
50 def _getParser(self):
51 op = KSOptionParser()
52 return op
53
54 def parse(self, args):
55 (_, extra) = self.op.parse_args(args=args, lineno=self.lineno)
56
57 if len(extra) != 1:
58 msg = "Kickstart command \"installerfw\" requires one " \
59 "argumet - a list of legacy features to disable"
60 raise KickstartValueError, formatErrorMsg(self.lineno, msg = msg)
61
62 self.features = extra[0].split(",")
63 return self
diff --git a/scripts/lib/mic/kickstart/custom_commands/micboot.py b/scripts/lib/mic/kickstart/custom_commands/micboot.py
new file mode 100644
index 0000000000..66d1678aa7
--- /dev/null
+++ b/scripts/lib/mic/kickstart/custom_commands/micboot.py
@@ -0,0 +1,49 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2008, 2009, 2010 Intel, Inc.
4#
5# Anas Nashif
6#
7# This program is free software; you can redistribute it and/or modify it
8# under the terms of the GNU General Public License as published by the Free
9# Software Foundation; version 2 of the License
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14# for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc., 59
18# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23from pykickstart.commands.bootloader import *
24
25class Mic_Bootloader(F8_Bootloader):
26 def __init__(self, writePriority=10, appendLine="", driveorder=None,
27 forceLBA=False, location="", md5pass="", password="",
28 upgrade=False, menus=""):
29 F8_Bootloader.__init__(self, writePriority, appendLine, driveorder,
30 forceLBA, location, md5pass, password, upgrade)
31
32 self.menus = ""
33 self.ptable = "msdos"
34
35 def _getArgsAsStr(self):
36 ret = F8_Bootloader._getArgsAsStr(self)
37
38 if self.menus == "":
39 ret += " --menus=%s" %(self.menus,)
40 if self.ptable:
41 ret += " --ptable=\"%s\"" %(self.ptable,)
42 return ret
43
44 def _getParser(self):
45 op = F8_Bootloader._getParser(self)
46 op.add_option("--menus", dest="menus")
47 op.add_option("--ptable", dest="ptable", type="string")
48 return op
49
diff --git a/scripts/lib/mic/kickstart/custom_commands/micrepo.py b/scripts/lib/mic/kickstart/custom_commands/micrepo.py
new file mode 100644
index 0000000000..b31576e400
--- /dev/null
+++ b/scripts/lib/mic/kickstart/custom_commands/micrepo.py
@@ -0,0 +1,127 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2008, 2009, 2010 Intel, Inc.
4#
5# Yi Yang <yi.y.yang@intel.com>
6#
7# This program is free software; you can redistribute it and/or modify it
8# under the terms of the GNU General Public License as published by the Free
9# Software Foundation; version 2 of the License
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14# for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc., 59
18# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20from pykickstart.base import *
21from pykickstart.errors import *
22from pykickstart.options import *
23from pykickstart.commands.repo import *
24
25class Mic_RepoData(F8_RepoData):
26
27 def __init__(self, baseurl="", mirrorlist=None, name="", priority=None,
28 includepkgs=(), excludepkgs=(), save=False, proxy=None,
29 proxy_username=None, proxy_password=None, debuginfo=False,
30 source=False, gpgkey=None, disable=False, ssl_verify="yes",
31 nocache=False):
32 kw = {}
33 # F8_RepoData keywords
34 if includepkgs:
35 kw['includepkgs'] = includepkgs
36 if excludepkgs:
37 kw['excludepkgs'] = excludepkgs
38
39 #FC6_RepoData keywords
40 if baseurl:
41 kw['baseurl'] = baseurl
42 if mirrorlist:
43 kw['mirrorlist'] = mirrorlist
44 if name:
45 kw['name'] = name
46
47 F8_RepoData.__init__(self, **kw)
48 self.save = save
49 self.proxy = proxy
50 self.proxy_username = proxy_username
51 self.proxy_password = proxy_password
52 self.debuginfo = debuginfo
53 self.disable = disable
54 self.source = source
55 self.gpgkey = gpgkey
56 self.ssl_verify = ssl_verify.lower()
57 self.priority = priority
58 self.nocache = nocache
59
60 def _getArgsAsStr(self):
61 retval = F8_RepoData._getArgsAsStr(self)
62
63 if self.save:
64 retval += " --save"
65 if self.proxy:
66 retval += " --proxy=%s" % self.proxy
67 if self.proxy_username:
68 retval += " --proxyuser=%s" % self.proxy_username
69 if self.proxy_password:
70 retval += " --proxypasswd=%s" % self.proxy_password
71 if self.debuginfo:
72 retval += " --debuginfo"
73 if self.source:
74 retval += " --source"
75 if self.gpgkey:
76 retval += " --gpgkey=%s" % self.gpgkey
77 if self.disable:
78 retval += " --disable"
79 if self.ssl_verify:
80 retval += " --ssl_verify=%s" % self.ssl_verify
81 if self.priority:
82 retval += " --priority=%s" % self.priority
83 if self.nocache:
84 retval += " --nocache"
85
86 return retval
87
88class Mic_Repo(F8_Repo):
89 def __init__(self, writePriority=0, repoList=None):
90 F8_Repo.__init__(self, writePriority, repoList)
91
92 def __str__(self):
93 retval = ""
94 for repo in self.repoList:
95 retval += repo.__str__()
96
97 return retval
98
99 def _getParser(self):
100 def list_cb (option, opt_str, value, parser):
101 for d in value.split(','):
102 parser.values.ensure_value(option.dest, []).append(d)
103
104 op = F8_Repo._getParser(self)
105 op.add_option("--save", action="store_true", dest="save",
106 default=False)
107 op.add_option("--proxy", type="string", action="store", dest="proxy",
108 default=None, nargs=1)
109 op.add_option("--proxyuser", type="string", action="store",
110 dest="proxy_username", default=None, nargs=1)
111 op.add_option("--proxypasswd", type="string", action="store",
112 dest="proxy_password", default=None, nargs=1)
113 op.add_option("--debuginfo", action="store_true", dest="debuginfo",
114 default=False)
115 op.add_option("--source", action="store_true", dest="source",
116 default=False)
117 op.add_option("--disable", action="store_true", dest="disable",
118 default=False)
119 op.add_option("--gpgkey", type="string", action="store", dest="gpgkey",
120 default=None, nargs=1)
121 op.add_option("--ssl_verify", type="string", action="store",
122 dest="ssl_verify", default="yes")
123 op.add_option("--priority", type="int", action="store", dest="priority",
124 default=None)
125 op.add_option("--nocache", action="store_true", dest="nocache",
126 default=False)
127 return op
diff --git a/scripts/lib/mic/kickstart/custom_commands/partition.py b/scripts/lib/mic/kickstart/custom_commands/partition.py
new file mode 100644
index 0000000000..59a87fb486
--- /dev/null
+++ b/scripts/lib/mic/kickstart/custom_commands/partition.py
@@ -0,0 +1,57 @@
1#!/usr/bin/python -tt
2#
3# Marko Saukko <marko.saukko@cybercom.com>
4#
5# Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
6#
7# This copyrighted material is made available to anyone wishing to use, modify,
8# copy, or redistribute it subject to the terms and conditions of the GNU
9# General Public License v.2. This program is distributed in the hope that it
10# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
11# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12# See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 51
16# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18from pykickstart.commands.partition import *
19
20class Mic_PartData(FC4_PartData):
21 removedKeywords = FC4_PartData.removedKeywords
22 removedAttrs = FC4_PartData.removedAttrs
23
24 def __init__(self, *args, **kwargs):
25 FC4_PartData.__init__(self, *args, **kwargs)
26 self.deleteRemovedAttrs()
27 self.align = kwargs.get("align", None)
28 self.extopts = kwargs.get("extopts", None)
29 self.part_type = kwargs.get("part_type", None)
30
31 def _getArgsAsStr(self):
32 retval = FC4_PartData._getArgsAsStr(self)
33
34 if self.align:
35 retval += " --align"
36 if self.extopts:
37 retval += " --extoptions=%s" % self.extopts
38 if self.part_type:
39 retval += " --part-type=%s" % self.part_type
40
41 return retval
42
43class Mic_Partition(FC4_Partition):
44 removedKeywords = FC4_Partition.removedKeywords
45 removedAttrs = FC4_Partition.removedAttrs
46
47 def _getParser(self):
48 op = FC4_Partition._getParser(self)
49 # The alignment value is given in kBytes. e.g., value 8 means that
50 # the partition is aligned to start from 8096 byte boundary.
51 op.add_option("--align", type="int", action="store", dest="align",
52 default=None)
53 op.add_option("--extoptions", type="string", action="store", dest="extopts",
54 default=None)
55 op.add_option("--part-type", type="string", action="store", dest="part_type",
56 default=None)
57 return op
diff --git a/scripts/lib/mic/msger.py b/scripts/lib/mic/msger.py
new file mode 100644
index 0000000000..9afc85be93
--- /dev/null
+++ b/scripts/lib/mic/msger.py
@@ -0,0 +1,309 @@
1#!/usr/bin/python -tt
2# vim: ai ts=4 sts=4 et sw=4
3#
4# Copyright (c) 2009, 2010, 2011 Intel, Inc.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the Free
8# Software Foundation; version 2 of the License
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13# for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc., 59
17# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19import os,sys
20import re
21import time
22
23__ALL__ = ['set_mode',
24 'get_loglevel',
25 'set_loglevel',
26 'set_logfile',
27 'raw',
28 'debug',
29 'verbose',
30 'info',
31 'warning',
32 'error',
33 'ask',
34 'pause',
35 ]
36
37# COLORs in ANSI
38INFO_COLOR = 32 # green
39WARN_COLOR = 33 # yellow
40ERR_COLOR = 31 # red
41ASK_COLOR = 34 # blue
42NO_COLOR = 0
43
44PREFIX_RE = re.compile('^<(.*?)>\s*(.*)', re.S)
45
46INTERACTIVE = True
47
48LOG_LEVEL = 1
49LOG_LEVELS = {
50 'quiet': 0,
51 'normal': 1,
52 'verbose': 2,
53 'debug': 3,
54 'never': 4,
55 }
56
57LOG_FILE_FP = None
58LOG_CONTENT = ''
59CATCHERR_BUFFILE_FD = -1
60CATCHERR_BUFFILE_PATH = None
61CATCHERR_SAVED_2 = -1
62
63def _general_print(head, color, msg = None, stream = None, level = 'normal'):
64 global LOG_CONTENT
65 if not stream:
66 stream = sys.stdout
67
68 if LOG_LEVELS[level] > LOG_LEVEL:
69 # skip
70 return
71
72 # encode raw 'unicode' str to utf8 encoded str
73 if msg and isinstance(msg, unicode):
74 msg = msg.encode('utf-8', 'ignore')
75
76 errormsg = ''
77 if CATCHERR_BUFFILE_FD > 0:
78 size = os.lseek(CATCHERR_BUFFILE_FD , 0, os.SEEK_END)
79 os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_SET)
80 errormsg = os.read(CATCHERR_BUFFILE_FD, size)
81 os.ftruncate(CATCHERR_BUFFILE_FD, 0)
82
83 # append error msg to LOG
84 if errormsg:
85 LOG_CONTENT += errormsg
86
87 # append normal msg to LOG
88 save_msg = msg.strip() if msg else None
89 if save_msg:
90 timestr = time.strftime("[%m/%d %H:%M:%S %Z] ", time.localtime())
91 LOG_CONTENT += timestr + save_msg + '\n'
92
93 if errormsg:
94 _color_print('', NO_COLOR, errormsg, stream, level)
95
96 _color_print(head, color, msg, stream, level)
97
98def _color_print(head, color, msg, stream, level):
99 colored = True
100 if color == NO_COLOR or \
101 not stream.isatty() or \
102 os.getenv('ANSI_COLORS_DISABLED') is not None:
103 colored = False
104
105 if head.startswith('\r'):
106 # need not \n at last
107 newline = False
108 else:
109 newline = True
110
111 if colored:
112 head = '\033[%dm%s:\033[0m ' %(color, head)
113 if not newline:
114 # ESC cmd to clear line
115 head = '\033[2K' + head
116 else:
117 if head:
118 head += ': '
119 if head.startswith('\r'):
120 head = head.lstrip()
121 newline = True
122
123 if msg is not None:
124 if isinstance(msg, unicode):
125 msg = msg.encode('utf8', 'ignore')
126
127 stream.write('%s%s' % (head, msg))
128 if newline:
129 stream.write('\n')
130
131 stream.flush()
132
133def _color_perror(head, color, msg, level = 'normal'):
134 if CATCHERR_BUFFILE_FD > 0:
135 _general_print(head, color, msg, sys.stdout, level)
136 else:
137 _general_print(head, color, msg, sys.stderr, level)
138
139def _split_msg(head, msg):
140 if isinstance(msg, list):
141 msg = '\n'.join(map(str, msg))
142
143 if msg.startswith('\n'):
144 # means print \n at first
145 msg = msg.lstrip()
146 head = '\n' + head
147
148 elif msg.startswith('\r'):
149 # means print \r at first
150 msg = msg.lstrip()
151 head = '\r' + head
152
153 m = PREFIX_RE.match(msg)
154 if m:
155 head += ' <%s>' % m.group(1)
156 msg = m.group(2)
157
158 return head, msg
159
160def get_loglevel():
161 return (k for k,v in LOG_LEVELS.items() if v==LOG_LEVEL).next()
162
163def set_loglevel(level):
164 global LOG_LEVEL
165 if level not in LOG_LEVELS:
166 # no effect
167 return
168
169 LOG_LEVEL = LOG_LEVELS[level]
170
171def set_interactive(mode=True):
172 global INTERACTIVE
173 if mode:
174 INTERACTIVE = True
175 else:
176 INTERACTIVE = False
177
178def log(msg=''):
179 # log msg to LOG_CONTENT then save to logfile
180 global LOG_CONTENT
181 if msg:
182 LOG_CONTENT += msg
183
184def raw(msg=''):
185 _general_print('', NO_COLOR, msg)
186
187def info(msg):
188 head, msg = _split_msg('Info', msg)
189 _general_print(head, INFO_COLOR, msg)
190
191def verbose(msg):
192 head, msg = _split_msg('Verbose', msg)
193 _general_print(head, INFO_COLOR, msg, level = 'verbose')
194
195def warning(msg):
196 head, msg = _split_msg('Warning', msg)
197 _color_perror(head, WARN_COLOR, msg)
198
199def debug(msg):
200 head, msg = _split_msg('Debug', msg)
201 _color_perror(head, ERR_COLOR, msg, level = 'debug')
202
203def error(msg):
204 head, msg = _split_msg('Error', msg)
205 _color_perror(head, ERR_COLOR, msg)
206 sys.exit(1)
207
208def ask(msg, default=True):
209 _general_print('\rQ', ASK_COLOR, '')
210 try:
211 if default:
212 msg += '(Y/n) '
213 else:
214 msg += '(y/N) '
215 if INTERACTIVE:
216 while True:
217 repl = raw_input(msg)
218 if repl.lower() == 'y':
219 return True
220 elif repl.lower() == 'n':
221 return False
222 elif not repl.strip():
223 # <Enter>
224 return default
225
226 # else loop
227 else:
228 if default:
229 msg += ' Y'
230 else:
231 msg += ' N'
232 _general_print('', NO_COLOR, msg)
233
234 return default
235 except KeyboardInterrupt:
236 sys.stdout.write('\n')
237 sys.exit(2)
238
239def choice(msg, choices, default=0):
240 if default >= len(choices):
241 return None
242 _general_print('\rQ', ASK_COLOR, '')
243 try:
244 msg += " [%s] " % '/'.join(choices)
245 if INTERACTIVE:
246 while True:
247 repl = raw_input(msg)
248 if repl in choices:
249 return repl
250 elif not repl.strip():
251 return choices[default]
252 else:
253 msg += choices[default]
254 _general_print('', NO_COLOR, msg)
255
256 return choices[default]
257 except KeyboardInterrupt:
258 sys.stdout.write('\n')
259 sys.exit(2)
260
261def pause(msg=None):
262 if INTERACTIVE:
263 _general_print('\rQ', ASK_COLOR, '')
264 if msg is None:
265 msg = 'press <ENTER> to continue ...'
266 raw_input(msg)
267
268def set_logfile(fpath):
269 global LOG_FILE_FP
270
271 def _savelogf():
272 if LOG_FILE_FP:
273 fp = open(LOG_FILE_FP, 'w')
274 fp.write(LOG_CONTENT)
275 fp.close()
276
277 if LOG_FILE_FP is not None:
278 warning('duplicate log file configuration')
279
280 LOG_FILE_FP = fpath
281
282 import atexit
283 atexit.register(_savelogf)
284
285def enable_logstderr(fpath):
286 global CATCHERR_BUFFILE_FD
287 global CATCHERR_BUFFILE_PATH
288 global CATCHERR_SAVED_2
289
290 if os.path.exists(fpath):
291 os.remove(fpath)
292 CATCHERR_BUFFILE_PATH = fpath
293 CATCHERR_BUFFILE_FD = os.open(CATCHERR_BUFFILE_PATH, os.O_RDWR|os.O_CREAT)
294 CATCHERR_SAVED_2 = os.dup(2)
295 os.dup2(CATCHERR_BUFFILE_FD, 2)
296
297def disable_logstderr():
298 global CATCHERR_BUFFILE_FD
299 global CATCHERR_BUFFILE_PATH
300 global CATCHERR_SAVED_2
301
302 raw(msg = None) # flush message buffer and print it.
303 os.dup2(CATCHERR_SAVED_2, 2)
304 os.close(CATCHERR_SAVED_2)
305 os.close(CATCHERR_BUFFILE_FD)
306 os.unlink(CATCHERR_BUFFILE_PATH)
307 CATCHERR_BUFFILE_FD = -1
308 CATCHERR_BUFFILE_PATH = None
309 CATCHERR_SAVED_2 = -1
diff --git a/scripts/lib/mic/plugin.py b/scripts/lib/mic/plugin.py
new file mode 100644
index 0000000000..18c93ad259
--- /dev/null
+++ b/scripts/lib/mic/plugin.py
@@ -0,0 +1,98 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os, sys
19
20from mic import msger
21from mic import pluginbase
22from mic.conf import configmgr
23from mic.utils import errors
24
25
26__ALL__ = ['PluginMgr', 'pluginmgr']
27
28PLUGIN_TYPES = ["imager", "backend"] # TODO "hook"
29
30
31class PluginMgr(object):
32 plugin_dirs = {}
33
34 # make the manager class as singleton
35 _instance = None
36 def __new__(cls, *args, **kwargs):
37 if not cls._instance:
38 cls._instance = super(PluginMgr, cls).__new__(cls, *args, **kwargs)
39
40 return cls._instance
41
42 def __init__(self):
43 self.plugin_dir = configmgr.common['plugin_dir']
44
45 def append_dirs(self, dirs):
46 for path in dirs:
47 self._add_plugindir(path)
48
49 # load all the plugins AGAIN
50 self._load_all()
51
52 def _add_plugindir(self, path):
53 path = os.path.abspath(os.path.expanduser(path))
54
55 if not os.path.isdir(path):
56 msger.warning("Plugin dir is not a directory or does not exist: %s"\
57 % path)
58 return
59
60 if path not in self.plugin_dirs:
61 self.plugin_dirs[path] = False
62 # the value True/False means "loaded"
63
64 def _load_all(self):
65 for (pdir, loaded) in self.plugin_dirs.iteritems():
66 if loaded: continue
67
68 sys.path.insert(0, pdir)
69 for mod in [x[:-3] for x in os.listdir(pdir) if x.endswith(".py")]:
70 if mod and mod != '__init__':
71 if mod in sys.modules:
72 #self.plugin_dirs[pdir] = True
73 msger.warning("Module %s already exists, skip" % mod)
74 else:
75 try:
76 pymod = __import__(mod)
77 self.plugin_dirs[pdir] = True
78 msger.debug("Plugin module %s:%s imported"\
79 % (mod, pymod.__file__))
80 except ImportError, err:
81 msg = 'Failed to load plugin %s/%s: %s' \
82 % (os.path.basename(pdir), mod, err)
83 msger.warning(msg)
84
85 del(sys.path[0])
86
87 def get_plugins(self, ptype):
88 """ the return value is dict of name:class pairs """
89
90 if ptype not in PLUGIN_TYPES:
91 raise errors.CreatorError('%s is not valid plugin type' % ptype)
92
93 self._add_plugindir(os.path.join(self.plugin_dir, ptype))
94 self._load_all()
95
96 return pluginbase.get_plugins(ptype)
97
98pluginmgr = PluginMgr()
diff --git a/scripts/lib/mic/pluginbase.py b/scripts/lib/mic/pluginbase.py
new file mode 100644
index 0000000000..6ac195b42d
--- /dev/null
+++ b/scripts/lib/mic/pluginbase.py
@@ -0,0 +1,101 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import shutil
20from mic import msger
21from mic.utils import errors
22
23class _Plugin(object):
24 class __metaclass__(type):
25 def __init__(cls, name, bases, attrs):
26 if not hasattr(cls, 'plugins'):
27 cls.plugins = {}
28
29 elif 'mic_plugin_type' in attrs:
30 if attrs['mic_plugin_type'] not in cls.plugins:
31 cls.plugins[attrs['mic_plugin_type']] = {}
32
33 elif hasattr(cls, 'mic_plugin_type') and 'name' in attrs:
34 cls.plugins[cls.mic_plugin_type][attrs['name']] = cls
35
36 def show_plugins(cls):
37 for cls in cls.plugins[cls.mic_plugin_type]:
38 print cls
39
40 def get_plugins(cls):
41 return cls.plugins
42
43class ImagerPlugin(_Plugin):
44 mic_plugin_type = "imager"
45
46 @classmethod
47 def check_image_exists(self, destdir, apacking=None,
48 images=(),
49 release=None):
50
51 # if it's a packing file, reset images
52 if apacking:
53 images = [apacking]
54
55 # release option will override images
56 if release is not None:
57 images = [os.path.basename(destdir.rstrip('/'))]
58 destdir = os.path.dirname(destdir.rstrip('/'))
59
60 for name in images:
61 if not name:
62 continue
63
64 image = os.path.join(destdir, name)
65 if not os.path.exists(image):
66 continue
67
68 if msger.ask("Target image/dir: %s already exists, "
69 "clean up and continue?" % image):
70 if os.path.isdir(image):
71 shutil.rmtree(image)
72 else:
73 os.unlink(image)
74 else:
75 raise errors.Abort("Cancled")
76
77 def do_create(self):
78 pass
79
80 def do_chroot(self):
81 pass
82
83class BackendPlugin(_Plugin):
84 mic_plugin_type="backend"
85
86 # suppress the verbose rpm warnings
87 if msger.get_loglevel() != 'debug':
88 import rpm
89 rpm.setVerbosity(rpm.RPMLOG_ERR)
90
91 def addRepository(self):
92 pass
93
94def get_plugins(typen):
95 ps = ImagerPlugin.get_plugins()
96 if typen in ps:
97 return ps[typen]
98 else:
99 return None
100
101__all__ = ['ImagerPlugin', 'BackendPlugin', 'get_plugins']
diff --git a/scripts/lib/mic/plugins/backend/yumpkgmgr.py b/scripts/lib/mic/plugins/backend/yumpkgmgr.py
new file mode 100644
index 0000000000..955f813109
--- /dev/null
+++ b/scripts/lib/mic/plugins/backend/yumpkgmgr.py
@@ -0,0 +1,490 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2007 Red Hat Inc.
4# Copyright (c) 2010, 2011 Intel, Inc.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the Free
8# Software Foundation; version 2 of the License
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13# for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc., 59
17# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19import os, sys
20import re
21import tempfile
22import glob
23from string import Template
24
25import rpmUtils
26import yum
27
28from mic import msger
29from mic.kickstart import ksparser
30from mic.utils import misc, rpmmisc
31from mic.utils.grabber import TextProgress
32from mic.utils.proxy import get_proxy_for
33from mic.utils.errors import CreatorError
34from mic.imager.baseimager import BaseImageCreator
35
36YUMCONF_TEMP = """[main]
37installroot=$installroot
38cachedir=/var/cache/yum
39persistdir=/var/lib/yum
40plugins=0
41reposdir=
42failovermethod=priority
43http_caching=packages
44sslverify=1
45"""
46
47class MyYumRepository(yum.yumRepo.YumRepository):
48 def __del__(self):
49 pass
50
51 def dirSetup(self):
52 super(MyYumRepository, self).dirSetup()
53 # relocate package dir
54 pkgdir = os.path.join(self.basecachedir, 'packages', self.id)
55 self.setAttribute('_dir_setup_pkgdir', pkgdir)
56 self._dirSetupMkdir_p(self.pkgdir)
57
58 def _getFile(self, url=None,
59 relative=None,
60 local=None,
61 start=None,
62 end=None,
63 copy_local=None,
64 checkfunc=None,
65 text=None,
66 reget='simple',
67 cache=True,
68 size=None):
69
70 m2c_connection = None
71 if not self.sslverify:
72 try:
73 import M2Crypto
74 m2c_connection = M2Crypto.SSL.Connection.clientPostConnectionCheck
75 M2Crypto.SSL.Connection.clientPostConnectionCheck = None
76 except ImportError, err:
77 raise CreatorError("%s, please try to install python-m2crypto" % str(err))
78
79 proxy = None
80 if url:
81 proxy = get_proxy_for(url)
82 else:
83 proxy = get_proxy_for(self.urls[0])
84
85 if proxy:
86 self.proxy = str(proxy)
87
88 size = int(size) if size else None
89 rvalue = super(MyYumRepository, self)._getFile(url,
90 relative,
91 local,
92 start,
93 end,
94 copy_local,
95 checkfunc,
96 text,
97 reget,
98 cache,
99 size)
100
101 if m2c_connection and \
102 not M2Crypto.SSL.Connection.clientPostConnectionCheck:
103 M2Crypto.SSL.Connection.clientPostConnectionCheck = m2c_connection
104
105 return rvalue
106
107from mic.pluginbase import BackendPlugin
108class Yum(BackendPlugin, yum.YumBase):
109 name = 'yum'
110
111 def __init__(self, target_arch, instroot, cachedir):
112 yum.YumBase.__init__(self)
113
114 self.cachedir = cachedir
115 self.instroot = instroot
116 self.target_arch = target_arch
117
118 if self.target_arch:
119 if not rpmUtils.arch.arches.has_key(self.target_arch):
120 rpmUtils.arch.arches["armv7hl"] = "noarch"
121 rpmUtils.arch.arches["armv7tnhl"] = "armv7nhl"
122 rpmUtils.arch.arches["armv7tnhl"] = "armv7thl"
123 rpmUtils.arch.arches["armv7thl"] = "armv7hl"
124 rpmUtils.arch.arches["armv7nhl"] = "armv7hl"
125 self.arch.setup_arch(self.target_arch)
126
127 self.__pkgs_license = {}
128 self.__pkgs_content = {}
129 self.__pkgs_vcsinfo = {}
130
131 self.install_debuginfo = False
132
133 def doFileLogSetup(self, uid, logfile):
134 # don't do the file log for the livecd as it can lead to open fds
135 # being left and an inability to clean up after ourself
136 pass
137
138 def close(self):
139 try:
140 os.unlink(self.confpath)
141 os.unlink(self.conf.installroot + "/yum.conf")
142 except:
143 pass
144
145 if self.ts:
146 self.ts.close()
147 self._delRepos()
148 self._delSacks()
149 yum.YumBase.close(self)
150 self.closeRpmDB()
151
152 if not os.path.exists("/etc/fedora-release") and \
153 not os.path.exists("/etc/meego-release"):
154 for i in range(3, os.sysconf("SC_OPEN_MAX")):
155 try:
156 os.close(i)
157 except:
158 pass
159
160 def __del__(self):
161 pass
162
163 def _writeConf(self, confpath, installroot):
164 conf = Template(YUMCONF_TEMP).safe_substitute(installroot=installroot)
165
166 f = file(confpath, "w+")
167 f.write(conf)
168 f.close()
169
170 os.chmod(confpath, 0644)
171
172 def _cleanupRpmdbLocks(self, installroot):
173 # cleans up temporary files left by bdb so that differing
174 # versions of rpm don't cause problems
175 for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
176 os.unlink(f)
177
178 def setup(self):
179 # create yum.conf
180 (fn, self.confpath) = tempfile.mkstemp(dir=self.cachedir,
181 prefix='yum.conf-')
182 os.close(fn)
183 self._writeConf(self.confpath, self.instroot)
184 self._cleanupRpmdbLocks(self.instroot)
185 # do setup
186 self.doConfigSetup(fn = self.confpath, root = self.instroot)
187 self.conf.cache = 0
188 self.doTsSetup()
189 self.doRpmDBSetup()
190 self.doRepoSetup()
191 self.doSackSetup()
192
193 def preInstall(self, pkg):
194 # FIXME: handle pre-install package
195 return None
196
197 def selectPackage(self, pkg):
198 """Select a given package.
199 Can be specified with name.arch or name*
200 """
201
202 try:
203 self.install(pattern = pkg)
204 return None
205 except yum.Errors.InstallError:
206 return "No package(s) available to install"
207 except yum.Errors.RepoError, e:
208 raise CreatorError("Unable to download from repo : %s" % (e,))
209 except yum.Errors.YumBaseError, e:
210 raise CreatorError("Unable to install: %s" % (e,))
211
212 def deselectPackage(self, pkg):
213 """Deselect package. Can be specified as name.arch or name*
214 """
215
216 sp = pkg.rsplit(".", 2)
217 txmbrs = []
218 if len(sp) == 2:
219 txmbrs = self.tsInfo.matchNaevr(name=sp[0], arch=sp[1])
220
221 if len(txmbrs) == 0:
222 exact, match, unmatch = yum.packages.parsePackages(
223 self.pkgSack.returnPackages(),
224 [pkg],
225 casematch=1)
226 for p in exact + match:
227 txmbrs.append(p)
228
229 if len(txmbrs) > 0:
230 for x in txmbrs:
231 self.tsInfo.remove(x.pkgtup)
232 # we also need to remove from the conditionals
233 # dict so that things don't get pulled back in as a result
234 # of them. yes, this is ugly. conditionals should die.
235 for req, pkgs in self.tsInfo.conditionals.iteritems():
236 if x in pkgs:
237 pkgs.remove(x)
238 self.tsInfo.conditionals[req] = pkgs
239 else:
240 msger.warning("No such package %s to remove" %(pkg,))
241
242 def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
243 try:
244 yum.YumBase.selectGroup(self, grp)
245 if include == ksparser.GROUP_REQUIRED:
246 for p in grp.default_packages.keys():
247 self.deselectPackage(p)
248
249 elif include == ksparser.GROUP_ALL:
250 for p in grp.optional_packages.keys():
251 self.selectPackage(p)
252
253 return None
254 except (yum.Errors.InstallError, yum.Errors.GroupsError), e:
255 return e
256 except yum.Errors.RepoError, e:
257 raise CreatorError("Unable to download from repo : %s" % (e,))
258 except yum.Errors.YumBaseError, e:
259 raise CreatorError("Unable to install: %s" % (e,))
260
261 def addRepository(self, name, url = None, mirrorlist = None, proxy = None,
262 proxy_username = None, proxy_password = None,
263 inc = None, exc = None, ssl_verify=True, nocache=False,
264 cost = None, priority=None):
265 # TODO: Handle priority attribute for repos
266 def _varSubstitute(option):
267 # takes a variable and substitutes like yum configs do
268 option = option.replace("$basearch", rpmUtils.arch.getBaseArch())
269 option = option.replace("$arch", rpmUtils.arch.getCanonArch())
270 return option
271
272 repo = MyYumRepository(name)
273
274 # Set proxy
275 repo.proxy = proxy
276 repo.proxy_username = proxy_username
277 repo.proxy_password = proxy_password
278
279 if url:
280 repo.baseurl.append(_varSubstitute(url))
281
282 # check LICENSE files
283 if not rpmmisc.checkRepositoryEULA(name, repo):
284 msger.warning('skip repo:%s for failed EULA confirmation' % name)
285 return None
286
287 if mirrorlist:
288 repo.mirrorlist = _varSubstitute(mirrorlist)
289
290 conf = yum.config.RepoConf()
291 for k, v in conf.iteritems():
292 if v or not hasattr(repo, k):
293 repo.setAttribute(k, v)
294
295 repo.sslverify = ssl_verify
296 repo.cache = not nocache
297
298 repo.basecachedir = self.cachedir
299 repo.base_persistdir = self.conf.persistdir
300 repo.failovermethod = "priority"
301 repo.metadata_expire = 0
302 # Enable gpg check for verifying corrupt packages
303 repo.gpgcheck = 1
304 repo.enable()
305 repo.setup(0)
306 self.repos.add(repo)
307 if cost:
308 repo.cost = cost
309
310 msger.verbose('repo: %s was added' % name)
311 return repo
312
313 def installLocal(self, pkg, po=None, updateonly=False):
314 ts = rpmUtils.transaction.initReadOnlyTransaction()
315 try:
316 hdr = rpmUtils.miscutils.hdrFromPackage(ts, pkg)
317 except rpmUtils.RpmUtilsError, e:
318 raise yum.Errors.MiscError, \
319 'Could not open local rpm file: %s: %s' % (pkg, e)
320
321 self.deselectPackage(hdr['name'])
322 yum.YumBase.installLocal(self, pkg, po, updateonly)
323
324 def installHasFile(self, file):
325 provides_pkg = self.whatProvides(file, None, None)
326 dlpkgs = map(
327 lambda x: x.po,
328 filter(
329 lambda txmbr: txmbr.ts_state in ("i", "u"),
330 self.tsInfo.getMembers()))
331
332 for p in dlpkgs:
333 for q in provides_pkg:
334 if (p == q):
335 return True
336
337 return False
338
339 def runInstall(self, checksize = 0):
340 os.environ["HOME"] = "/"
341 os.environ["LD_PRELOAD"] = ""
342 try:
343 (res, resmsg) = self.buildTransaction()
344 except yum.Errors.RepoError, e:
345 raise CreatorError("Unable to download from repo : %s" %(e,))
346
347 if res != 2:
348 raise CreatorError("Failed to build transaction : %s" \
349 % str.join("\n", resmsg))
350
351 dlpkgs = map(
352 lambda x: x.po,
353 filter(
354 lambda txmbr: txmbr.ts_state in ("i", "u"),
355 self.tsInfo.getMembers()))
356
357 # record all pkg and the content
358 for pkg in dlpkgs:
359 pkg_long_name = misc.RPM_FMT % {
360 'name': pkg.name,
361 'arch': pkg.arch,
362 'version': pkg.version,
363 'release': pkg.release
364 }
365 self.__pkgs_content[pkg_long_name] = pkg.files
366 license = pkg.license
367 if license in self.__pkgs_license.keys():
368 self.__pkgs_license[license].append(pkg_long_name)
369 else:
370 self.__pkgs_license[license] = [pkg_long_name]
371
372 total_count = len(dlpkgs)
373 cached_count = 0
374 download_total_size = sum(map(lambda x: int(x.packagesize), dlpkgs))
375
376 msger.info("\nChecking packages cached ...")
377 for po in dlpkgs:
378 local = po.localPkg()
379 repo = filter(lambda r: r.id == po.repoid, self.repos.listEnabled())[0]
380 if not repo.cache and os.path.exists(local):
381 os.unlink(local)
382 if not os.path.exists(local):
383 continue
384 if not self.verifyPkg(local, po, False):
385 msger.warning("Package %s is damaged: %s" \
386 % (os.path.basename(local), local))
387 else:
388 download_total_size -= int(po.packagesize)
389 cached_count +=1
390
391 cache_avail_size = misc.get_filesystem_avail(self.cachedir)
392 if cache_avail_size < download_total_size:
393 raise CreatorError("No enough space used for downloading.")
394
395 # record the total size of installed pkgs
396 pkgs_total_size = 0L
397 for x in dlpkgs:
398 if hasattr(x, 'installedsize'):
399 pkgs_total_size += int(x.installedsize)
400 else:
401 pkgs_total_size += int(x.size)
402
403 # check needed size before actually download and install
404 if checksize and pkgs_total_size > checksize:
405 raise CreatorError("No enough space used for installing, "
406 "please resize partition size in ks file")
407
408 msger.info("Packages: %d Total, %d Cached, %d Missed" \
409 % (total_count, cached_count, total_count - cached_count))
410
411 try:
412 repos = self.repos.listEnabled()
413 for repo in repos:
414 repo.setCallback(TextProgress(total_count - cached_count))
415
416 self.downloadPkgs(dlpkgs)
417 # FIXME: sigcheck?
418
419 self.initActionTs()
420 self.populateTs(keepold=0)
421
422 deps = self.ts.check()
423 if len(deps) != 0:
424 # This isn't fatal, Ubuntu has this issue but it is ok.
425 msger.debug(deps)
426 msger.warning("Dependency check failed!")
427
428 rc = self.ts.order()
429 if rc != 0:
430 raise CreatorError("ordering packages for installation failed")
431
432 # FIXME: callback should be refactored a little in yum
433 cb = rpmmisc.RPMInstallCallback(self.ts)
434 cb.tsInfo = self.tsInfo
435 cb.filelog = False
436
437 msger.warning('\nCaution, do NOT interrupt the installation, '
438 'else mic cannot finish the cleanup.')
439
440 installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
441 msger.enable_logstderr(installlogfile)
442 self.runTransaction(cb)
443 self._cleanupRpmdbLocks(self.conf.installroot)
444
445 except rpmUtils.RpmUtilsError, e:
446 raise CreatorError("mic does NOT support delta rpm: %s" % e)
447 except yum.Errors.RepoError, e:
448 raise CreatorError("Unable to download from repo : %s" % e)
449 except yum.Errors.YumBaseError, e:
450 raise CreatorError("Unable to install: %s" % e)
451 finally:
452 msger.disable_logstderr()
453
454 def getVcsInfo(self):
455 return self.__pkgs_vcsinfo
456
457 def getAllContent(self):
458 return self.__pkgs_content
459
460 def getPkgsLicense(self):
461 return self.__pkgs_license
462
463 def getFilelist(self, pkgname):
464 if not pkgname:
465 return None
466
467 pkg = filter(lambda txmbr: txmbr.po.name == pkgname, self.tsInfo.getMembers())
468 if not pkg:
469 return None
470 return pkg[0].po.filelist
471
472 def package_url(self, pkgname):
473 pkgs = self.pkgSack.searchNevra(name=pkgname)
474 if pkgs:
475 proxy = None
476 proxies = None
477 url = pkgs[0].remote_url
478 repoid = pkgs[0].repoid
479 repos = filter(lambda r: r.id == repoid, self.repos.listEnabled())
480
481 if repos:
482 proxy = repos[0].proxy
483 if not proxy:
484 proxy = get_proxy_for(url)
485 if proxy:
486 proxies = {str(url.split(':')[0]): str(proxy)}
487
488 return (url, proxies)
489
490 return (None, None)
diff --git a/scripts/lib/mic/plugins/backend/zypppkgmgr.py b/scripts/lib/mic/plugins/backend/zypppkgmgr.py
new file mode 100755
index 0000000000..c760859832
--- /dev/null
+++ b/scripts/lib/mic/plugins/backend/zypppkgmgr.py
@@ -0,0 +1,973 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2010, 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import shutil
20import urlparse
21import rpm
22
23import zypp
24if not hasattr(zypp, 'PoolQuery') or \
25 not hasattr(zypp.RepoManager, 'loadSolvFile'):
26 raise ImportError("python-zypp in host system cannot support PoolQuery or "
27 "loadSolvFile interface, please update it to enhanced "
28 "version which can be found in download.tizen.org/tools")
29
30from mic import msger
31from mic.kickstart import ksparser
32from mic.utils import misc, rpmmisc, runner, fs_related
33from mic.utils.grabber import myurlgrab, TextProgress
34from mic.utils.proxy import get_proxy_for
35from mic.utils.errors import CreatorError, RepoError, RpmError
36from mic.imager.baseimager import BaseImageCreator
37
38class RepositoryStub:
39 def __init__(self):
40 self.name = None
41 self.baseurl = []
42 self.mirrorlist = None
43 self.proxy = None
44 self.proxy_username = None
45 self.proxy_password = None
46 self.nocache = False
47
48 self.enabled = True
49 self.autorefresh = True
50 self.keeppackages = True
51 self.priority = None
52
53from mic.pluginbase import BackendPlugin
54class Zypp(BackendPlugin):
55 name = 'zypp'
56
57 def __init__(self, target_arch, instroot, cachedir):
58 self.cachedir = cachedir
59 self.instroot = instroot
60 self.target_arch = target_arch
61
62 self.__pkgs_license = {}
63 self.__pkgs_content = {}
64 self.__pkgs_vcsinfo = {}
65 self.repos = []
66 self.to_deselect = []
67 self.localpkgs = {}
68 self.repo_manager = None
69 self.repo_manager_options = None
70 self.Z = None
71 self.ts = None
72 self.ts_pre = None
73 self.incpkgs = {}
74 self.excpkgs = {}
75 self.pre_pkgs = []
76 self.probFilterFlags = [ rpm.RPMPROB_FILTER_OLDPACKAGE,
77 rpm.RPMPROB_FILTER_REPLACEPKG ]
78
79 self.has_prov_query = True
80 self.install_debuginfo = False
81
82 def doFileLogSetup(self, uid, logfile):
83 # don't do the file log for the livecd as it can lead to open fds
84 # being left and an inability to clean up after ourself
85 pass
86
87 def closeRpmDB(self):
88 pass
89
90 def close(self):
91 if self.ts:
92 self.ts.closeDB()
93 self.ts = None
94
95 if self.ts_pre:
96 self.ts_pre.closeDB()
97 self.ts = None
98
99 self.closeRpmDB()
100
101 if not os.path.exists("/etc/fedora-release") and \
102 not os.path.exists("/etc/meego-release"):
103 for i in range(3, os.sysconf("SC_OPEN_MAX")):
104 try:
105 os.close(i)
106 except:
107 pass
108
109 def __del__(self):
110 self.close()
111
112 def _cleanupRpmdbLocks(self, installroot):
113 # cleans up temporary files left by bdb so that differing
114 # versions of rpm don't cause problems
115 import glob
116 for f in glob.glob(installroot + "/var/lib/rpm/__db*"):
117 os.unlink(f)
118
119 def _cleanupZyppJunk(self, installroot):
120 try:
121 shutil.rmtree(os.path.join(installroot, '.zypp'))
122 except:
123 pass
124
125 def setup(self):
126 self._cleanupRpmdbLocks(self.instroot)
127
128 def whatObsolete(self, pkg):
129 query = zypp.PoolQuery()
130 query.addKind(zypp.ResKind.package)
131 query.addAttribute(zypp.SolvAttr.obsoletes, pkg)
132 query.setMatchExact()
133 for pi in query.queryResults(self.Z.pool()):
134 return pi
135 return None
136
137 def _zyppQueryPackage(self, pkg):
138 query = zypp.PoolQuery()
139 query.addKind(zypp.ResKind.package)
140 query.addAttribute(zypp.SolvAttr.name,pkg)
141 query.setMatchExact()
142 for pi in query.queryResults(self.Z.pool()):
143 return pi
144 return None
145
146 def _splitPkgString(self, pkg):
147 sp = pkg.rsplit(".",1)
148 name = sp[0]
149 arch = None
150 if len(sp) == 2:
151 arch = sp[1]
152 sysarch = zypp.Arch(self.target_arch)
153 if not zypp.Arch(arch).compatible_with (sysarch):
154 arch = None
155 name = ".".join(sp)
156 return name, arch
157
158 def selectPackage(self, pkg):
159 """Select a given package or package pattern, can be specified
160 with name.arch or name* or *name
161 """
162
163 if not self.Z:
164 self.__initialize_zypp()
165
166 def markPoolItem(obs, pi):
167 if obs == None:
168 pi.status().setToBeInstalled (zypp.ResStatus.USER)
169 else:
170 obs.status().setToBeInstalled (zypp.ResStatus.USER)
171
172 def cmpEVR(p1, p2):
173 # compare criterion: arch compatibility first, then repo
174 # priority, and version last
175 a1 = p1.arch()
176 a2 = p2.arch()
177 if str(a1) != str(a2):
178 if a1.compatible_with(a2):
179 return -1
180 else:
181 return 1
182 # Priority of a repository is an integer value between 0 (the
183 # highest priority) and 99 (the lowest priority)
184 pr1 = int(p1.repoInfo().priority())
185 pr2 = int(p2.repoInfo().priority())
186 if pr1 > pr2:
187 return -1
188 elif pr1 < pr2:
189 return 1
190
191 ed1 = p1.edition()
192 ed2 = p2.edition()
193 (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()])
194 (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()])
195 return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
196
197 found = False
198 startx = pkg.startswith("*")
199 endx = pkg.endswith("*")
200 ispattern = startx or endx
201 name, arch = self._splitPkgString(pkg)
202
203 q = zypp.PoolQuery()
204 q.addKind(zypp.ResKind.package)
205
206 if ispattern:
207 if startx and not endx:
208 pattern = '%s$' % (pkg[1:])
209 if endx and not startx:
210 pattern = '^%s' % (pkg[0:-1])
211 if endx and startx:
212 pattern = '%s' % (pkg[1:-1])
213 q.setMatchRegex()
214 q.addAttribute(zypp.SolvAttr.name,pattern)
215
216 elif arch:
217 q.setMatchExact()
218 q.addAttribute(zypp.SolvAttr.name,name)
219
220 else:
221 q.setMatchExact()
222 q.addAttribute(zypp.SolvAttr.name,pkg)
223
224 for pitem in sorted(
225 q.queryResults(self.Z.pool()),
226 cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
227 reverse=True):
228 item = zypp.asKindPackage(pitem)
229 if item.name() in self.excpkgs.keys() and \
230 self.excpkgs[item.name()] == item.repoInfo().name():
231 continue
232 if item.name() in self.incpkgs.keys() and \
233 self.incpkgs[item.name()] != item.repoInfo().name():
234 continue
235
236 found = True
237 obspkg = self.whatObsolete(item.name())
238 if arch:
239 if arch == str(item.arch()):
240 item.status().setToBeInstalled (zypp.ResStatus.USER)
241 else:
242 markPoolItem(obspkg, pitem)
243 if not ispattern:
244 break
245
246 # Can't match using package name, then search from packge
247 # provides infomation
248 if found == False and not ispattern:
249 q.addAttribute(zypp.SolvAttr.provides, pkg)
250 q.addAttribute(zypp.SolvAttr.name,'')
251
252 for pitem in sorted(
253 q.queryResults(self.Z.pool()),
254 cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
255 reverse=True):
256 item = zypp.asKindPackage(pitem)
257 if item.name() in self.excpkgs.keys() and \
258 self.excpkgs[item.name()] == item.repoInfo().name():
259 continue
260 if item.name() in self.incpkgs.keys() and \
261 self.incpkgs[item.name()] != item.repoInfo().name():
262 continue
263
264 found = True
265 obspkg = self.whatObsolete(item.name())
266 markPoolItem(obspkg, pitem)
267 break
268
269 if found:
270 return None
271 else:
272 raise CreatorError("Unable to find package: %s" % (pkg,))
273
274 def inDeselectPackages(self, pitem):
275 """check if specified pacakges are in the list of inDeselectPackages
276 """
277 item = zypp.asKindPackage(pitem)
278 name = item.name()
279 for pkg in self.to_deselect:
280 startx = pkg.startswith("*")
281 endx = pkg.endswith("*")
282 ispattern = startx or endx
283 pkgname, pkgarch = self._splitPkgString(pkg)
284 if not ispattern:
285 if pkgarch:
286 if name == pkgname and str(item.arch()) == pkgarch:
287 return True;
288 else:
289 if name == pkgname:
290 return True;
291 else:
292 if startx and name.endswith(pkg[1:]):
293 return True;
294 if endx and name.startswith(pkg[:-1]):
295 return True;
296
297 return False;
298
299 def deselectPackage(self, pkg):
300 """collect packages should not be installed"""
301 self.to_deselect.append(pkg)
302
303 def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT):
304 if not self.Z:
305 self.__initialize_zypp()
306 found = False
307 q=zypp.PoolQuery()
308 q.addKind(zypp.ResKind.pattern)
309 for pitem in q.queryResults(self.Z.pool()):
310 item = zypp.asKindPattern(pitem)
311 summary = "%s" % item.summary()
312 name = "%s" % item.name()
313 if name == grp or summary == grp:
314 found = True
315 pitem.status().setToBeInstalled (zypp.ResStatus.USER)
316 break
317
318 if found:
319 if include == ksparser.GROUP_REQUIRED:
320 map(
321 lambda p: self.deselectPackage(p),
322 grp.default_packages.keys())
323
324 return None
325 else:
326 raise CreatorError("Unable to find pattern: %s" % (grp,))
327
328 def addRepository(self, name,
329 url = None,
330 mirrorlist = None,
331 proxy = None,
332 proxy_username = None,
333 proxy_password = None,
334 inc = None,
335 exc = None,
336 ssl_verify = True,
337 nocache = False,
338 cost=None,
339 priority=None):
340 # TODO: Handle cost attribute for repos
341
342 if not self.repo_manager:
343 self.__initialize_repo_manager()
344
345 if not proxy and url:
346 proxy = get_proxy_for(url)
347
348 repo = RepositoryStub()
349 repo.name = name
350 repo.id = name
351 repo.proxy = proxy
352 repo.proxy_username = proxy_username
353 repo.proxy_password = proxy_password
354 repo.ssl_verify = ssl_verify
355 repo.nocache = nocache
356 repo.baseurl.append(url)
357 if inc:
358 for pkg in inc:
359 self.incpkgs[pkg] = name
360 if exc:
361 for pkg in exc:
362 self.excpkgs[pkg] = name
363
364 # check LICENSE files
365 if not rpmmisc.checkRepositoryEULA(name, repo):
366 msger.warning('skip repo:%s for failed EULA confirmation' % name)
367 return None
368
369 if mirrorlist:
370 repo.mirrorlist = mirrorlist
371
372 # Enable gpg check for verifying corrupt packages
373 repo.gpgcheck = 1
374 if priority is not None:
375 # priority 0 has issue in RepoInfo.setPriority
376 repo.priority = priority + 1
377
378 try:
379 repo_info = zypp.RepoInfo()
380 repo_info.setAlias(repo.name)
381 repo_info.setName(repo.name)
382 repo_info.setEnabled(repo.enabled)
383 repo_info.setAutorefresh(repo.autorefresh)
384 repo_info.setKeepPackages(repo.keeppackages)
385 baseurl = zypp.Url(repo.baseurl[0])
386 if not ssl_verify:
387 baseurl.setQueryParam("ssl_verify", "no")
388 if proxy:
389 scheme, host, path, parm, query, frag = urlparse.urlparse(proxy)
390
391 proxyinfo = host.split(":")
392 host = proxyinfo[0]
393
394 port = "80"
395 if len(proxyinfo) > 1:
396 port = proxyinfo[1]
397
398 if proxy.startswith("socks") and len(proxy.rsplit(':', 1)) == 2:
399 host = proxy.rsplit(':', 1)[0]
400 port = proxy.rsplit(':', 1)[1]
401
402 baseurl.setQueryParam ("proxy", host)
403 baseurl.setQueryParam ("proxyport", port)
404
405 repo.baseurl[0] = baseurl.asCompleteString()
406 self.repos.append(repo)
407
408 repo_info.addBaseUrl(baseurl)
409
410 if repo.priority is not None:
411 repo_info.setPriority(repo.priority)
412
413 # this hack is used to change zypp credential file location
414 # the default one is $HOME/.zypp, which cause conflicts when
415 # installing some basic packages, and the location doesn't
416 # have any interface actually, so use a tricky way anyway
417 homedir = None
418 if 'HOME' in os.environ:
419 homedir = os.environ['HOME']
420 os.environ['HOME'] = '/'
421 else:
422 os.environ['HOME'] = '/'
423
424 self.repo_manager.addRepository(repo_info)
425
426 # save back the $HOME env
427 if homedir:
428 os.environ['HOME'] = homedir
429 else:
430 del os.environ['HOME']
431
432 self.__build_repo_cache(name)
433
434 except RuntimeError, e:
435 raise CreatorError(str(e))
436
437 msger.verbose('repo: %s was added' % name)
438 return repo
439
440 def installHasFile(self, file):
441 return False
442
443 def preInstall(self, pkg):
444 self.pre_pkgs.append(pkg)
445
446 def runInstall(self, checksize = 0):
447 os.environ["HOME"] = "/"
448 os.environ["LD_PRELOAD"] = ""
449 self.buildTransaction()
450
451 todo = zypp.GetResolvablesToInsDel(self.Z.pool())
452 installed_pkgs = todo._toInstall
453 dlpkgs = []
454 for pitem in installed_pkgs:
455 if not zypp.isKindPattern(pitem) and \
456 not self.inDeselectPackages(pitem):
457 item = zypp.asKindPackage(pitem)
458 dlpkgs.append(item)
459
460 if not self.install_debuginfo or str(item.arch()) == "noarch":
461 continue
462
463 dipkg = self._zyppQueryPackage("%s-debuginfo" % item.name())
464 if dipkg:
465 ditem = zypp.asKindPackage(dipkg)
466 dlpkgs.append(ditem)
467 else:
468 msger.warning("No debuginfo rpm found for: %s" \
469 % item.name())
470
471 # record all pkg and the content
472 localpkgs = self.localpkgs.keys()
473 for pkg in dlpkgs:
474 license = ''
475 if pkg.name() in localpkgs:
476 hdr = rpmmisc.readRpmHeader(self.ts, self.localpkgs[pkg.name()])
477 pkg_long_name = misc.RPM_FMT % {
478 'name': hdr['name'],
479 'arch': hdr['arch'],
480 'version': hdr['version'],
481 'release': hdr['release']
482 }
483 license = hdr['license']
484
485 else:
486 pkg_long_name = misc.RPM_FMT % {
487 'name': pkg.name(),
488 'arch': pkg.arch(),
489 'version': pkg.edition().version(),
490 'release': pkg.edition().release()
491 }
492
493 license = pkg.license()
494
495 if license in self.__pkgs_license.keys():
496 self.__pkgs_license[license].append(pkg_long_name)
497 else:
498 self.__pkgs_license[license] = [pkg_long_name]
499
500 total_count = len(dlpkgs)
501 cached_count = 0
502 download_total_size = sum(map(lambda x: int(x.downloadSize()), dlpkgs))
503 localpkgs = self.localpkgs.keys()
504
505 msger.info("Checking packages cached ...")
506 for po in dlpkgs:
507 # Check if it is cached locally
508 if po.name() in localpkgs:
509 cached_count += 1
510 else:
511 local = self.getLocalPkgPath(po)
512 name = str(po.repoInfo().name())
513 try:
514 repo = filter(lambda r: r.name == name, self.repos)[0]
515 except IndexError:
516 repo = None
517 nocache = repo.nocache if repo else False
518
519 if os.path.exists(local):
520 if nocache or self.checkPkg(local) !=0:
521 os.unlink(local)
522 else:
523 download_total_size -= int(po.downloadSize())
524 cached_count += 1
525 cache_avail_size = misc.get_filesystem_avail(self.cachedir)
526 if cache_avail_size < download_total_size:
527 raise CreatorError("No enough space used for downloading.")
528
529 # record the total size of installed pkgs
530 install_total_size = sum(map(lambda x: int(x.installSize()), dlpkgs))
531 # check needed size before actually download and install
532
533 # FIXME: for multiple partitions for loop type, check fails
534 # skip the check temporarily
535 #if checksize and install_total_size > checksize:
536 # raise CreatorError("No enough space used for installing, "
537 # "please resize partition size in ks file")
538
539 download_count = total_count - cached_count
540 msger.info("Packages: %d Total, %d Cached, %d Missed" \
541 % (total_count, cached_count, download_count))
542
543 try:
544 if download_count > 0:
545 msger.info("Downloading packages ...")
546 self.downloadPkgs(dlpkgs, download_count)
547
548 self.installPkgs(dlpkgs)
549
550 except (RepoError, RpmError):
551 raise
552 except Exception, e:
553 raise CreatorError("Package installation failed: %s" % (e,))
554
555 def getVcsInfo(self):
556 if self.__pkgs_vcsinfo:
557 return
558
559 if not self.ts:
560 self.__initialize_transaction()
561
562 mi = self.ts.dbMatch()
563 for hdr in mi:
564 lname = misc.RPM_FMT % {
565 'name': hdr['name'],
566 'arch': hdr['arch'],
567 'version': hdr['version'],
568 'release': hdr['release']
569 }
570 self.__pkgs_vcsinfo[lname] = hdr['VCS']
571
572 return self.__pkgs_vcsinfo
573
574 def getAllContent(self):
575 if self.__pkgs_content:
576 return self.__pkgs_content
577
578 if not self.ts:
579 self.__initialize_transaction()
580
581 mi = self.ts.dbMatch()
582 for hdr in mi:
583 lname = misc.RPM_FMT % {
584 'name': hdr['name'],
585 'arch': hdr['arch'],
586 'version': hdr['version'],
587 'release': hdr['release']
588 }
589 self.__pkgs_content[lname] = hdr['FILENAMES']
590
591 return self.__pkgs_content
592
593 def getPkgsLicense(self):
594 return self.__pkgs_license
595
596 def getFilelist(self, pkgname):
597 if not pkgname:
598 return None
599
600 if not self.ts:
601 self.__initialize_transaction()
602
603 mi = self.ts.dbMatch('name', pkgname)
604 for header in mi:
605 return header['FILENAMES']
606
607 def __initialize_repo_manager(self):
608 if self.repo_manager:
609 return
610
611 # Clean up repo metadata
612 shutil.rmtree(self.cachedir + "/etc", ignore_errors = True)
613 shutil.rmtree(self.cachedir + "/solv", ignore_errors = True)
614 shutil.rmtree(self.cachedir + "/raw", ignore_errors = True)
615
616 zypp.KeyRing.setDefaultAccept( zypp.KeyRing.ACCEPT_UNSIGNED_FILE
617 | zypp.KeyRing.ACCEPT_VERIFICATION_FAILED
618 | zypp.KeyRing.ACCEPT_UNKNOWNKEY
619 | zypp.KeyRing.TRUST_KEY_TEMPORARILY
620 )
621
622 self.repo_manager_options = \
623 zypp.RepoManagerOptions(zypp.Pathname(self.instroot))
624
625 self.repo_manager_options.knownReposPath = \
626 zypp.Pathname(self.cachedir + "/etc/zypp/repos.d")
627
628 self.repo_manager_options.repoCachePath = \
629 zypp.Pathname(self.cachedir)
630
631 self.repo_manager_options.repoRawCachePath = \
632 zypp.Pathname(self.cachedir + "/raw")
633
634 self.repo_manager_options.repoSolvCachePath = \
635 zypp.Pathname(self.cachedir + "/solv")
636
637 self.repo_manager_options.repoPackagesCachePath = \
638 zypp.Pathname(self.cachedir + "/packages")
639
640 self.repo_manager = zypp.RepoManager(self.repo_manager_options)
641
642 def __build_repo_cache(self, name):
643 repo = self.repo_manager.getRepositoryInfo(name)
644 if self.repo_manager.isCached(repo) or not repo.enabled():
645 return
646
647 msger.info('Refreshing repository: %s ...' % name)
648 self.repo_manager.buildCache(repo, zypp.RepoManager.BuildIfNeeded)
649
650 def __initialize_zypp(self):
651 if self.Z:
652 return
653
654 zconfig = zypp.ZConfig_instance()
655
656 # Set system architecture
657 if self.target_arch:
658 zconfig.setSystemArchitecture(zypp.Arch(self.target_arch))
659
660 msger.info("zypp architecture is <%s>" % zconfig.systemArchitecture())
661
662 # repoPackagesCachePath is corrected by this
663 self.repo_manager = zypp.RepoManager(self.repo_manager_options)
664 repos = self.repo_manager.knownRepositories()
665 for repo in repos:
666 if not repo.enabled():
667 continue
668 self.repo_manager.loadFromCache(repo)
669
670 self.Z = zypp.ZYppFactory_instance().getZYpp()
671 self.Z.initializeTarget(zypp.Pathname(self.instroot))
672 self.Z.target().load()
673
674 def buildTransaction(self):
675 if not self.Z.resolver().resolvePool():
676 probs = self.Z.resolver().problems()
677
678 for problem in probs:
679 msger.warning("repo problem: %s, %s" \
680 % (problem.description().decode("utf-8"),
681 problem.details().decode("utf-8")))
682
683 raise RepoError("found %d resolver problem, abort!" \
684 % len(probs))
685
686 def getLocalPkgPath(self, po):
687 repoinfo = po.repoInfo()
688 cacheroot = repoinfo.packagesPath()
689 location= po.location()
690 rpmpath = str(location.filename())
691 pkgpath = "%s/%s" % (cacheroot, os.path.basename(rpmpath))
692 return pkgpath
693
694 def installLocal(self, pkg, po=None, updateonly=False):
695 if not self.ts:
696 self.__initialize_transaction()
697
698 solvfile = "%s/.solv" % (self.cachedir)
699
700 rc, out = runner.runtool([fs_related.find_binary_path("rpms2solv"),
701 pkg])
702 if rc == 0:
703 f = open(solvfile, "w+")
704 f.write(out)
705 f.close()
706
707 warnmsg = self.repo_manager.loadSolvFile(solvfile,
708 os.path.basename(pkg))
709 if warnmsg:
710 msger.warning(warnmsg)
711
712 os.unlink(solvfile)
713 else:
714 msger.warning('Can not get %s solv data.' % pkg)
715
716 hdr = rpmmisc.readRpmHeader(self.ts, pkg)
717 arch = zypp.Arch(hdr['arch'])
718 sysarch = zypp.Arch(self.target_arch)
719
720 if arch.compatible_with (sysarch):
721 pkgname = hdr['name']
722 self.localpkgs[pkgname] = pkg
723 self.selectPackage(pkgname)
724 msger.info("Marking %s to be installed" % (pkg))
725
726 else:
727 msger.warning("Cannot add package %s to transaction. "
728 "Not a compatible architecture: %s" \
729 % (pkg, hdr['arch']))
730
731 def downloadPkgs(self, package_objects, count):
732 localpkgs = self.localpkgs.keys()
733 progress_obj = TextProgress(count)
734
735 for po in package_objects:
736 if po.name() in localpkgs:
737 continue
738
739 filename = self.getLocalPkgPath(po)
740 if os.path.exists(filename):
741 if self.checkPkg(filename) == 0:
742 continue
743
744 dirn = os.path.dirname(filename)
745 if not os.path.exists(dirn):
746 os.makedirs(dirn)
747
748 url = self.get_url(po)
749 proxies = self.get_proxies(po)
750
751 try:
752 filename = myurlgrab(url, filename, proxies, progress_obj)
753 except CreatorError:
754 self.close()
755 raise
756
757 def preinstallPkgs(self):
758 if not self.ts_pre:
759 self.__initialize_transaction()
760
761 self.ts_pre.order()
762 cb = rpmmisc.RPMInstallCallback(self.ts_pre)
763 cb.headmsg = "Preinstall"
764 installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
765
766 # start to catch stderr output from librpm
767 msger.enable_logstderr(installlogfile)
768
769 errors = self.ts_pre.run(cb.callback, '')
770 # stop catch
771 msger.disable_logstderr()
772 self.ts_pre.closeDB()
773 self.ts_pre = None
774
775 if errors is not None:
776 if len(errors) == 0:
777 msger.warning('scriptlet or other non-fatal errors occurred '
778 'during transaction.')
779
780 else:
781 for e in errors:
782 msger.warning(e[0])
783 raise RepoError('Could not run transaction.')
784
785 def installPkgs(self, package_objects):
786 if not self.ts:
787 self.__initialize_transaction()
788
789 # clean rpm lock
790 self._cleanupRpmdbLocks(self.instroot)
791 self._cleanupZyppJunk(self.instroot)
792 # Set filters
793 probfilter = 0
794 for flag in self.probFilterFlags:
795 probfilter |= flag
796 self.ts.setProbFilter(probfilter)
797 self.ts_pre.setProbFilter(probfilter)
798
799 localpkgs = self.localpkgs.keys()
800
801 for po in package_objects:
802 pkgname = po.name()
803 if pkgname in localpkgs:
804 rpmpath = self.localpkgs[pkgname]
805 else:
806 rpmpath = self.getLocalPkgPath(po)
807
808 if not os.path.exists(rpmpath):
809 # Maybe it is a local repo
810 rpmuri = self.get_url(po)
811 if rpmuri.startswith("file:/"):
812 rpmpath = rpmuri[5:]
813
814 if not os.path.exists(rpmpath):
815 raise RpmError("Error: %s doesn't exist" % rpmpath)
816
817 h = rpmmisc.readRpmHeader(self.ts, rpmpath)
818
819 if pkgname in self.pre_pkgs:
820 msger.verbose("pre-install package added: %s" % pkgname)
821 self.ts_pre.addInstall(h, rpmpath, 'u')
822
823 self.ts.addInstall(h, rpmpath, 'u')
824
825 unresolved_dependencies = self.ts.check()
826 if not unresolved_dependencies:
827 if self.pre_pkgs:
828 self.preinstallPkgs()
829
830 self.ts.order()
831 cb = rpmmisc.RPMInstallCallback(self.ts)
832 installlogfile = "%s/__catched_stderr.buf" % (self.instroot)
833
834 # start to catch stderr output from librpm
835 msger.enable_logstderr(installlogfile)
836
837 errors = self.ts.run(cb.callback, '')
838 # stop catch
839 msger.disable_logstderr()
840 self.ts.closeDB()
841 self.ts = None
842
843 if errors is not None:
844 if len(errors) == 0:
845 msger.warning('scriptlet or other non-fatal errors occurred '
846 'during transaction.')
847
848 else:
849 for e in errors:
850 msger.warning(e[0])
851 raise RepoError('Could not run transaction.')
852
853 else:
854 for pkg, need, needflags, sense, key in unresolved_dependencies:
855 package = '-'.join(pkg)
856
857 if needflags == rpm.RPMSENSE_LESS:
858 deppkg = ' < '.join(need)
859 elif needflags == rpm.RPMSENSE_EQUAL:
860 deppkg = ' = '.join(need)
861 elif needflags == rpm.RPMSENSE_GREATER:
862 deppkg = ' > '.join(need)
863 else:
864 deppkg = '-'.join(need)
865
866 if sense == rpm.RPMDEP_SENSE_REQUIRES:
867 msger.warning("[%s] Requires [%s], which is not provided" \
868 % (package, deppkg))
869
870 elif sense == rpm.RPMDEP_SENSE_CONFLICTS:
871 msger.warning("[%s] Conflicts with [%s]" %(package,deppkg))
872
873 raise RepoError("Unresolved dependencies, transaction failed.")
874
875 def __initialize_transaction(self):
876 if not self.ts:
877 self.ts = rpm.TransactionSet(self.instroot)
878 # Set to not verify DSA signatures.
879 self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
880
881 if not self.ts_pre:
882 self.ts_pre = rpm.TransactionSet(self.instroot)
883 # Just unpack the files, don't run scripts
884 self.ts_pre.setFlags(rpm.RPMTRANS_FLAG_ALLFILES | rpm.RPMTRANS_FLAG_NOSCRIPTS)
885 # Set to not verify DSA signatures.
886 self.ts_pre.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS)
887
888 def checkPkg(self, pkg):
889 ret = 1
890 if not os.path.exists(pkg):
891 return ret
892 ret = rpmmisc.checkRpmIntegrity('rpm', pkg)
893 if ret != 0:
894 msger.warning("package %s is damaged: %s" \
895 % (os.path.basename(pkg), pkg))
896
897 return ret
898
899 def _add_prob_flags(self, *flags):
900 for flag in flags:
901 if flag not in self.probFilterFlags:
902 self.probFilterFlags.append(flag)
903
904 def get_proxies(self, pobj):
905 if not pobj:
906 return None
907
908 proxy = None
909 proxies = None
910 repoinfo = pobj.repoInfo()
911 reponame = "%s" % repoinfo.name()
912 repos = filter(lambda r: r.name == reponame, self.repos)
913 repourl = str(repoinfo.baseUrls()[0])
914
915 if repos:
916 proxy = repos[0].proxy
917 if not proxy:
918 proxy = get_proxy_for(repourl)
919 if proxy:
920 proxies = {str(repourl.split(':')[0]): str(proxy)}
921
922 return proxies
923
924 def get_url(self, pobj):
925 if not pobj:
926 return None
927
928 name = str(pobj.repoInfo().name())
929 try:
930 repo = filter(lambda r: r.name == name, self.repos)[0]
931 except IndexError:
932 return None
933
934 baseurl = repo.baseurl[0]
935
936 index = baseurl.find("?")
937 if index > -1:
938 baseurl = baseurl[:index]
939
940 location = pobj.location()
941 location = str(location.filename())
942 if location.startswith("./"):
943 location = location[2:]
944
945 return os.path.join(baseurl, location)
946
947 def package_url(self, pkgname):
948
949 def cmpEVR(p1, p2):
950 ed1 = p1.edition()
951 ed2 = p2.edition()
952 (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()])
953 (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()])
954 return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
955
956 if not self.Z:
957 self.__initialize_zypp()
958
959 q = zypp.PoolQuery()
960 q.addKind(zypp.ResKind.package)
961 q.setMatchExact()
962 q.addAttribute(zypp.SolvAttr.name, pkgname)
963 items = sorted(q.queryResults(self.Z.pool()),
964 cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)),
965 reverse=True)
966
967 if items:
968 item = zypp.asKindPackage(items[0])
969 url = self.get_url(item)
970 proxies = self.get_proxies(item)
971 return (url, proxies)
972
973 return (None, None)
diff --git a/scripts/lib/mic/plugins/hook/.py b/scripts/lib/mic/plugins/hook/.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/scripts/lib/mic/plugins/hook/.py
diff --git a/scripts/lib/mic/plugins/hook/empty_hook.py b/scripts/lib/mic/plugins/hook/empty_hook.py
new file mode 100644
index 0000000000..397585d8c1
--- /dev/null
+++ b/scripts/lib/mic/plugins/hook/empty_hook.py
@@ -0,0 +1,3 @@
1#!/usr/bin/python
2
3# TODO: plugin base for hooks
diff --git a/scripts/lib/mic/plugins/imager/fs_plugin.py b/scripts/lib/mic/plugins/imager/fs_plugin.py
new file mode 100644
index 0000000000..8e758db544
--- /dev/null
+++ b/scripts/lib/mic/plugins/imager/fs_plugin.py
@@ -0,0 +1,143 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import sys
20
21from mic import chroot, msger, rt_util
22from mic.utils import cmdln, misc, errors, fs_related
23from mic.imager import fs
24from mic.conf import configmgr
25from mic.plugin import pluginmgr
26
27from mic.pluginbase import ImagerPlugin
28class FsPlugin(ImagerPlugin):
29 name = 'fs'
30
31 @classmethod
32 @cmdln.option("--include-src",
33 dest="include_src",
34 action="store_true",
35 default=False,
36 help="Generate a image with source rpms included")
37 def do_create(self, subcmd, opts, *args):
38 """${cmd_name}: create fs image
39
40 Usage:
41 ${name} ${cmd_name} <ksfile> [OPTS]
42
43 ${cmd_option_list}
44 """
45
46 if len(args) != 1:
47 raise errors.Usage("Extra arguments given")
48
49 creatoropts = configmgr.create
50 ksconf = args[0]
51
52 if creatoropts['runtime'] == 'bootstrap':
53 configmgr._ksconf = ksconf
54 rt_util.bootstrap_mic()
55
56 recording_pkgs = []
57 if len(creatoropts['record_pkgs']) > 0:
58 recording_pkgs = creatoropts['record_pkgs']
59
60 if creatoropts['release'] is not None:
61 if 'name' not in recording_pkgs:
62 recording_pkgs.append('name')
63 if 'vcs' not in recording_pkgs:
64 recording_pkgs.append('vcs')
65
66 configmgr._ksconf = ksconf
67
68 # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
69 if creatoropts['release'] is not None:
70 creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
71
72 # try to find the pkgmgr
73 pkgmgr = None
74 backends = pluginmgr.get_plugins('backend')
75 if 'auto' == creatoropts['pkgmgr']:
76 for key in configmgr.prefer_backends:
77 if key in backends:
78 pkgmgr = backends[key]
79 break
80 else:
81 for key in backends.keys():
82 if key == creatoropts['pkgmgr']:
83 pkgmgr = backends[key]
84 break
85
86 if not pkgmgr:
87 raise errors.CreatorError("Can't find backend: %s, "
88 "available choices: %s" %
89 (creatoropts['pkgmgr'],
90 ','.join(backends.keys())))
91
92 creator = fs.FsImageCreator(creatoropts, pkgmgr)
93 creator._include_src = opts.include_src
94
95 if len(recording_pkgs) > 0:
96 creator._recording_pkgs = recording_pkgs
97
98 self.check_image_exists(creator.destdir,
99 creator.pack_to,
100 [creator.name],
101 creatoropts['release'])
102
103 try:
104 creator.check_depend_tools()
105 creator.mount(None, creatoropts["cachedir"])
106 creator.install()
107 #Download the source packages ###private options
108 if opts.include_src:
109 installed_pkgs = creator.get_installed_packages()
110 msger.info('--------------------------------------------------')
111 msger.info('Generating the image with source rpms included ...')
112 if not misc.SrcpkgsDownload(installed_pkgs, creatoropts["repomd"], creator._instroot, creatoropts["cachedir"]):
113 msger.warning("Source packages can't be downloaded")
114
115 creator.configure(creatoropts["repomd"])
116 creator.copy_kernel()
117 creator.unmount()
118 creator.package(creatoropts["outdir"])
119 if creatoropts['release'] is not None:
120 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
121 creator.print_outimage_info()
122 except errors.CreatorError:
123 raise
124 finally:
125 creator.cleanup()
126
127 msger.info("Finished.")
128 return 0
129
130 @classmethod
131 def do_chroot(self, target, cmd=[]):#chroot.py parse opts&args
132 try:
133 if len(cmd) != 0:
134 cmdline = ' '.join(cmd)
135 else:
136 cmdline = "/bin/bash"
137 envcmd = fs_related.find_binary_inchroot("env", target)
138 if envcmd:
139 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
140 chroot.chroot(target, None, cmdline)
141 finally:
142 chroot.cleanup_after_chroot("dir", None, None, None)
143 return 1
diff --git a/scripts/lib/mic/plugins/imager/livecd_plugin.py b/scripts/lib/mic/plugins/imager/livecd_plugin.py
new file mode 100644
index 0000000000..d24ef59264
--- /dev/null
+++ b/scripts/lib/mic/plugins/imager/livecd_plugin.py
@@ -0,0 +1,255 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import shutil
20import tempfile
21
22from mic import chroot, msger, rt_util
23from mic.utils import misc, fs_related, errors
24from mic.conf import configmgr
25import mic.imager.livecd as livecd
26from mic.plugin import pluginmgr
27
28from mic.pluginbase import ImagerPlugin
29class LiveCDPlugin(ImagerPlugin):
30 name = 'livecd'
31
32 @classmethod
33 def do_create(self, subcmd, opts, *args):
34 """${cmd_name}: create livecd image
35
36 Usage:
37 ${name} ${cmd_name} <ksfile> [OPTS]
38
39 ${cmd_option_list}
40 """
41
42 if len(args) != 1:
43 raise errors.Usage("Extra arguments given")
44
45 creatoropts = configmgr.create
46 ksconf = args[0]
47
48 if creatoropts['runtime'] == 'bootstrap':
49 configmgr._ksconf = ksconf
50 rt_util.bootstrap_mic()
51
52 if creatoropts['arch'] and creatoropts['arch'].startswith('arm'):
53 msger.warning('livecd cannot support arm images, Quit')
54 return
55
56 recording_pkgs = []
57 if len(creatoropts['record_pkgs']) > 0:
58 recording_pkgs = creatoropts['record_pkgs']
59
60 if creatoropts['release'] is not None:
61 if 'name' not in recording_pkgs:
62 recording_pkgs.append('name')
63 if 'vcs' not in recording_pkgs:
64 recording_pkgs.append('vcs')
65
66 configmgr._ksconf = ksconf
67
68 # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
69 if creatoropts['release'] is not None:
70 creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
71
72 # try to find the pkgmgr
73 pkgmgr = None
74 backends = pluginmgr.get_plugins('backend')
75 if 'auto' == creatoropts['pkgmgr']:
76 for key in configmgr.prefer_backends:
77 if key in backends:
78 pkgmgr = backends[key]
79 break
80 else:
81 for key in backends.keys():
82 if key == creatoropts['pkgmgr']:
83 pkgmgr = backends[key]
84 break
85
86 if not pkgmgr:
87 raise errors.CreatorError("Can't find backend: %s, "
88 "available choices: %s" %
89 (creatoropts['pkgmgr'],
90 ','.join(backends.keys())))
91
92 creator = livecd.LiveCDImageCreator(creatoropts, pkgmgr)
93
94 if len(recording_pkgs) > 0:
95 creator._recording_pkgs = recording_pkgs
96
97 self.check_image_exists(creator.destdir,
98 creator.pack_to,
99 [creator.name + ".iso"],
100 creatoropts['release'])
101
102 try:
103 creator.check_depend_tools()
104 creator.mount(None, creatoropts["cachedir"])
105 creator.install()
106 creator.configure(creatoropts["repomd"])
107 creator.copy_kernel()
108 creator.unmount()
109 creator.package(creatoropts["outdir"])
110 if creatoropts['release'] is not None:
111 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
112 creator.print_outimage_info()
113
114 except errors.CreatorError:
115 raise
116 finally:
117 creator.cleanup()
118
119 msger.info("Finished.")
120 return 0
121
122 @classmethod
123 def do_chroot(cls, target, cmd=[]):
124 os_image = cls.do_unpack(target)
125 os_image_dir = os.path.dirname(os_image)
126
127 # unpack image to target dir
128 imgsize = misc.get_file_size(os_image) * 1024L * 1024L
129 imgtype = misc.get_image_type(os_image)
130 if imgtype == "btrfsimg":
131 fstype = "btrfs"
132 myDiskMount = fs_related.BtrfsDiskMount
133 elif imgtype in ("ext3fsimg", "ext4fsimg"):
134 fstype = imgtype[:4]
135 myDiskMount = fs_related.ExtDiskMount
136 else:
137 raise errors.CreatorError("Unsupported filesystem type: %s" % fstype)
138
139 extmnt = misc.mkdtemp()
140 extloop = myDiskMount(fs_related.SparseLoopbackDisk(os_image, imgsize),
141 extmnt,
142 fstype,
143 4096,
144 "%s label" % fstype)
145 try:
146 extloop.mount()
147
148 except errors.MountError:
149 extloop.cleanup()
150 shutil.rmtree(extmnt, ignore_errors = True)
151 shutil.rmtree(os_image_dir, ignore_errors = True)
152 raise
153
154 try:
155 if len(cmd) != 0:
156 cmdline = ' '.join(cmd)
157 else:
158 cmdline = "/bin/bash"
159 envcmd = fs_related.find_binary_inchroot("env", extmnt)
160 if envcmd:
161 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
162 chroot.chroot(extmnt, None, cmdline)
163 except:
164 raise errors.CreatorError("Failed to chroot to %s." %target)
165 finally:
166 chroot.cleanup_after_chroot("img", extloop, os_image_dir, extmnt)
167
168 @classmethod
169 def do_pack(cls, base_on):
170 import subprocess
171
172 def __mkinitrd(instance):
173 kernelver = instance._get_kernel_versions().values()[0][0]
174 args = [ "/usr/libexec/mkliveinitrd", "/boot/initrd-%s.img" % kernelver, "%s" % kernelver ]
175 try:
176 subprocess.call(args, preexec_fn = instance._chroot)
177 except OSError, (err, msg):
178 raise errors.CreatorError("Failed to execute /usr/libexec/mkliveinitrd: %s" % msg)
179
180 def __run_post_cleanups(instance):
181 kernelver = instance._get_kernel_versions().values()[0][0]
182 args = ["rm", "-f", "/boot/initrd-%s.img" % kernelver]
183
184 try:
185 subprocess.call(args, preexec_fn = instance._chroot)
186 except OSError, (err, msg):
187 raise errors.CreatorError("Failed to run post cleanups: %s" % msg)
188
189 convertoropts = configmgr.convert
190 convertoropts['name'] = os.path.splitext(os.path.basename(base_on))[0]
191 convertor = livecd.LiveCDImageCreator(convertoropts)
192 imgtype = misc.get_image_type(base_on)
193 if imgtype == "btrfsimg":
194 fstype = "btrfs"
195 elif imgtype in ("ext3fsimg", "ext4fsimg"):
196 fstype = imgtype[:4]
197 else:
198 raise errors.CreatorError("Unsupported filesystem type: %s" % fstype)
199 convertor._set_fstype(fstype)
200 try:
201 convertor.mount(base_on)
202 __mkinitrd(convertor)
203 convertor._create_bootconfig()
204 __run_post_cleanups(convertor)
205 convertor.launch_shell(convertoropts['shell'])
206 convertor.unmount()
207 convertor.package()
208 convertor.print_outimage_info()
209 finally:
210 shutil.rmtree(os.path.dirname(base_on), ignore_errors = True)
211
212 @classmethod
213 def do_unpack(cls, srcimg):
214 img = srcimg
215 imgmnt = misc.mkdtemp()
216 imgloop = fs_related.DiskMount(fs_related.LoopbackDisk(img, 0), imgmnt)
217 try:
218 imgloop.mount()
219 except errors.MountError:
220 imgloop.cleanup()
221 raise
222
223 # legacy LiveOS filesystem layout support, remove for F9 or F10
224 if os.path.exists(imgmnt + "/squashfs.img"):
225 squashimg = imgmnt + "/squashfs.img"
226 else:
227 squashimg = imgmnt + "/LiveOS/squashfs.img"
228
229 tmpoutdir = misc.mkdtemp()
230 # unsquashfs requires outdir mustn't exist
231 shutil.rmtree(tmpoutdir, ignore_errors = True)
232 misc.uncompress_squashfs(squashimg, tmpoutdir)
233
234 try:
235 # legacy LiveOS filesystem layout support, remove for F9 or F10
236 if os.path.exists(tmpoutdir + "/os.img"):
237 os_image = tmpoutdir + "/os.img"
238 else:
239 os_image = tmpoutdir + "/LiveOS/ext3fs.img"
240
241 if not os.path.exists(os_image):
242 raise errors.CreatorError("'%s' is not a valid live CD ISO : neither "
243 "LiveOS/ext3fs.img nor os.img exist" %img)
244
245 imgname = os.path.basename(srcimg)
246 imgname = os.path.splitext(imgname)[0] + ".img"
247 rtimage = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), imgname)
248 shutil.copyfile(os_image, rtimage)
249
250 finally:
251 imgloop.cleanup()
252 shutil.rmtree(tmpoutdir, ignore_errors = True)
253 shutil.rmtree(imgmnt, ignore_errors = True)
254
255 return rtimage
diff --git a/scripts/lib/mic/plugins/imager/liveusb_plugin.py b/scripts/lib/mic/plugins/imager/liveusb_plugin.py
new file mode 100644
index 0000000000..7aa8927df9
--- /dev/null
+++ b/scripts/lib/mic/plugins/imager/liveusb_plugin.py
@@ -0,0 +1,260 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import shutil
20import tempfile
21
22from mic import chroot, msger, rt_util
23from mic.utils import misc, fs_related, errors
24from mic.utils.partitionedfs import PartitionedMount
25from mic.conf import configmgr
26from mic.plugin import pluginmgr
27
28import mic.imager.liveusb as liveusb
29
30from mic.pluginbase import ImagerPlugin
31class LiveUSBPlugin(ImagerPlugin):
32 name = 'liveusb'
33
34 @classmethod
35 def do_create(self, subcmd, opts, *args):
36 """${cmd_name}: create liveusb image
37
38 Usage:
39 ${name} ${cmd_name} <ksfile> [OPTS]
40
41 ${cmd_option_list}
42 """
43
44 if len(args) != 1:
45 raise errors.Usage("Extra arguments given")
46
47 creatoropts = configmgr.create
48 ksconf = args[0]
49
50 if creatoropts['runtime'] == "bootstrap":
51 configmgr._ksconf = ksconf
52 rt_util.bootstrap_mic()
53
54 if creatoropts['arch'] and creatoropts['arch'].startswith('arm'):
55 msger.warning('liveusb cannot support arm images, Quit')
56 return
57
58 recording_pkgs = []
59 if len(creatoropts['record_pkgs']) > 0:
60 recording_pkgs = creatoropts['record_pkgs']
61
62 if creatoropts['release'] is not None:
63 if 'name' not in recording_pkgs:
64 recording_pkgs.append('name')
65 if 'vcs' not in recording_pkgs:
66 recording_pkgs.append('vcs')
67
68 configmgr._ksconf = ksconf
69
70 # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
71 if creatoropts['release'] is not None:
72 creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
73
74 # try to find the pkgmgr
75 pkgmgr = None
76 backends = pluginmgr.get_plugins('backend')
77 if 'auto' == creatoropts['pkgmgr']:
78 for key in configmgr.prefer_backends:
79 if key in backends:
80 pkgmgr = backends[key]
81 break
82 else:
83 for key in backends.keys():
84 if key == creatoropts['pkgmgr']:
85 pkgmgr = backends[key]
86 break
87
88 if not pkgmgr:
89 raise errors.CreatorError("Can't find backend: %s, "
90 "available choices: %s" %
91 (creatoropts['pkgmgr'],
92 ','.join(backends.keys())))
93
94 creator = liveusb.LiveUSBImageCreator(creatoropts, pkgmgr)
95
96 if len(recording_pkgs) > 0:
97 creator._recording_pkgs = recording_pkgs
98
99 self.check_image_exists(creator.destdir,
100 creator.pack_to,
101 [creator.name + ".usbimg"],
102 creatoropts['release'])
103 try:
104 creator.check_depend_tools()
105 creator.mount(None, creatoropts["cachedir"])
106 creator.install()
107 creator.configure(creatoropts["repomd"])
108 creator.copy_kernel()
109 creator.unmount()
110 creator.package(creatoropts["outdir"])
111 if creatoropts['release'] is not None:
112 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
113 creator.print_outimage_info()
114
115 except errors.CreatorError:
116 raise
117 finally:
118 creator.cleanup()
119
120 msger.info("Finished.")
121 return 0
122
123 @classmethod
124 def do_chroot(cls, target, cmd=[]):
125 os_image = cls.do_unpack(target)
126 os_image_dir = os.path.dirname(os_image)
127
128 # unpack image to target dir
129 imgsize = misc.get_file_size(os_image) * 1024L * 1024L
130 imgtype = misc.get_image_type(os_image)
131 if imgtype == "btrfsimg":
132 fstype = "btrfs"
133 myDiskMount = fs_related.BtrfsDiskMount
134 elif imgtype in ("ext3fsimg", "ext4fsimg"):
135 fstype = imgtype[:4]
136 myDiskMount = fs_related.ExtDiskMount
137 else:
138 raise errors.CreatorError("Unsupported filesystem type: %s" % fstype)
139
140 extmnt = misc.mkdtemp()
141 extloop = myDiskMount(fs_related.SparseLoopbackDisk(os_image, imgsize),
142 extmnt,
143 fstype,
144 4096,
145 "%s label" % fstype)
146
147 try:
148 extloop.mount()
149
150 except errors.MountError:
151 extloop.cleanup()
152 shutil.rmtree(extmnt, ignore_errors = True)
153 raise
154
155 try:
156 if len(cmd) != 0:
157 cmdline = ' '.join(cmd)
158 else:
159 cmdline = "/bin/bash"
160 envcmd = fs_related.find_binary_inchroot("env", extmnt)
161 if envcmd:
162 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
163 chroot.chroot(extmnt, None, cmdline)
164 except:
165 raise errors.CreatorError("Failed to chroot to %s." %target)
166 finally:
167 chroot.cleanup_after_chroot("img", extloop, os_image_dir, extmnt)
168
169 @classmethod
170 def do_pack(cls, base_on):
171 import subprocess
172
173 def __mkinitrd(instance):
174 kernelver = instance._get_kernel_versions().values()[0][0]
175 args = [ "/usr/libexec/mkliveinitrd", "/boot/initrd-%s.img" % kernelver, "%s" % kernelver ]
176 try:
177 subprocess.call(args, preexec_fn = instance._chroot)
178
179 except OSError, (err, msg):
180 raise errors.CreatorError("Failed to execute /usr/libexec/mkliveinitrd: %s" % msg)
181
182 def __run_post_cleanups(instance):
183 kernelver = instance._get_kernel_versions().values()[0][0]
184 args = ["rm", "-f", "/boot/initrd-%s.img" % kernelver]
185
186 try:
187 subprocess.call(args, preexec_fn = instance._chroot)
188 except OSError, (err, msg):
189 raise errors.CreatorError("Failed to run post cleanups: %s" % msg)
190
191 convertoropts = configmgr.convert
192 convertoropts['name'] = os.path.splitext(os.path.basename(base_on))[0]
193 convertor = liveusb.LiveUSBImageCreator(convertoropts)
194 imgtype = misc.get_image_type(base_on)
195 if imgtype == "btrfsimg":
196 fstype = "btrfs"
197 elif imgtype in ("ext3fsimg", "ext4fsimg"):
198 fstype = imgtype[:4]
199 else:
200 raise errors.CreatorError("Unsupported filesystem type: %s" % fstyp)
201 convertor._set_fstype(fstype)
202 try:
203 convertor.mount(base_on)
204 __mkinitrd(convertor)
205 convertor._create_bootconfig()
206 __run_post_cleanups(convertor)
207 convertor.launch_shell(convertoropts['shell'])
208 convertor.unmount()
209 convertor.package()
210 convertor.print_outimage_info()
211 finally:
212 shutil.rmtree(os.path.dirname(base_on), ignore_errors = True)
213
214 @classmethod
215 def do_unpack(cls, srcimg):
216 img = srcimg
217 imgsize = misc.get_file_size(img) * 1024L * 1024L
218 imgmnt = misc.mkdtemp()
219 disk = fs_related.SparseLoopbackDisk(img, imgsize)
220 imgloop = PartitionedMount(imgmnt, skipformat = True)
221 imgloop.add_disk('/dev/sdb', disk)
222 imgloop.add_partition(imgsize/1024/1024, "/dev/sdb", "/", "vfat", boot=False)
223 try:
224 imgloop.mount()
225 except errors.MountError:
226 imgloop.cleanup()
227 raise
228
229 # legacy LiveOS filesystem layout support, remove for F9 or F10
230 if os.path.exists(imgmnt + "/squashfs.img"):
231 squashimg = imgmnt + "/squashfs.img"
232 else:
233 squashimg = imgmnt + "/LiveOS/squashfs.img"
234
235 tmpoutdir = misc.mkdtemp()
236 # unsquashfs requires outdir mustn't exist
237 shutil.rmtree(tmpoutdir, ignore_errors = True)
238 misc.uncompress_squashfs(squashimg, tmpoutdir)
239
240 try:
241 # legacy LiveOS filesystem layout support, remove for F9 or F10
242 if os.path.exists(tmpoutdir + "/os.img"):
243 os_image = tmpoutdir + "/os.img"
244 else:
245 os_image = tmpoutdir + "/LiveOS/ext3fs.img"
246
247 if not os.path.exists(os_image):
248 raise errors.CreatorError("'%s' is not a valid live CD ISO : neither "
249 "LiveOS/ext3fs.img nor os.img exist" %img)
250 imgname = os.path.basename(srcimg)
251 imgname = os.path.splitext(imgname)[0] + ".img"
252 rtimage = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), imgname)
253 shutil.copyfile(os_image, rtimage)
254
255 finally:
256 imgloop.cleanup()
257 shutil.rmtree(tmpoutdir, ignore_errors = True)
258 shutil.rmtree(imgmnt, ignore_errors = True)
259
260 return rtimage
diff --git a/scripts/lib/mic/plugins/imager/loop_plugin.py b/scripts/lib/mic/plugins/imager/loop_plugin.py
new file mode 100644
index 0000000000..8f4b030f6b
--- /dev/null
+++ b/scripts/lib/mic/plugins/imager/loop_plugin.py
@@ -0,0 +1,255 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import shutil
20import tempfile
21
22from mic import chroot, msger, rt_util
23from mic.utils import misc, fs_related, errors, cmdln
24from mic.conf import configmgr
25from mic.plugin import pluginmgr
26from mic.imager.loop import LoopImageCreator, load_mountpoints
27
28from mic.pluginbase import ImagerPlugin
29class LoopPlugin(ImagerPlugin):
30 name = 'loop'
31
32 @classmethod
33 @cmdln.option("--compress-disk-image", dest="compress_image",
34 type='choice', choices=("gz", "bz2"), default=None,
35 help="Same with --compress-image")
36 # alias to compress-image for compatibility
37 @cmdln.option("--compress-image", dest="compress_image",
38 type='choice', choices=("gz", "bz2"), default=None,
39 help="Compress all loop images with 'gz' or 'bz2'")
40 @cmdln.option("--shrink", action='store_true', default=False,
41 help="Whether to shrink loop images to minimal size")
42 def do_create(self, subcmd, opts, *args):
43 """${cmd_name}: create loop image
44
45 Usage:
46 ${name} ${cmd_name} <ksfile> [OPTS]
47
48 ${cmd_option_list}
49 """
50
51 if len(args) != 1:
52 raise errors.Usage("Extra arguments given")
53
54 creatoropts = configmgr.create
55 ksconf = args[0]
56
57 if creatoropts['runtime'] == "bootstrap":
58 configmgr._ksconf = ksconf
59 rt_util.bootstrap_mic()
60
61 recording_pkgs = []
62 if len(creatoropts['record_pkgs']) > 0:
63 recording_pkgs = creatoropts['record_pkgs']
64
65 if creatoropts['release'] is not None:
66 if 'name' not in recording_pkgs:
67 recording_pkgs.append('name')
68 if 'vcs' not in recording_pkgs:
69 recording_pkgs.append('vcs')
70
71 configmgr._ksconf = ksconf
72
73 # Called After setting the configmgr._ksconf
74 # as the creatoropts['name'] is reset there.
75 if creatoropts['release'] is not None:
76 creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'],
77 creatoropts['release'],
78 creatoropts['name'])
79 # try to find the pkgmgr
80 pkgmgr = None
81 backends = pluginmgr.get_plugins('backend')
82 if 'auto' == creatoropts['pkgmgr']:
83 for key in configmgr.prefer_backends:
84 if key in backends:
85 pkgmgr = backends[key]
86 break
87 else:
88 for key in backends.keys():
89 if key == creatoropts['pkgmgr']:
90 pkgmgr = backends[key]
91 break
92
93 if not pkgmgr:
94 raise errors.CreatorError("Can't find backend: %s, "
95 "available choices: %s" %
96 (creatoropts['pkgmgr'],
97 ','.join(backends.keys())))
98
99 creator = LoopImageCreator(creatoropts,
100 pkgmgr,
101 opts.compress_image,
102 opts.shrink)
103
104 if len(recording_pkgs) > 0:
105 creator._recording_pkgs = recording_pkgs
106
107 image_names = [creator.name + ".img"]
108 image_names.extend(creator.get_image_names())
109 self.check_image_exists(creator.destdir,
110 creator.pack_to,
111 image_names,
112 creatoropts['release'])
113
114 try:
115 creator.check_depend_tools()
116 creator.mount(None, creatoropts["cachedir"])
117 creator.install()
118 creator.configure(creatoropts["repomd"])
119 creator.copy_kernel()
120 creator.unmount()
121 creator.package(creatoropts["outdir"])
122
123 if creatoropts['release'] is not None:
124 creator.release_output(ksconf,
125 creatoropts['outdir'],
126 creatoropts['release'])
127 creator.print_outimage_info()
128
129 except errors.CreatorError:
130 raise
131 finally:
132 creator.cleanup()
133
134 msger.info("Finished.")
135 return 0
136
137 @classmethod
138 def _do_chroot_tar(cls, target, cmd=[]):
139 mountfp_xml = os.path.splitext(target)[0] + '.xml'
140 if not os.path.exists(mountfp_xml):
141 raise errors.CreatorError("No mount point file found for this tar "
142 "image, please check %s" % mountfp_xml)
143
144 import tarfile
145 tar = tarfile.open(target, 'r')
146 tmpdir = misc.mkdtemp()
147 tar.extractall(path=tmpdir)
148 tar.close()
149
150 mntdir = misc.mkdtemp()
151
152 loops = []
153 for (mp, label, name, size, fstype) in load_mountpoints(mountfp_xml):
154 if fstype in ("ext2", "ext3", "ext4"):
155 myDiskMount = fs_related.ExtDiskMount
156 elif fstype == "btrfs":
157 myDiskMount = fs_related.BtrfsDiskMount
158 elif fstype in ("vfat", "msdos"):
159 myDiskMount = fs_related.VfatDiskMount
160 else:
161 msger.error("Cannot support fstype: %s" % fstype)
162
163 name = os.path.join(tmpdir, name)
164 size = size * 1024L * 1024L
165 loop = myDiskMount(fs_related.SparseLoopbackDisk(name, size),
166 os.path.join(mntdir, mp.lstrip('/')),
167 fstype, size, label)
168
169 try:
170 msger.verbose("Mount %s to %s" % (mp, mntdir + mp))
171 fs_related.makedirs(os.path.join(mntdir, mp.lstrip('/')))
172 loop.mount()
173
174 except:
175 loop.cleanup()
176 for lp in reversed(loops):
177 chroot.cleanup_after_chroot("img", lp, None, mntdir)
178
179 shutil.rmtree(tmpdir, ignore_errors=True)
180 raise
181
182 loops.append(loop)
183
184 try:
185 if len(cmd) != 0:
186 cmdline = "/usr/bin/env HOME=/root " + ' '.join(cmd)
187 else:
188 cmdline = "/usr/bin/env HOME=/root /bin/bash"
189 chroot.chroot(mntdir, None, cmdline)
190 except:
191 raise errors.CreatorError("Failed to chroot to %s." % target)
192 finally:
193 for loop in reversed(loops):
194 chroot.cleanup_after_chroot("img", loop, None, mntdir)
195
196 shutil.rmtree(tmpdir, ignore_errors=True)
197
198 @classmethod
199 def do_chroot(cls, target, cmd=[]):
200 if target.endswith('.tar'):
201 import tarfile
202 if tarfile.is_tarfile(target):
203 LoopPlugin._do_chroot_tar(target, cmd)
204 return
205 else:
206 raise errors.CreatorError("damaged tarball for loop images")
207
208 img = target
209 imgsize = misc.get_file_size(img) * 1024L * 1024L
210 imgtype = misc.get_image_type(img)
211 if imgtype == "btrfsimg":
212 fstype = "btrfs"
213 myDiskMount = fs_related.BtrfsDiskMount
214 elif imgtype in ("ext3fsimg", "ext4fsimg"):
215 fstype = imgtype[:4]
216 myDiskMount = fs_related.ExtDiskMount
217 else:
218 raise errors.CreatorError("Unsupported filesystem type: %s" \
219 % imgtype)
220
221 extmnt = misc.mkdtemp()
222 extloop = myDiskMount(fs_related.SparseLoopbackDisk(img, imgsize),
223 extmnt,
224 fstype,
225 4096,
226 "%s label" % fstype)
227 try:
228 extloop.mount()
229
230 except errors.MountError:
231 extloop.cleanup()
232 shutil.rmtree(extmnt, ignore_errors=True)
233 raise
234
235 try:
236 if len(cmd) != 0:
237 cmdline = ' '.join(cmd)
238 else:
239 cmdline = "/bin/bash"
240 envcmd = fs_related.find_binary_inchroot("env", extmnt)
241 if envcmd:
242 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
243 chroot.chroot(extmnt, None, cmdline)
244 except:
245 raise errors.CreatorError("Failed to chroot to %s." % img)
246 finally:
247 chroot.cleanup_after_chroot("img", extloop, None, extmnt)
248
249 @classmethod
250 def do_unpack(cls, srcimg):
251 image = os.path.join(tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"),
252 "target.img")
253 msger.info("Copying file system ...")
254 shutil.copyfile(srcimg, image)
255 return image
diff --git a/scripts/lib/mic/plugins/imager/raw_plugin.py b/scripts/lib/mic/plugins/imager/raw_plugin.py
new file mode 100644
index 0000000000..1b9631dfa2
--- /dev/null
+++ b/scripts/lib/mic/plugins/imager/raw_plugin.py
@@ -0,0 +1,275 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import shutil
20import re
21import tempfile
22
23from mic import chroot, msger, rt_util
24from mic.utils import misc, fs_related, errors, runner, cmdln
25from mic.conf import configmgr
26from mic.plugin import pluginmgr
27from mic.utils.partitionedfs import PartitionedMount
28
29import mic.imager.raw as raw
30
31from mic.pluginbase import ImagerPlugin
32class RawPlugin(ImagerPlugin):
33 name = 'raw'
34
35 @classmethod
36 @cmdln.option("--compress-disk-image", dest="compress_image", type='choice',
37 choices=("gz", "bz2"), default=None,
38 help="Same with --compress-image")
39 @cmdln.option("--compress-image", dest="compress_image", type='choice',
40 choices=("gz", "bz2"), default = None,
41 help="Compress all raw images before package")
42 @cmdln.option("--generate-bmap", action="store_true", default = None,
43 help="also generate the block map file")
44 @cmdln.option("--fstab-entry", dest="fstab_entry", type='choice',
45 choices=("name", "uuid"), default="uuid",
46 help="Set fstab entry, 'name' means using device names, "
47 "'uuid' means using filesystem uuid")
48 def do_create(self, subcmd, opts, *args):
49 """${cmd_name}: create raw image
50
51 Usage:
52 ${name} ${cmd_name} <ksfile> [OPTS]
53
54 ${cmd_option_list}
55 """
56
57 if len(args) != 1:
58 raise errors.Usage("Extra arguments given")
59
60 creatoropts = configmgr.create
61 ksconf = args[0]
62
63 if creatoropts['runtime'] == "bootstrap":
64 configmgr._ksconf = ksconf
65 rt_util.bootstrap_mic()
66
67 recording_pkgs = []
68 if len(creatoropts['record_pkgs']) > 0:
69 recording_pkgs = creatoropts['record_pkgs']
70
71 if creatoropts['release'] is not None:
72 if 'name' not in recording_pkgs:
73 recording_pkgs.append('name')
74 if 'vcs' not in recording_pkgs:
75 recording_pkgs.append('vcs')
76
77 configmgr._ksconf = ksconf
78
79 # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there.
80 if creatoropts['release'] is not None:
81 creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name'])
82
83 # try to find the pkgmgr
84 pkgmgr = None
85 backends = pluginmgr.get_plugins('backend')
86 if 'auto' == creatoropts['pkgmgr']:
87 for key in configmgr.prefer_backends:
88 if key in backends:
89 pkgmgr = backends[key]
90 break
91 else:
92 for key in backends.keys():
93 if key == creatoropts['pkgmgr']:
94 pkgmgr = backends[key]
95 break
96
97 if not pkgmgr:
98 raise errors.CreatorError("Can't find backend: %s, "
99 "available choices: %s" %
100 (creatoropts['pkgmgr'],
101 ','.join(backends.keys())))
102
103 creator = raw.RawImageCreator(creatoropts, pkgmgr, opts.compress_image,
104 opts.generate_bmap, opts.fstab_entry)
105
106 if len(recording_pkgs) > 0:
107 creator._recording_pkgs = recording_pkgs
108
109 images = ["%s-%s.raw" % (creator.name, disk_name)
110 for disk_name in creator.get_disk_names()]
111 self.check_image_exists(creator.destdir,
112 creator.pack_to,
113 images,
114 creatoropts['release'])
115
116 try:
117 creator.check_depend_tools()
118 creator.mount(None, creatoropts["cachedir"])
119 creator.install()
120 creator.configure(creatoropts["repomd"])
121 creator.copy_kernel()
122 creator.unmount()
123 creator.generate_bmap()
124 creator.package(creatoropts["outdir"])
125 if creatoropts['release'] is not None:
126 creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release'])
127 creator.print_outimage_info()
128
129 except errors.CreatorError:
130 raise
131 finally:
132 creator.cleanup()
133
134 msger.info("Finished.")
135 return 0
136
137 @classmethod
138 def do_chroot(cls, target, cmd=[]):
139 img = target
140 imgsize = misc.get_file_size(img) * 1024L * 1024L
141 partedcmd = fs_related.find_binary_path("parted")
142 disk = fs_related.SparseLoopbackDisk(img, imgsize)
143 imgmnt = misc.mkdtemp()
144 imgloop = PartitionedMount(imgmnt, skipformat = True)
145 imgloop.add_disk('/dev/sdb', disk)
146 img_fstype = "ext3"
147
148 msger.info("Partition Table:")
149 partnum = []
150 for line in runner.outs([partedcmd, "-s", img, "print"]).splitlines():
151 # no use strip to keep line output here
152 if "Number" in line:
153 msger.raw(line)
154 if line.strip() and line.strip()[0].isdigit():
155 partnum.append(line.strip()[0])
156 msger.raw(line)
157
158 rootpart = None
159 if len(partnum) > 1:
160 rootpart = msger.choice("please choose root partition", partnum)
161
162 # Check the partitions from raw disk.
163 # if choose root part, the mark it as mounted
164 if rootpart:
165 root_mounted = True
166 else:
167 root_mounted = False
168 partition_mounts = 0
169 for line in runner.outs([partedcmd,"-s",img,"unit","B","print"]).splitlines():
170 line = line.strip()
171
172 # Lines that start with number are the partitions,
173 # because parted can be translated we can't refer to any text lines.
174 if not line or not line[0].isdigit():
175 continue
176
177 # Some vars have extra , as list seperator.
178 line = line.replace(",","")
179
180 # Example of parted output lines that are handled:
181 # Number Start End Size Type File system Flags
182 # 1 512B 3400000511B 3400000000B primary
183 # 2 3400531968B 3656384511B 255852544B primary linux-swap(v1)
184 # 3 3656384512B 3720347647B 63963136B primary fat16 boot, lba
185
186 partition_info = re.split("\s+",line)
187
188 size = partition_info[3].split("B")[0]
189
190 if len(partition_info) < 6 or partition_info[5] in ["boot"]:
191 # No filesystem can be found from partition line. Assuming
192 # btrfs, because that is the only MeeGo fs that parted does
193 # not recognize properly.
194 # TODO: Can we make better assumption?
195 fstype = "btrfs"
196 elif partition_info[5] in ["ext2","ext3","ext4","btrfs"]:
197 fstype = partition_info[5]
198 elif partition_info[5] in ["fat16","fat32"]:
199 fstype = "vfat"
200 elif "swap" in partition_info[5]:
201 fstype = "swap"
202 else:
203 raise errors.CreatorError("Could not recognize partition fs type '%s'." % partition_info[5])
204
205 if rootpart and rootpart == line[0]:
206 mountpoint = '/'
207 elif not root_mounted and fstype in ["ext2","ext3","ext4","btrfs"]:
208 # TODO: Check that this is actually the valid root partition from /etc/fstab
209 mountpoint = "/"
210 root_mounted = True
211 elif fstype == "swap":
212 mountpoint = "swap"
213 else:
214 # TODO: Assing better mount points for the rest of the partitions.
215 partition_mounts += 1
216 mountpoint = "/media/partition_%d" % partition_mounts
217
218 if "boot" in partition_info:
219 boot = True
220 else:
221 boot = False
222
223 msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (size, fstype, mountpoint, boot))
224 # TODO: add_partition should take bytes as size parameter.
225 imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint, fstype = fstype, boot = boot)
226
227 try:
228 imgloop.mount()
229
230 except errors.MountError:
231 imgloop.cleanup()
232 raise
233
234 try:
235 if len(cmd) != 0:
236 cmdline = ' '.join(cmd)
237 else:
238 cmdline = "/bin/bash"
239 envcmd = fs_related.find_binary_inchroot("env", imgmnt)
240 if envcmd:
241 cmdline = "%s HOME=/root %s" % (envcmd, cmdline)
242 chroot.chroot(imgmnt, None, cmdline)
243 except:
244 raise errors.CreatorError("Failed to chroot to %s." %img)
245 finally:
246 chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
247
248 @classmethod
249 def do_unpack(cls, srcimg):
250 srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L
251 srcmnt = misc.mkdtemp("srcmnt")
252 disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize)
253 srcloop = PartitionedMount(srcmnt, skipformat = True)
254
255 srcloop.add_disk('/dev/sdb', disk)
256 srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False)
257 try:
258 srcloop.mount()
259
260 except errors.MountError:
261 srcloop.cleanup()
262 raise
263
264 image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img")
265 args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image]
266
267 msger.info("`dd` image ...")
268 rc = runner.show(args)
269 srcloop.cleanup()
270 shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True)
271
272 if rc != 0:
273 raise errors.CreatorError("Failed to dd")
274 else:
275 return image
diff --git a/scripts/lib/mic/rt_util.py b/scripts/lib/mic/rt_util.py
new file mode 100644
index 0000000000..2a31f4a218
--- /dev/null
+++ b/scripts/lib/mic/rt_util.py
@@ -0,0 +1,223 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2009, 2010, 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18from __future__ import with_statement
19import os
20import sys
21import glob
22import re
23import shutil
24import subprocess
25
26from mic import bootstrap, msger
27from mic.conf import configmgr
28from mic.utils import errors, proxy
29from mic.utils.fs_related import find_binary_path, makedirs
30from mic.chroot import setup_chrootenv, cleanup_chrootenv
31
32expath = lambda p: os.path.abspath(os.path.expanduser(p))
33
34def bootstrap_mic(argv=None):
35
36
37 def mychroot():
38 os.chroot(rootdir)
39 os.chdir(cwd)
40
41 # by default, sys.argv is used to run mic in bootstrap
42 if not argv:
43 argv = sys.argv
44 if argv[0] not in ('/usr/bin/mic', 'mic'):
45 argv[0] = '/usr/bin/mic'
46
47 cropts = configmgr.create
48 bsopts = configmgr.bootstrap
49 distro = bsopts['distro_name'].lower()
50
51 rootdir = bsopts['rootdir']
52 pkglist = bsopts['packages']
53 cwd = os.getcwd()
54
55 # create bootstrap and run mic in bootstrap
56 bsenv = bootstrap.Bootstrap(rootdir, distro, cropts['arch'])
57 bsenv.logfile = cropts['logfile']
58 # rootdir is regenerated as a temp dir
59 rootdir = bsenv.rootdir
60
61 if 'optional' in bsopts:
62 optlist = bsopts['optional']
63 else:
64 optlist = []
65
66 try:
67 msger.info("Creating %s bootstrap ..." % distro)
68 bsenv.create(cropts['repomd'], pkglist, optlist)
69
70 # bootstrap is relocated under "bootstrap"
71 if os.path.exists(os.path.join(rootdir, "bootstrap")):
72 rootdir = os.path.join(rootdir, "bootstrap")
73
74 bsenv.dirsetup(rootdir)
75 sync_mic(rootdir)
76
77 #FIXME: sync the ks file to bootstrap
78 if "/" == os.path.dirname(os.path.abspath(configmgr._ksconf)):
79 safecopy(configmgr._ksconf, rootdir)
80
81 msger.info("Start mic in bootstrap: %s\n" % rootdir)
82 bindmounts = get_bindmounts(cropts)
83 ret = bsenv.run(argv, cwd, rootdir, bindmounts)
84
85 except errors.BootstrapError, err:
86 msger.warning('\n%s' % err)
87 if msger.ask("Switch to native mode and continue?"):
88 return
89 raise
90 except RuntimeError, err:
91 #change exception type but keep the trace back
92 value, tb = sys.exc_info()[1:]
93 raise errors.BootstrapError, value, tb
94 else:
95 sys.exit(ret)
96 finally:
97 bsenv.cleanup()
98
99def get_bindmounts(cropts):
100 binddirs = [
101 os.getcwd(),
102 cropts['tmpdir'],
103 cropts['cachedir'],
104 cropts['outdir'],
105 cropts['local_pkgs_path'],
106 ]
107 bindfiles = [
108 cropts['logfile'],
109 configmgr._ksconf,
110 ]
111
112 for lrepo in cropts['localrepos']:
113 binddirs.append(lrepo)
114
115 bindlist = map(expath, filter(None, binddirs))
116 bindlist += map(os.path.dirname, map(expath, filter(None, bindfiles)))
117 bindlist = sorted(set(bindlist))
118 bindmounts = ';'.join(bindlist)
119 return bindmounts
120
121
122def get_mic_binpath():
123 fp = None
124 try:
125 import pkg_resources # depends on 'setuptools'
126 except ImportError:
127 pass
128 else:
129 dist = pkg_resources.get_distribution('mic')
130 # the real script is under EGG_INFO/scripts
131 if dist.has_metadata('scripts/mic'):
132 fp = os.path.join(dist.egg_info, "scripts/mic")
133
134 if fp:
135 return fp
136
137 # not found script if 'flat' egg installed
138 try:
139 return find_binary_path('mic')
140 except errors.CreatorError:
141 raise errors.BootstrapError("Can't find mic binary in host OS")
142
143
144def get_mic_modpath():
145 try:
146 import mic
147 except ImportError:
148 raise errors.BootstrapError("Can't find mic module in host OS")
149 path = os.path.abspath(mic.__file__)
150 return os.path.dirname(path)
151
152def get_mic_libpath():
153 # TBD: so far mic lib path is hard coded
154 return "/usr/lib/mic"
155
156# the hard code path is prepared for bootstrap
157def sync_mic(bootstrap, binpth = '/usr/bin/mic',
158 libpth='/usr/lib',
159 pylib = '/usr/lib/python2.7/site-packages',
160 conf = '/etc/mic/mic.conf'):
161 _path = lambda p: os.path.join(bootstrap, p.lstrip('/'))
162
163 micpaths = {
164 'binpth': get_mic_binpath(),
165 'libpth': get_mic_libpath(),
166 'pylib': get_mic_modpath(),
167 'conf': '/etc/mic/mic.conf',
168 }
169
170 if not os.path.exists(_path(pylib)):
171 pyptn = '/usr/lib/python?.?/site-packages'
172 pylibs = glob.glob(_path(pyptn))
173 if pylibs:
174 pylib = pylibs[0].replace(bootstrap, '')
175 else:
176 raise errors.BootstrapError("Can't find python site dir in: %s" %
177 bootstrap)
178
179 for key, value in micpaths.items():
180 try:
181 safecopy(value, _path(eval(key)), False, ["*.pyc", "*.pyo"])
182 except (OSError, IOError), err:
183 raise errors.BootstrapError(err)
184
185 # auto select backend
186 conf_str = file(_path(conf)).read()
187 conf_str = re.sub("pkgmgr\s*=\s*.*", "pkgmgr=auto", conf_str)
188 with open(_path(conf), 'w') as wf:
189 wf.write(conf_str)
190
191 # chmod +x /usr/bin/mic
192 os.chmod(_path(binpth), 0777)
193
194 # correct python interpreter
195 mic_cont = file(_path(binpth)).read()
196 mic_cont = "#!/usr/bin/python\n" + mic_cont
197 with open(_path(binpth), 'w') as wf:
198 wf.write(mic_cont)
199
200
201def safecopy(src, dst, symlinks=False, ignore_ptns=()):
202 if os.path.isdir(src):
203 if os.path.isdir(dst):
204 dst = os.path.join(dst, os.path.basename(src))
205 if os.path.exists(dst):
206 shutil.rmtree(dst, ignore_errors=True)
207
208 src = src.rstrip('/')
209 # check common prefix to ignore copying itself
210 if dst.startswith(src + '/'):
211 ignore_ptns = list(ignore_ptns) + [ os.path.basename(src) ]
212
213 ignores = shutil.ignore_patterns(*ignore_ptns)
214 try:
215 shutil.copytree(src, dst, symlinks, ignores)
216 except (OSError, IOError):
217 shutil.rmtree(dst, ignore_errors=True)
218 raise
219 else:
220 if not os.path.isdir(dst):
221 makedirs(os.path.dirname(dst))
222
223 shutil.copy2(src, dst)
diff --git a/scripts/lib/mic/test b/scripts/lib/mic/test
new file mode 100644
index 0000000000..9daeafb986
--- /dev/null
+++ b/scripts/lib/mic/test
@@ -0,0 +1 @@
test
diff --git a/scripts/lib/mic/utils/BmapCreate.py b/scripts/lib/mic/utils/BmapCreate.py
new file mode 100644
index 0000000000..65b19a5f46
--- /dev/null
+++ b/scripts/lib/mic/utils/BmapCreate.py
@@ -0,0 +1,298 @@
1""" This module implements the block map (bmap) creation functionality and
2provides the corresponding API in form of the 'BmapCreate' class.
3
4The idea is that while images files may generally be very large (e.g., 4GiB),
5they may nevertheless contain only little real data, e.g., 512MiB. This data
6are files, directories, file-system meta-data, partition table, etc. When
7copying the image to the target device, you do not have to copy all the 4GiB of
8data, you can copy only 512MiB of it, which is 4 times less, so copying should
9presumably be 4 times faster.
10
11The block map file is an XML file which contains a list of blocks which have to
12be copied to the target device. The other blocks are not used and there is no
13need to copy them. The XML file also contains some additional information like
14block size, image size, count of mapped blocks, etc. There are also many
15commentaries, so it is human-readable.
16
17The image has to be a sparse file. Generally, this means that when you generate
18this image file, you should start with a huge sparse file which contains a
19single hole spanning the entire file. Then you should partition it, write all
20the data (probably by means of loop-back mounting the image or parts of it),
21etc. The end result should be a sparse file where mapped areas represent useful
22parts of the image and holes represent useless parts of the image, which do not
23have to be copied when copying the image to the target device.
24
25This module uses the FIBMAP ioctl to detect holes. """
26
27# Disable the following pylint recommendations:
28# * Too many instance attributes - R0902
29# * Too few public methods - R0903
30# pylint: disable=R0902,R0903
31
32import hashlib
33from mic.utils.misc import human_size
34from mic.utils import Fiemap
35
36# The bmap format version we generate
37SUPPORTED_BMAP_VERSION = "1.3"
38
39_BMAP_START_TEMPLATE = \
40"""<?xml version="1.0" ?>
41<!-- This file contains the block map for an image file, which is basically
42 a list of useful (mapped) block numbers in the image file. In other words,
43 it lists only those blocks which contain data (boot sector, partition
44 table, file-system metadata, files, directories, extents, etc). These
45 blocks have to be copied to the target device. The other blocks do not
46 contain any useful data and do not have to be copied to the target
47 device.
48
49 The block map an optimization which allows to copy or flash the image to
50 the image quicker than copying of flashing the entire image. This is
51 because with bmap less data is copied: <MappedBlocksCount> blocks instead
52 of <BlocksCount> blocks.
53
54 Besides the machine-readable data, this file contains useful commentaries
55 which contain human-readable information like image size, percentage of
56 mapped data, etc.
57
58 The 'version' attribute is the block map file format version in the
59 'major.minor' format. The version major number is increased whenever an
60 incompatible block map format change is made. The minor number changes
61 in case of minor backward-compatible changes. -->
62
63<bmap version="%s">
64 <!-- Image size in bytes: %s -->
65 <ImageSize> %u </ImageSize>
66
67 <!-- Size of a block in bytes -->
68 <BlockSize> %u </BlockSize>
69
70 <!-- Count of blocks in the image file -->
71 <BlocksCount> %u </BlocksCount>
72
73"""
74
75class Error(Exception):
76 """ A class for exceptions generated by this module. We currently support
77 only one type of exceptions, and we basically throw human-readable problem
78 description in case of errors. """
79 pass
80
81class BmapCreate:
82 """ This class implements the bmap creation functionality. To generate a
83 bmap for an image (which is supposedly a sparse file), you should first
84 create an instance of 'BmapCreate' and provide:
85
86 * full path or a file-like object of the image to create bmap for
87 * full path or a file object to use for writing the results to
88
89 Then you should invoke the 'generate()' method of this class. It will use
90 the FIEMAP ioctl to generate the bmap. """
91
92 def _open_image_file(self):
93 """ Open the image file. """
94
95 try:
96 self._f_image = open(self._image_path, 'rb')
97 except IOError as err:
98 raise Error("cannot open image file '%s': %s" \
99 % (self._image_path, err))
100
101 self._f_image_needs_close = True
102
103 def _open_bmap_file(self):
104 """ Open the bmap file. """
105
106 try:
107 self._f_bmap = open(self._bmap_path, 'w+')
108 except IOError as err:
109 raise Error("cannot open bmap file '%s': %s" \
110 % (self._bmap_path, err))
111
112 self._f_bmap_needs_close = True
113
114 def __init__(self, image, bmap):
115 """ Initialize a class instance:
116 * image - full path or a file-like object of the image to create bmap
117 for
118 * bmap - full path or a file object to use for writing the resulting
119 bmap to """
120
121 self.image_size = None
122 self.image_size_human = None
123 self.block_size = None
124 self.blocks_cnt = None
125 self.mapped_cnt = None
126 self.mapped_size = None
127 self.mapped_size_human = None
128 self.mapped_percent = None
129
130 self._mapped_count_pos1 = None
131 self._mapped_count_pos2 = None
132 self._sha1_pos = None
133
134 self._f_image_needs_close = False
135 self._f_bmap_needs_close = False
136
137 if hasattr(image, "read"):
138 self._f_image = image
139 self._image_path = image.name
140 else:
141 self._image_path = image
142 self._open_image_file()
143
144 if hasattr(bmap, "read"):
145 self._f_bmap = bmap
146 self._bmap_path = bmap.name
147 else:
148 self._bmap_path = bmap
149 self._open_bmap_file()
150
151 self.fiemap = Fiemap.Fiemap(self._f_image)
152
153 self.image_size = self.fiemap.image_size
154 self.image_size_human = human_size(self.image_size)
155 if self.image_size == 0:
156 raise Error("cannot generate bmap for zero-sized image file '%s'" \
157 % self._image_path)
158
159 self.block_size = self.fiemap.block_size
160 self.blocks_cnt = self.fiemap.blocks_cnt
161
162 def _bmap_file_start(self):
163 """ A helper function which generates the starting contents of the
164 block map file: the header comment, image size, block size, etc. """
165
166 # We do not know the amount of mapped blocks at the moment, so just put
167 # whitespaces instead of real numbers. Assume the longest possible
168 # numbers.
169 mapped_count = ' ' * len(str(self.image_size))
170 mapped_size_human = ' ' * len(self.image_size_human)
171
172 xml = _BMAP_START_TEMPLATE \
173 % (SUPPORTED_BMAP_VERSION, self.image_size_human,
174 self.image_size, self.block_size, self.blocks_cnt)
175 xml += " <!-- Count of mapped blocks: "
176
177 self._f_bmap.write(xml)
178 self._mapped_count_pos1 = self._f_bmap.tell()
179
180 # Just put white-spaces instead of real information about mapped blocks
181 xml = "%s or %.1f -->\n" % (mapped_size_human, 100.0)
182 xml += " <MappedBlocksCount> "
183
184 self._f_bmap.write(xml)
185 self._mapped_count_pos2 = self._f_bmap.tell()
186
187 xml = "%s </MappedBlocksCount>\n\n" % mapped_count
188
189 # pylint: disable=C0301
190 xml += " <!-- The checksum of this bmap file. When it is calculated, the value of\n"
191 xml += " the SHA1 checksum has be zeoro (40 ASCII \"0\" symbols). -->\n"
192 xml += " <BmapFileSHA1> "
193
194 self._f_bmap.write(xml)
195 self._sha1_pos = self._f_bmap.tell()
196
197 xml = "0" * 40 + " </BmapFileSHA1>\n\n"
198 xml += " <!-- The block map which consists of elements which may either be a\n"
199 xml += " range of blocks or a single block. The 'sha1' attribute (if present)\n"
200 xml += " is the SHA1 checksum of this blocks range. -->\n"
201 xml += " <BlockMap>\n"
202 # pylint: enable=C0301
203
204 self._f_bmap.write(xml)
205
206 def _bmap_file_end(self):
207 """ A helper function which generates the final parts of the block map
208 file: the ending tags and the information about the amount of mapped
209 blocks. """
210
211 xml = " </BlockMap>\n"
212 xml += "</bmap>\n"
213
214 self._f_bmap.write(xml)
215
216 self._f_bmap.seek(self._mapped_count_pos1)
217 self._f_bmap.write("%s or %.1f%%" % \
218 (self.mapped_size_human, self.mapped_percent))
219
220 self._f_bmap.seek(self._mapped_count_pos2)
221 self._f_bmap.write("%u" % self.mapped_cnt)
222
223 self._f_bmap.seek(0)
224 sha1 = hashlib.sha1(self._f_bmap.read()).hexdigest()
225 self._f_bmap.seek(self._sha1_pos)
226 self._f_bmap.write("%s" % sha1)
227
228 def _calculate_sha1(self, first, last):
229 """ A helper function which calculates SHA1 checksum for the range of
230 blocks of the image file: from block 'first' to block 'last'. """
231
232 start = first * self.block_size
233 end = (last + 1) * self.block_size
234
235 self._f_image.seek(start)
236 hash_obj = hashlib.new("sha1")
237
238 chunk_size = 1024*1024
239 to_read = end - start
240 read = 0
241
242 while read < to_read:
243 if read + chunk_size > to_read:
244 chunk_size = to_read - read
245 chunk = self._f_image.read(chunk_size)
246 hash_obj.update(chunk)
247 read += chunk_size
248
249 return hash_obj.hexdigest()
250
251 def generate(self, include_checksums = True):
252 """ Generate bmap for the image file. If 'include_checksums' is 'True',
253 also generate SHA1 checksums for block ranges. """
254
255 # Save image file position in order to restore it at the end
256 image_pos = self._f_image.tell()
257
258 self._bmap_file_start()
259
260 # Generate the block map and write it to the XML block map
261 # file as we go.
262 self.mapped_cnt = 0
263 for first, last in self.fiemap.get_mapped_ranges(0, self.blocks_cnt):
264 self.mapped_cnt += last - first + 1
265 if include_checksums:
266 sha1 = self._calculate_sha1(first, last)
267 sha1 = " sha1=\"%s\"" % sha1
268 else:
269 sha1 = ""
270
271 if first != last:
272 self._f_bmap.write(" <Range%s> %s-%s </Range>\n" \
273 % (sha1, first, last))
274 else:
275 self._f_bmap.write(" <Range%s> %s </Range>\n" \
276 % (sha1, first))
277
278 self.mapped_size = self.mapped_cnt * self.block_size
279 self.mapped_size_human = human_size(self.mapped_size)
280 self.mapped_percent = (self.mapped_cnt * 100.0) / self.blocks_cnt
281
282 self._bmap_file_end()
283
284 try:
285 self._f_bmap.flush()
286 except IOError as err:
287 raise Error("cannot flush the bmap file '%s': %s" \
288 % (self._bmap_path, err))
289
290 self._f_image.seek(image_pos)
291
292 def __del__(self):
293 """ The class destructor which closes the opened files. """
294
295 if self._f_image_needs_close:
296 self._f_image.close()
297 if self._f_bmap_needs_close:
298 self._f_bmap.close()
diff --git a/scripts/lib/mic/utils/Fiemap.py b/scripts/lib/mic/utils/Fiemap.py
new file mode 100644
index 0000000000..f2db6ff0b8
--- /dev/null
+++ b/scripts/lib/mic/utils/Fiemap.py
@@ -0,0 +1,252 @@
1""" This module implements python API for the FIEMAP ioctl. The FIEMAP ioctl
2allows to find holes and mapped areas in a file. """
3
4# Note, a lot of code in this module is not very readable, because it deals
5# with the rather complex FIEMAP ioctl. To understand the code, you need to
6# know the FIEMAP interface, which is documented in the
7# Documentation/filesystems/fiemap.txt file in the Linux kernel sources.
8
9# Disable the following pylint recommendations:
10# * Too many instance attributes (R0902)
11# pylint: disable=R0902
12
13import os
14import struct
15import array
16import fcntl
17from mic.utils.misc import get_block_size
18
19# Format string for 'struct fiemap'
20_FIEMAP_FORMAT = "=QQLLLL"
21# sizeof(struct fiemap)
22_FIEMAP_SIZE = struct.calcsize(_FIEMAP_FORMAT)
23# Format string for 'struct fiemap_extent'
24_FIEMAP_EXTENT_FORMAT = "=QQQQQLLLL"
25# sizeof(struct fiemap_extent)
26_FIEMAP_EXTENT_SIZE = struct.calcsize(_FIEMAP_EXTENT_FORMAT)
27# The FIEMAP ioctl number
28_FIEMAP_IOCTL = 0xC020660B
29
30# Minimum buffer which is required for 'class Fiemap' to operate
31MIN_BUFFER_SIZE = _FIEMAP_SIZE + _FIEMAP_EXTENT_SIZE
32# The default buffer size for 'class Fiemap'
33DEFAULT_BUFFER_SIZE = 256 * 1024
34
35class Error(Exception):
36 """ A class for exceptions generated by this module. We currently support
37 only one type of exceptions, and we basically throw human-readable problem
38 description in case of errors. """
39 pass
40
41class Fiemap:
42 """ This class provides API to the FIEMAP ioctl. Namely, it allows to
43 iterate over all mapped blocks and over all holes. """
44
45 def _open_image_file(self):
46 """ Open the image file. """
47
48 try:
49 self._f_image = open(self._image_path, 'rb')
50 except IOError as err:
51 raise Error("cannot open image file '%s': %s" \
52 % (self._image_path, err))
53
54 self._f_image_needs_close = True
55
56 def __init__(self, image, buf_size = DEFAULT_BUFFER_SIZE):
57 """ Initialize a class instance. The 'image' argument is full path to
58 the file to operate on, or a file object to operate on.
59
60 The 'buf_size' argument is the size of the buffer for 'struct
61 fiemap_extent' elements which will be used when invoking the FIEMAP
62 ioctl. The larger is the buffer, the less times the FIEMAP ioctl will
63 be invoked. """
64
65 self._f_image_needs_close = False
66
67 if hasattr(image, "fileno"):
68 self._f_image = image
69 self._image_path = image.name
70 else:
71 self._image_path = image
72 self._open_image_file()
73
74 # Validate 'buf_size'
75 if buf_size < MIN_BUFFER_SIZE:
76 raise Error("too small buffer (%d bytes), minimum is %d bytes" \
77 % (buf_size, MIN_BUFFER_SIZE))
78
79 # How many 'struct fiemap_extent' elements fit the buffer
80 buf_size -= _FIEMAP_SIZE
81 self._fiemap_extent_cnt = buf_size / _FIEMAP_EXTENT_SIZE
82 self._buf_size = self._fiemap_extent_cnt * _FIEMAP_EXTENT_SIZE
83 self._buf_size += _FIEMAP_SIZE
84
85 # Allocate a mutable buffer for the FIEMAP ioctl
86 self._buf = array.array('B', [0] * self._buf_size)
87
88 self.image_size = os.fstat(self._f_image.fileno()).st_size
89
90 try:
91 self.block_size = get_block_size(self._f_image)
92 except IOError as err:
93 raise Error("cannot get block size for '%s': %s" \
94 % (self._image_path, err))
95
96 self.blocks_cnt = self.image_size + self.block_size - 1
97 self.blocks_cnt /= self.block_size
98
99 # Synchronize the image file to make sure FIEMAP returns correct values
100 try:
101 self._f_image.flush()
102 except IOError as err:
103 raise Error("cannot flush image file '%s': %s" \
104 % (self._image_path, err))
105 try:
106 os.fsync(self._f_image.fileno()),
107 except OSError as err:
108 raise Error("cannot synchronize image file '%s': %s " \
109 % (self._image_path, err.strerror))
110
111 # Check if the FIEMAP ioctl is supported
112 self.block_is_mapped(0)
113
114 def __del__(self):
115 """ The class destructor which closes the opened files. """
116
117 if self._f_image_needs_close:
118 self._f_image.close()
119
120 def _invoke_fiemap(self, block, count):
121 """ Invoke the FIEMAP ioctl for 'count' blocks of the file starting from
122 block number 'block'.
123
124 The full result of the operation is stored in 'self._buf' on exit.
125 Returns the unpacked 'struct fiemap' data structure in form of a python
126 list (just like 'struct.upack()'). """
127
128 if block < 0 or block >= self.blocks_cnt:
129 raise Error("bad block number %d, should be within [0, %d]" \
130 % (block, self.blocks_cnt))
131
132 # Initialize the 'struct fiemap' part of the buffer
133 struct.pack_into(_FIEMAP_FORMAT, self._buf, 0, block * self.block_size,
134 count * self.block_size, 0, 0,
135 self._fiemap_extent_cnt, 0)
136
137 try:
138 fcntl.ioctl(self._f_image, _FIEMAP_IOCTL, self._buf, 1)
139 except IOError as err:
140 error_msg = "the FIEMAP ioctl failed for '%s': %s" \
141 % (self._image_path, err)
142 if err.errno == os.errno.EPERM or err.errno == os.errno.EACCES:
143 # The FIEMAP ioctl was added in kernel version 2.6.28 in 2008
144 error_msg += " (looks like your kernel does not support FIEMAP)"
145
146 raise Error(error_msg)
147
148 return struct.unpack(_FIEMAP_FORMAT, self._buf[:_FIEMAP_SIZE])
149
150 def block_is_mapped(self, block):
151 """ This function returns 'True' if block number 'block' of the image
152 file is mapped and 'False' otherwise. """
153
154 struct_fiemap = self._invoke_fiemap(block, 1)
155
156 # The 3rd element of 'struct_fiemap' is the 'fm_mapped_extents' field.
157 # If it contains zero, the block is not mapped, otherwise it is
158 # mapped.
159 return bool(struct_fiemap[3])
160
161 def block_is_unmapped(self, block):
162 """ This function returns 'True' if block number 'block' of the image
163 file is not mapped (hole) and 'False' otherwise. """
164
165 return not self.block_is_mapped(block)
166
167 def _unpack_fiemap_extent(self, index):
168 """ Unpack a 'struct fiemap_extent' structure object number 'index'
169 from the internal 'self._buf' buffer. """
170
171 offset = _FIEMAP_SIZE + _FIEMAP_EXTENT_SIZE * index
172 return struct.unpack(_FIEMAP_EXTENT_FORMAT,
173 self._buf[offset : offset + _FIEMAP_EXTENT_SIZE])
174
175 def _do_get_mapped_ranges(self, start, count):
176 """ Implements most the functionality for the 'get_mapped_ranges()'
177 generator: invokes the FIEMAP ioctl, walks through the mapped
178 extents and yields mapped block ranges. However, the ranges may be
179 consecutive (e.g., (1, 100), (100, 200)) and 'get_mapped_ranges()'
180 simply merges them. """
181
182 block = start
183 while block < start + count:
184 struct_fiemap = self._invoke_fiemap(block, count)
185
186 mapped_extents = struct_fiemap[3]
187 if mapped_extents == 0:
188 # No more mapped blocks
189 return
190
191 extent = 0
192 while extent < mapped_extents:
193 fiemap_extent = self._unpack_fiemap_extent(extent)
194
195 # Start of the extent
196 extent_start = fiemap_extent[0]
197 # Starting block number of the extent
198 extent_block = extent_start / self.block_size
199 # Length of the extent
200 extent_len = fiemap_extent[2]
201 # Count of blocks in the extent
202 extent_count = extent_len / self.block_size
203
204 # Extent length and offset have to be block-aligned
205 assert extent_start % self.block_size == 0
206 assert extent_len % self.block_size == 0
207
208 if extent_block > start + count - 1:
209 return
210
211 first = max(extent_block, block)
212 last = min(extent_block + extent_count, start + count) - 1
213 yield (first, last)
214
215 extent += 1
216
217 block = extent_block + extent_count
218
219 def get_mapped_ranges(self, start, count):
220 """ A generator which yields ranges of mapped blocks in the file. The
221 ranges are tuples of 2 elements: [first, last], where 'first' is the
222 first mapped block and 'last' is the last mapped block.
223
224 The ranges are yielded for the area of the file of size 'count' blocks,
225 starting from block 'start'. """
226
227 iterator = self._do_get_mapped_ranges(start, count)
228
229 first_prev, last_prev = iterator.next()
230
231 for first, last in iterator:
232 if last_prev == first - 1:
233 last_prev = last
234 else:
235 yield (first_prev, last_prev)
236 first_prev, last_prev = first, last
237
238 yield (first_prev, last_prev)
239
240 def get_unmapped_ranges(self, start, count):
241 """ Just like 'get_mapped_ranges()', but yields unmapped block ranges
242 instead (holes). """
243
244 hole_first = start
245 for first, last in self._do_get_mapped_ranges(start, count):
246 if first > hole_first:
247 yield (hole_first, first - 1)
248
249 hole_first = last + 1
250
251 if hole_first < start + count:
252 yield (hole_first, start + count - 1)
diff --git a/scripts/lib/mic/utils/__init__.py b/scripts/lib/mic/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/scripts/lib/mic/utils/__init__.py
diff --git a/scripts/lib/mic/utils/cmdln.py b/scripts/lib/mic/utils/cmdln.py
new file mode 100644
index 0000000000..b099473ee4
--- /dev/null
+++ b/scripts/lib/mic/utils/cmdln.py
@@ -0,0 +1,1586 @@
1#!/usr/bin/env python
2# Copyright (c) 2002-2007 ActiveState Software Inc.
3# License: MIT (see LICENSE.txt for license details)
4# Author: Trent Mick
5# Home: http://trentm.com/projects/cmdln/
6
7"""An improvement on Python's standard cmd.py module.
8
9As with cmd.py, this module provides "a simple framework for writing
10line-oriented command intepreters." This module provides a 'RawCmdln'
11class that fixes some design flaws in cmd.Cmd, making it more scalable
12and nicer to use for good 'cvs'- or 'svn'-style command line interfaces
13or simple shells. And it provides a 'Cmdln' class that add
14optparse-based option processing. Basically you use it like this:
15
16 import cmdln
17
18 class MySVN(cmdln.Cmdln):
19 name = "svn"
20
21 @cmdln.alias('stat', 'st')
22 @cmdln.option('-v', '--verbose', action='store_true'
23 help='print verbose information')
24 def do_status(self, subcmd, opts, *paths):
25 print "handle 'svn status' command"
26
27 #...
28
29 if __name__ == "__main__":
30 shell = MySVN()
31 retval = shell.main()
32 sys.exit(retval)
33
34See the README.txt or <http://trentm.com/projects/cmdln/> for more
35details.
36"""
37
38__version_info__ = (1, 1, 2)
39__version__ = '.'.join(map(str, __version_info__))
40
41import os
42import sys
43import re
44import cmd
45import optparse
46from pprint import pprint
47import sys
48
49
50
51
52#---- globals
53
54LOOP_ALWAYS, LOOP_NEVER, LOOP_IF_EMPTY = range(3)
55
56# An unspecified optional argument when None is a meaningful value.
57_NOT_SPECIFIED = ("Not", "Specified")
58
59# Pattern to match a TypeError message from a call that
60# failed because of incorrect number of arguments (see
61# Python/getargs.c).
62_INCORRECT_NUM_ARGS_RE = re.compile(
63 r"(takes [\w ]+ )(\d+)( arguments? \()(\d+)( given\))")
64
65
66
67#---- exceptions
68
69class CmdlnError(Exception):
70 """A cmdln.py usage error."""
71 def __init__(self, msg):
72 self.msg = msg
73 def __str__(self):
74 return self.msg
75
76class CmdlnUserError(Exception):
77 """An error by a user of a cmdln-based tool/shell."""
78 pass
79
80
81
82#---- public methods and classes
83
84def alias(*aliases):
85 """Decorator to add aliases for Cmdln.do_* command handlers.
86
87 Example:
88 class MyShell(cmdln.Cmdln):
89 @cmdln.alias("!", "sh")
90 def do_shell(self, argv):
91 #...implement 'shell' command
92 """
93 def decorate(f):
94 if not hasattr(f, "aliases"):
95 f.aliases = []
96 f.aliases += aliases
97 return f
98 return decorate
99
100
101class RawCmdln(cmd.Cmd):
102 """An improved (on cmd.Cmd) framework for building multi-subcommand
103 scripts (think "svn" & "cvs") and simple shells (think "pdb" and
104 "gdb").
105
106 A simple example:
107
108 import cmdln
109
110 class MySVN(cmdln.RawCmdln):
111 name = "svn"
112
113 @cmdln.aliases('stat', 'st')
114 def do_status(self, argv):
115 print "handle 'svn status' command"
116
117 if __name__ == "__main__":
118 shell = MySVN()
119 retval = shell.main()
120 sys.exit(retval)
121
122 See <http://trentm.com/projects/cmdln> for more information.
123 """
124 name = None # if unset, defaults basename(sys.argv[0])
125 prompt = None # if unset, defaults to self.name+"> "
126 version = None # if set, default top-level options include --version
127
128 # Default messages for some 'help' command error cases.
129 # They are interpolated with one arg: the command.
130 nohelp = "no help on '%s'"
131 unknowncmd = "unknown command: '%s'"
132
133 helpindent = '' # string with which to indent help output
134
135 def __init__(self, completekey='tab',
136 stdin=None, stdout=None, stderr=None):
137 """Cmdln(completekey='tab', stdin=None, stdout=None, stderr=None)
138
139 The optional argument 'completekey' is the readline name of a
140 completion key; it defaults to the Tab key. If completekey is
141 not None and the readline module is available, command completion
142 is done automatically.
143
144 The optional arguments 'stdin', 'stdout' and 'stderr' specify
145 alternate input, output and error output file objects; if not
146 specified, sys.* are used.
147
148 If 'stdout' but not 'stderr' is specified, stdout is used for
149 error output. This is to provide least surprise for users used
150 to only the 'stdin' and 'stdout' options with cmd.Cmd.
151 """
152 import sys
153 if self.name is None:
154 self.name = os.path.basename(sys.argv[0])
155 if self.prompt is None:
156 self.prompt = self.name+"> "
157 self._name_str = self._str(self.name)
158 self._prompt_str = self._str(self.prompt)
159 if stdin is not None:
160 self.stdin = stdin
161 else:
162 self.stdin = sys.stdin
163 if stdout is not None:
164 self.stdout = stdout
165 else:
166 self.stdout = sys.stdout
167 if stderr is not None:
168 self.stderr = stderr
169 elif stdout is not None:
170 self.stderr = stdout
171 else:
172 self.stderr = sys.stderr
173 self.cmdqueue = []
174 self.completekey = completekey
175 self.cmdlooping = False
176
177 def get_optparser(self):
178 """Hook for subclasses to set the option parser for the
179 top-level command/shell.
180
181 This option parser is used retrieved and used by `.main()' to
182 handle top-level options.
183
184 The default implements a single '-h|--help' option. Sub-classes
185 can return None to have no options at the top-level. Typically
186 an instance of CmdlnOptionParser should be returned.
187 """
188 version = (self.version is not None
189 and "%s %s" % (self._name_str, self.version)
190 or None)
191 return CmdlnOptionParser(self, version=version)
192
193 def postoptparse(self):
194 """Hook method executed just after `.main()' parses top-level
195 options.
196
197 When called `self.options' holds the results of the option parse.
198 """
199 pass
200
201 def main(self, argv=None, loop=LOOP_NEVER):
202 """A possible mainline handler for a script, like so:
203
204 import cmdln
205 class MyCmd(cmdln.Cmdln):
206 name = "mycmd"
207 ...
208
209 if __name__ == "__main__":
210 MyCmd().main()
211
212 By default this will use sys.argv to issue a single command to
213 'MyCmd', then exit. The 'loop' argument can be use to control
214 interactive shell behaviour.
215
216 Arguments:
217 "argv" (optional, default sys.argv) is the command to run.
218 It must be a sequence, where the first element is the
219 command name and subsequent elements the args for that
220 command.
221 "loop" (optional, default LOOP_NEVER) is a constant
222 indicating if a command loop should be started (i.e. an
223 interactive shell). Valid values (constants on this module):
224 LOOP_ALWAYS start loop and run "argv", if any
225 LOOP_NEVER run "argv" (or .emptyline()) and exit
226 LOOP_IF_EMPTY run "argv", if given, and exit;
227 otherwise, start loop
228 """
229 if argv is None:
230 import sys
231 argv = sys.argv
232 else:
233 argv = argv[:] # don't modify caller's list
234
235 self.optparser = self.get_optparser()
236 if self.optparser: # i.e. optparser=None means don't process for opts
237 try:
238 self.options, args = self.optparser.parse_args(argv[1:])
239 except CmdlnUserError, ex:
240 msg = "%s: %s\nTry '%s help' for info.\n"\
241 % (self.name, ex, self.name)
242 self.stderr.write(self._str(msg))
243 self.stderr.flush()
244 return 1
245 except StopOptionProcessing, ex:
246 return 0
247 else:
248 self.options, args = None, argv[1:]
249 self.postoptparse()
250
251 if loop == LOOP_ALWAYS:
252 if args:
253 self.cmdqueue.append(args)
254 return self.cmdloop()
255 elif loop == LOOP_NEVER:
256 if args:
257 return self.cmd(args)
258 else:
259 return self.emptyline()
260 elif loop == LOOP_IF_EMPTY:
261 if args:
262 return self.cmd(args)
263 else:
264 return self.cmdloop()
265
266 def cmd(self, argv):
267 """Run one command and exit.
268
269 "argv" is the arglist for the command to run. argv[0] is the
270 command to run. If argv is an empty list then the
271 'emptyline' handler is run.
272
273 Returns the return value from the command handler.
274 """
275 assert isinstance(argv, (list, tuple)), \
276 "'argv' is not a sequence: %r" % argv
277 retval = None
278 try:
279 argv = self.precmd(argv)
280 retval = self.onecmd(argv)
281 self.postcmd(argv)
282 except:
283 if not self.cmdexc(argv):
284 raise
285 retval = 1
286 return retval
287
288 def _str(self, s):
289 """Safely convert the given str/unicode to a string for printing."""
290 try:
291 return str(s)
292 except UnicodeError:
293 #XXX What is the proper encoding to use here? 'utf-8' seems
294 # to work better than "getdefaultencoding" (usually
295 # 'ascii'), on OS X at least.
296 #import sys
297 #return s.encode(sys.getdefaultencoding(), "replace")
298 return s.encode("utf-8", "replace")
299
300 def cmdloop(self, intro=None):
301 """Repeatedly issue a prompt, accept input, parse into an argv, and
302 dispatch (via .precmd(), .onecmd() and .postcmd()), passing them
303 the argv. In other words, start a shell.
304
305 "intro" (optional) is a introductory message to print when
306 starting the command loop. This overrides the class
307 "intro" attribute, if any.
308 """
309 self.cmdlooping = True
310 self.preloop()
311 if self.use_rawinput and self.completekey:
312 try:
313 import readline
314 self.old_completer = readline.get_completer()
315 readline.set_completer(self.complete)
316 readline.parse_and_bind(self.completekey+": complete")
317 except ImportError:
318 pass
319 try:
320 if intro is None:
321 intro = self.intro
322 if intro:
323 intro_str = self._str(intro)
324 self.stdout.write(intro_str+'\n')
325 self.stop = False
326 retval = None
327 while not self.stop:
328 if self.cmdqueue:
329 argv = self.cmdqueue.pop(0)
330 assert isinstance(argv, (list, tuple)), \
331 "item on 'cmdqueue' is not a sequence: %r" % argv
332 else:
333 if self.use_rawinput:
334 try:
335 line = raw_input(self._prompt_str)
336 except EOFError:
337 line = 'EOF'
338 else:
339 self.stdout.write(self._prompt_str)
340 self.stdout.flush()
341 line = self.stdin.readline()
342 if not len(line):
343 line = 'EOF'
344 else:
345 line = line[:-1] # chop '\n'
346 argv = line2argv(line)
347 try:
348 argv = self.precmd(argv)
349 retval = self.onecmd(argv)
350 self.postcmd(argv)
351 except:
352 if not self.cmdexc(argv):
353 raise
354 retval = 1
355 self.lastretval = retval
356 self.postloop()
357 finally:
358 if self.use_rawinput and self.completekey:
359 try:
360 import readline
361 readline.set_completer(self.old_completer)
362 except ImportError:
363 pass
364 self.cmdlooping = False
365 return retval
366
367 def precmd(self, argv):
368 """Hook method executed just before the command argv is
369 interpreted, but after the input prompt is generated and issued.
370
371 "argv" is the cmd to run.
372
373 Returns an argv to run (i.e. this method can modify the command
374 to run).
375 """
376 return argv
377
378 def postcmd(self, argv):
379 """Hook method executed just after a command dispatch is finished.
380
381 "argv" is the command that was run.
382 """
383 pass
384
385 def cmdexc(self, argv):
386 """Called if an exception is raised in any of precmd(), onecmd(),
387 or postcmd(). If True is returned, the exception is deemed to have
388 been dealt with. Otherwise, the exception is re-raised.
389
390 The default implementation handles CmdlnUserError's, which
391 typically correspond to user error in calling commands (as
392 opposed to programmer error in the design of the script using
393 cmdln.py).
394 """
395 import sys
396 type, exc, traceback = sys.exc_info()
397 if isinstance(exc, CmdlnUserError):
398 msg = "%s %s: %s\nTry '%s help %s' for info.\n"\
399 % (self.name, argv[0], exc, self.name, argv[0])
400 self.stderr.write(self._str(msg))
401 self.stderr.flush()
402 return True
403
404 def onecmd(self, argv):
405 if not argv:
406 return self.emptyline()
407 self.lastcmd = argv
408 cmdname = self._get_canonical_cmd_name(argv[0])
409 if cmdname:
410 handler = self._get_cmd_handler(cmdname)
411 if handler:
412 return self._dispatch_cmd(handler, argv)
413 return self.default(argv)
414
415 def _dispatch_cmd(self, handler, argv):
416 return handler(argv)
417
418 def default(self, argv):
419 """Hook called to handle a command for which there is no handler.
420
421 "argv" is the command and arguments to run.
422
423 The default implementation writes and error message to stderr
424 and returns an error exit status.
425
426 Returns a numeric command exit status.
427 """
428 errmsg = self._str(self.unknowncmd % (argv[0],))
429 if self.cmdlooping:
430 self.stderr.write(errmsg+"\n")
431 else:
432 self.stderr.write("%s: %s\nTry '%s help' for info.\n"
433 % (self._name_str, errmsg, self._name_str))
434 self.stderr.flush()
435 return 1
436
437 def parseline(self, line):
438 # This is used by Cmd.complete (readline completer function) to
439 # massage the current line buffer before completion processing.
440 # We override to drop special '!' handling.
441 line = line.strip()
442 if not line:
443 return None, None, line
444 elif line[0] == '?':
445 line = 'help ' + line[1:]
446 i, n = 0, len(line)
447 while i < n and line[i] in self.identchars: i = i+1
448 cmd, arg = line[:i], line[i:].strip()
449 return cmd, arg, line
450
451 def helpdefault(self, cmd, known):
452 """Hook called to handle help on a command for which there is no
453 help handler.
454
455 "cmd" is the command name on which help was requested.
456 "known" is a boolean indicating if this command is known
457 (i.e. if there is a handler for it).
458
459 Returns a return code.
460 """
461 if known:
462 msg = self._str(self.nohelp % (cmd,))
463 if self.cmdlooping:
464 self.stderr.write(msg + '\n')
465 else:
466 self.stderr.write("%s: %s\n" % (self.name, msg))
467 else:
468 msg = self.unknowncmd % (cmd,)
469 if self.cmdlooping:
470 self.stderr.write(msg + '\n')
471 else:
472 self.stderr.write("%s: %s\n"
473 "Try '%s help' for info.\n"
474 % (self.name, msg, self.name))
475 self.stderr.flush()
476 return 1
477
478 def do_help(self, argv):
479 """${cmd_name}: give detailed help on a specific sub-command
480
481 Usage:
482 ${name} help [COMMAND]
483 """
484 if len(argv) > 1: # asking for help on a particular command
485 doc = None
486 cmdname = self._get_canonical_cmd_name(argv[1]) or argv[1]
487 if not cmdname:
488 return self.helpdefault(argv[1], False)
489 else:
490 helpfunc = getattr(self, "help_"+cmdname, None)
491 if helpfunc:
492 doc = helpfunc()
493 else:
494 handler = self._get_cmd_handler(cmdname)
495 if handler:
496 doc = handler.__doc__
497 if doc is None:
498 return self.helpdefault(argv[1], handler != None)
499 else: # bare "help" command
500 doc = self.__class__.__doc__ # try class docstring
501 if doc is None:
502 # Try to provide some reasonable useful default help.
503 if self.cmdlooping: prefix = ""
504 else: prefix = self.name+' '
505 doc = """Usage:
506 %sCOMMAND [ARGS...]
507 %shelp [COMMAND]
508
509 ${option_list}
510 ${command_list}
511 ${help_list}
512 """ % (prefix, prefix)
513 cmdname = None
514
515 if doc: # *do* have help content, massage and print that
516 doc = self._help_reindent(doc)
517 doc = self._help_preprocess(doc, cmdname)
518 doc = doc.rstrip() + '\n' # trim down trailing space
519 self.stdout.write(self._str(doc))
520 self.stdout.flush()
521 do_help.aliases = ["?"]
522
523 def _help_reindent(self, help, indent=None):
524 """Hook to re-indent help strings before writing to stdout.
525
526 "help" is the help content to re-indent
527 "indent" is a string with which to indent each line of the
528 help content after normalizing. If unspecified or None
529 then the default is use: the 'self.helpindent' class
530 attribute. By default this is the empty string, i.e.
531 no indentation.
532
533 By default, all common leading whitespace is removed and then
534 the lot is indented by 'self.helpindent'. When calculating the
535 common leading whitespace the first line is ignored -- hence
536 help content for Conan can be written as follows and have the
537 expected indentation:
538
539 def do_crush(self, ...):
540 '''${cmd_name}: crush your enemies, see them driven before you...
541
542 c.f. Conan the Barbarian'''
543 """
544 if indent is None:
545 indent = self.helpindent
546 lines = help.splitlines(0)
547 _dedentlines(lines, skip_first_line=True)
548 lines = [(indent+line).rstrip() for line in lines]
549 return '\n'.join(lines)
550
551 def _help_preprocess(self, help, cmdname):
552 """Hook to preprocess a help string before writing to stdout.
553
554 "help" is the help string to process.
555 "cmdname" is the canonical sub-command name for which help
556 is being given, or None if the help is not specific to a
557 command.
558
559 By default the following template variables are interpolated in
560 help content. (Note: these are similar to Python 2.4's
561 string.Template interpolation but not quite.)
562
563 ${name}
564 The tool's/shell's name, i.e. 'self.name'.
565 ${option_list}
566 A formatted table of options for this shell/tool.
567 ${command_list}
568 A formatted table of available sub-commands.
569 ${help_list}
570 A formatted table of additional help topics (i.e. 'help_*'
571 methods with no matching 'do_*' method).
572 ${cmd_name}
573 The name (and aliases) for this sub-command formatted as:
574 "NAME (ALIAS1, ALIAS2, ...)".
575 ${cmd_usage}
576 A formatted usage block inferred from the command function
577 signature.
578 ${cmd_option_list}
579 A formatted table of options for this sub-command. (This is
580 only available for commands using the optparse integration,
581 i.e. using @cmdln.option decorators or manually setting the
582 'optparser' attribute on the 'do_*' method.)
583
584 Returns the processed help.
585 """
586 preprocessors = {
587 "${name}": self._help_preprocess_name,
588 "${option_list}": self._help_preprocess_option_list,
589 "${command_list}": self._help_preprocess_command_list,
590 "${help_list}": self._help_preprocess_help_list,
591 "${cmd_name}": self._help_preprocess_cmd_name,
592 "${cmd_usage}": self._help_preprocess_cmd_usage,
593 "${cmd_option_list}": self._help_preprocess_cmd_option_list,
594 }
595
596 for marker, preprocessor in preprocessors.items():
597 if marker in help:
598 help = preprocessor(help, cmdname)
599 return help
600
601 def _help_preprocess_name(self, help, cmdname=None):
602 return help.replace("${name}", self.name)
603
604 def _help_preprocess_option_list(self, help, cmdname=None):
605 marker = "${option_list}"
606 indent, indent_width = _get_indent(marker, help)
607 suffix = _get_trailing_whitespace(marker, help)
608
609 if self.optparser:
610 # Setup formatting options and format.
611 # - Indentation of 4 is better than optparse default of 2.
612 # C.f. Damian Conway's discussion of this in Perl Best
613 # Practices.
614 self.optparser.formatter.indent_increment = 4
615 self.optparser.formatter.current_indent = indent_width
616 block = self.optparser.format_option_help() + '\n'
617 else:
618 block = ""
619
620 help = help.replace(indent+marker+suffix, block, 1)
621 return help
622
623
624 def _help_preprocess_command_list(self, help, cmdname=None):
625 marker = "${command_list}"
626 indent, indent_width = _get_indent(marker, help)
627 suffix = _get_trailing_whitespace(marker, help)
628
629 # Find any aliases for commands.
630 token2canonical = self._get_canonical_map()
631 aliases = {}
632 for token, cmdname in token2canonical.items():
633 if token == cmdname: continue
634 aliases.setdefault(cmdname, []).append(token)
635
636 # Get the list of (non-hidden) commands and their
637 # documentation, if any.
638 cmdnames = {} # use a dict to strip duplicates
639 for attr in self.get_names():
640 if attr.startswith("do_"):
641 cmdnames[attr[3:]] = True
642 cmdnames = cmdnames.keys()
643 cmdnames.sort()
644 linedata = []
645 for cmdname in cmdnames:
646 if aliases.get(cmdname):
647 a = aliases[cmdname]
648 a.sort()
649 cmdstr = "%s (%s)" % (cmdname, ", ".join(a))
650 else:
651 cmdstr = cmdname
652 doc = None
653 try:
654 helpfunc = getattr(self, 'help_'+cmdname)
655 except AttributeError:
656 handler = self._get_cmd_handler(cmdname)
657 if handler:
658 doc = handler.__doc__
659 else:
660 doc = helpfunc()
661
662 # Strip "${cmd_name}: " from the start of a command's doc. Best
663 # practice dictates that command help strings begin with this, but
664 # it isn't at all wanted for the command list.
665 to_strip = "${cmd_name}:"
666 if doc and doc.startswith(to_strip):
667 #log.debug("stripping %r from start of %s's help string",
668 # to_strip, cmdname)
669 doc = doc[len(to_strip):].lstrip()
670 linedata.append( (cmdstr, doc) )
671
672 if linedata:
673 subindent = indent + ' '*4
674 lines = _format_linedata(linedata, subindent, indent_width+4)
675 block = indent + "Commands:\n" \
676 + '\n'.join(lines) + "\n\n"
677 help = help.replace(indent+marker+suffix, block, 1)
678 return help
679
680 def _gen_names_and_attrs(self):
681 # Inheritance says we have to look in class and
682 # base classes; order is not important.
683 names = []
684 classes = [self.__class__]
685 while classes:
686 aclass = classes.pop(0)
687 if aclass.__bases__:
688 classes = classes + list(aclass.__bases__)
689 for name in dir(aclass):
690 yield (name, getattr(aclass, name))
691
692 def _help_preprocess_help_list(self, help, cmdname=None):
693 marker = "${help_list}"
694 indent, indent_width = _get_indent(marker, help)
695 suffix = _get_trailing_whitespace(marker, help)
696
697 # Determine the additional help topics, if any.
698 helpnames = {}
699 token2cmdname = self._get_canonical_map()
700 for attrname, attr in self._gen_names_and_attrs():
701 if not attrname.startswith("help_"): continue
702 helpname = attrname[5:]
703 if helpname not in token2cmdname:
704 helpnames[helpname] = attr
705
706 if helpnames:
707 linedata = [(n, a.__doc__ or "") for n, a in helpnames.items()]
708 linedata.sort()
709
710 subindent = indent + ' '*4
711 lines = _format_linedata(linedata, subindent, indent_width+4)
712 block = (indent
713 + "Additional help topics (run `%s help TOPIC'):\n" % self.name
714 + '\n'.join(lines)
715 + "\n\n")
716 else:
717 block = ''
718 help = help.replace(indent+marker+suffix, block, 1)
719 return help
720
721 def _help_preprocess_cmd_name(self, help, cmdname=None):
722 marker = "${cmd_name}"
723 handler = self._get_cmd_handler(cmdname)
724 if not handler:
725 raise CmdlnError("cannot preprocess '%s' into help string: "
726 "could not find command handler for %r"
727 % (marker, cmdname))
728 s = cmdname
729 if hasattr(handler, "aliases"):
730 s += " (%s)" % (", ".join(handler.aliases))
731 help = help.replace(marker, s)
732 return help
733
734 #TODO: this only makes sense as part of the Cmdln class.
735 # Add hooks to add help preprocessing template vars and put
736 # this one on that class.
737 def _help_preprocess_cmd_usage(self, help, cmdname=None):
738 marker = "${cmd_usage}"
739 handler = self._get_cmd_handler(cmdname)
740 if not handler:
741 raise CmdlnError("cannot preprocess '%s' into help string: "
742 "could not find command handler for %r"
743 % (marker, cmdname))
744 indent, indent_width = _get_indent(marker, help)
745 suffix = _get_trailing_whitespace(marker, help)
746
747 # Extract the introspection bits we need.
748 func = handler.im_func
749 if func.func_defaults:
750 func_defaults = list(func.func_defaults)
751 else:
752 func_defaults = []
753 co_argcount = func.func_code.co_argcount
754 co_varnames = func.func_code.co_varnames
755 co_flags = func.func_code.co_flags
756 CO_FLAGS_ARGS = 4
757 CO_FLAGS_KWARGS = 8
758
759 # Adjust argcount for possible *args and **kwargs arguments.
760 argcount = co_argcount
761 if co_flags & CO_FLAGS_ARGS: argcount += 1
762 if co_flags & CO_FLAGS_KWARGS: argcount += 1
763
764 # Determine the usage string.
765 usage = "%s %s" % (self.name, cmdname)
766 if argcount <= 2: # handler ::= do_FOO(self, argv)
767 usage += " [ARGS...]"
768 elif argcount >= 3: # handler ::= do_FOO(self, subcmd, opts, ...)
769 argnames = list(co_varnames[3:argcount])
770 tail = ""
771 if co_flags & CO_FLAGS_KWARGS:
772 name = argnames.pop(-1)
773 import warnings
774 # There is no generally accepted mechanism for passing
775 # keyword arguments from the command line. Could
776 # *perhaps* consider: arg=value arg2=value2 ...
777 warnings.warn("argument '**%s' on '%s.%s' command "
778 "handler will never get values"
779 % (name, self.__class__.__name__,
780 func.func_name))
781 if co_flags & CO_FLAGS_ARGS:
782 name = argnames.pop(-1)
783 tail = "[%s...]" % name.upper()
784 while func_defaults:
785 func_defaults.pop(-1)
786 name = argnames.pop(-1)
787 tail = "[%s%s%s]" % (name.upper(), (tail and ' ' or ''), tail)
788 while argnames:
789 name = argnames.pop(-1)
790 tail = "%s %s" % (name.upper(), tail)
791 usage += ' ' + tail
792
793 block_lines = [
794 self.helpindent + "Usage:",
795 self.helpindent + ' '*4 + usage
796 ]
797 block = '\n'.join(block_lines) + '\n\n'
798
799 help = help.replace(indent+marker+suffix, block, 1)
800 return help
801
802 #TODO: this only makes sense as part of the Cmdln class.
803 # Add hooks to add help preprocessing template vars and put
804 # this one on that class.
805 def _help_preprocess_cmd_option_list(self, help, cmdname=None):
806 marker = "${cmd_option_list}"
807 handler = self._get_cmd_handler(cmdname)
808 if not handler:
809 raise CmdlnError("cannot preprocess '%s' into help string: "
810 "could not find command handler for %r"
811 % (marker, cmdname))
812 indent, indent_width = _get_indent(marker, help)
813 suffix = _get_trailing_whitespace(marker, help)
814 if hasattr(handler, "optparser"):
815 # Setup formatting options and format.
816 # - Indentation of 4 is better than optparse default of 2.
817 # C.f. Damian Conway's discussion of this in Perl Best
818 # Practices.
819 handler.optparser.formatter.indent_increment = 4
820 handler.optparser.formatter.current_indent = indent_width
821 block = handler.optparser.format_option_help() + '\n'
822 else:
823 block = ""
824
825 help = help.replace(indent+marker+suffix, block, 1)
826 return help
827
828 def _get_canonical_cmd_name(self, token):
829 map = self._get_canonical_map()
830 return map.get(token, None)
831
832 def _get_canonical_map(self):
833 """Return a mapping of available command names and aliases to
834 their canonical command name.
835 """
836 cacheattr = "_token2canonical"
837 if not hasattr(self, cacheattr):
838 # Get the list of commands and their aliases, if any.
839 token2canonical = {}
840 cmd2funcname = {} # use a dict to strip duplicates
841 for attr in self.get_names():
842 if attr.startswith("do_"): cmdname = attr[3:]
843 elif attr.startswith("_do_"): cmdname = attr[4:]
844 else:
845 continue
846 cmd2funcname[cmdname] = attr
847 token2canonical[cmdname] = cmdname
848 for cmdname, funcname in cmd2funcname.items(): # add aliases
849 func = getattr(self, funcname)
850 aliases = getattr(func, "aliases", [])
851 for alias in aliases:
852 if alias in cmd2funcname:
853 import warnings
854 warnings.warn("'%s' alias for '%s' command conflicts "
855 "with '%s' handler"
856 % (alias, cmdname, cmd2funcname[alias]))
857 continue
858 token2canonical[alias] = cmdname
859 setattr(self, cacheattr, token2canonical)
860 return getattr(self, cacheattr)
861
862 def _get_cmd_handler(self, cmdname):
863 handler = None
864 try:
865 handler = getattr(self, 'do_' + cmdname)
866 except AttributeError:
867 try:
868 # Private command handlers begin with "_do_".
869 handler = getattr(self, '_do_' + cmdname)
870 except AttributeError:
871 pass
872 return handler
873
874 def _do_EOF(self, argv):
875 # Default EOF handler
876 # Note: an actual EOF is redirected to this command.
877 #TODO: separate name for this. Currently it is available from
878 # command-line. Is that okay?
879 self.stdout.write('\n')
880 self.stdout.flush()
881 self.stop = True
882
883 def emptyline(self):
884 # Different from cmd.Cmd: don't repeat the last command for an
885 # emptyline.
886 if self.cmdlooping:
887 pass
888 else:
889 return self.do_help(["help"])
890
891
892#---- optparse.py extension to fix (IMO) some deficiencies
893#
894# See the class _OptionParserEx docstring for details.
895#
896
897class StopOptionProcessing(Exception):
898 """Indicate that option *and argument* processing should stop
899 cleanly. This is not an error condition. It is similar in spirit to
900 StopIteration. This is raised by _OptionParserEx's default "help"
901 and "version" option actions and can be raised by custom option
902 callbacks too.
903
904 Hence the typical CmdlnOptionParser (a subclass of _OptionParserEx)
905 usage is:
906
907 parser = CmdlnOptionParser(mycmd)
908 parser.add_option("-f", "--force", dest="force")
909 ...
910 try:
911 opts, args = parser.parse_args()
912 except StopOptionProcessing:
913 # normal termination, "--help" was probably given
914 sys.exit(0)
915 """
916
917class _OptionParserEx(optparse.OptionParser):
918 """An optparse.OptionParser that uses exceptions instead of sys.exit.
919
920 This class is an extension of optparse.OptionParser that differs
921 as follows:
922 - Correct (IMO) the default OptionParser error handling to never
923 sys.exit(). Instead OptParseError exceptions are passed through.
924 - Add the StopOptionProcessing exception (a la StopIteration) to
925 indicate normal termination of option processing.
926 See StopOptionProcessing's docstring for details.
927
928 I'd also like to see the following in the core optparse.py, perhaps
929 as a RawOptionParser which would serve as a base class for the more
930 generally used OptionParser (that works as current):
931 - Remove the implicit addition of the -h|--help and --version
932 options. They can get in the way (e.g. if want '-?' and '-V' for
933 these as well) and it is not hard to do:
934 optparser.add_option("-h", "--help", action="help")
935 optparser.add_option("--version", action="version")
936 These are good practices, just not valid defaults if they can
937 get in the way.
938 """
939 def error(self, msg):
940 raise optparse.OptParseError(msg)
941
942 def exit(self, status=0, msg=None):
943 if status == 0:
944 raise StopOptionProcessing(msg)
945 else:
946 #TODO: don't lose status info here
947 raise optparse.OptParseError(msg)
948
949
950
951#---- optparse.py-based option processing support
952
953class CmdlnOptionParser(_OptionParserEx):
954 """An optparse.OptionParser class more appropriate for top-level
955 Cmdln options. For parsing of sub-command options, see
956 SubCmdOptionParser.
957
958 Changes:
959 - disable_interspersed_args() by default, because a Cmdln instance
960 has sub-commands which may themselves have options.
961 - Redirect print_help() to the Cmdln.do_help() which is better
962 equiped to handle the "help" action.
963 - error() will raise a CmdlnUserError: OptionParse.error() is meant
964 to be called for user errors. Raising a well-known error here can
965 make error handling clearer.
966 - Also see the changes in _OptionParserEx.
967 """
968 def __init__(self, cmdln, **kwargs):
969 self.cmdln = cmdln
970 kwargs["prog"] = self.cmdln.name
971 _OptionParserEx.__init__(self, **kwargs)
972 self.disable_interspersed_args()
973
974 def print_help(self, file=None):
975 self.cmdln.onecmd(["help"])
976
977 def error(self, msg):
978 raise CmdlnUserError(msg)
979
980
981class SubCmdOptionParser(_OptionParserEx):
982 def set_cmdln_info(self, cmdln, subcmd):
983 """Called by Cmdln to pass relevant info about itself needed
984 for print_help().
985 """
986 self.cmdln = cmdln
987 self.subcmd = subcmd
988
989 def print_help(self, file=None):
990 self.cmdln.onecmd(["help", self.subcmd])
991
992 def error(self, msg):
993 raise CmdlnUserError(msg)
994
995
996def option(*args, **kwargs):
997 """Decorator to add an option to the optparser argument of a Cmdln
998 subcommand.
999
1000 Example:
1001 class MyShell(cmdln.Cmdln):
1002 @cmdln.option("-f", "--force", help="force removal")
1003 def do_remove(self, subcmd, opts, *args):
1004 #...
1005 """
1006 #XXX Is there a possible optimization for many options to not have a
1007 # large stack depth here?
1008 def decorate(f):
1009 if not hasattr(f, "optparser"):
1010 f.optparser = SubCmdOptionParser()
1011 f.optparser.add_option(*args, **kwargs)
1012 return f
1013 return decorate
1014
1015
1016class Cmdln(RawCmdln):
1017 """An improved (on cmd.Cmd) framework for building multi-subcommand
1018 scripts (think "svn" & "cvs") and simple shells (think "pdb" and
1019 "gdb").
1020
1021 A simple example:
1022
1023 import cmdln
1024
1025 class MySVN(cmdln.Cmdln):
1026 name = "svn"
1027
1028 @cmdln.aliases('stat', 'st')
1029 @cmdln.option('-v', '--verbose', action='store_true'
1030 help='print verbose information')
1031 def do_status(self, subcmd, opts, *paths):
1032 print "handle 'svn status' command"
1033
1034 #...
1035
1036 if __name__ == "__main__":
1037 shell = MySVN()
1038 retval = shell.main()
1039 sys.exit(retval)
1040
1041 'Cmdln' extends 'RawCmdln' by providing optparse option processing
1042 integration. See this class' _dispatch_cmd() docstring and
1043 <http://trentm.com/projects/cmdln> for more information.
1044 """
1045 def _dispatch_cmd(self, handler, argv):
1046 """Introspect sub-command handler signature to determine how to
1047 dispatch the command. The raw handler provided by the base
1048 'RawCmdln' class is still supported:
1049
1050 def do_foo(self, argv):
1051 # 'argv' is the vector of command line args, argv[0] is
1052 # the command name itself (i.e. "foo" or an alias)
1053 pass
1054
1055 In addition, if the handler has more than 2 arguments option
1056 processing is automatically done (using optparse):
1057
1058 @cmdln.option('-v', '--verbose', action='store_true')
1059 def do_bar(self, subcmd, opts, *args):
1060 # subcmd = <"bar" or an alias>
1061 # opts = <an optparse.Values instance>
1062 if opts.verbose:
1063 print "lots of debugging output..."
1064 # args = <tuple of arguments>
1065 for arg in args:
1066 bar(arg)
1067
1068 TODO: explain that "*args" can be other signatures as well.
1069
1070 The `cmdln.option` decorator corresponds to an `add_option()`
1071 method call on an `optparse.OptionParser` instance.
1072
1073 You can declare a specific number of arguments:
1074
1075 @cmdln.option('-v', '--verbose', action='store_true')
1076 def do_bar2(self, subcmd, opts, bar_one, bar_two):
1077 #...
1078
1079 and an appropriate error message will be raised/printed if the
1080 command is called with a different number of args.
1081 """
1082 co_argcount = handler.im_func.func_code.co_argcount
1083 if co_argcount == 2: # handler ::= do_foo(self, argv)
1084 return handler(argv)
1085 elif co_argcount >= 3: # handler ::= do_foo(self, subcmd, opts, ...)
1086 try:
1087 optparser = handler.optparser
1088 except AttributeError:
1089 optparser = handler.im_func.optparser = SubCmdOptionParser()
1090 assert isinstance(optparser, SubCmdOptionParser)
1091 optparser.set_cmdln_info(self, argv[0])
1092 try:
1093 opts, args = optparser.parse_args(argv[1:])
1094 except StopOptionProcessing:
1095 #TODO: this doesn't really fly for a replacement of
1096 # optparse.py behaviour, does it?
1097 return 0 # Normal command termination
1098
1099 try:
1100 return handler(argv[0], opts, *args)
1101 except TypeError, ex:
1102 # Some TypeError's are user errors:
1103 # do_foo() takes at least 4 arguments (3 given)
1104 # do_foo() takes at most 5 arguments (6 given)
1105 # do_foo() takes exactly 5 arguments (6 given)
1106 # Raise CmdlnUserError for these with a suitably
1107 # massaged error message.
1108 import sys
1109 tb = sys.exc_info()[2] # the traceback object
1110 if tb.tb_next is not None:
1111 # If the traceback is more than one level deep, then the
1112 # TypeError do *not* happen on the "handler(...)" call
1113 # above. In that we don't want to handle it specially
1114 # here: it would falsely mask deeper code errors.
1115 raise
1116 msg = ex.args[0]
1117 match = _INCORRECT_NUM_ARGS_RE.search(msg)
1118 if match:
1119 msg = list(match.groups())
1120 msg[1] = int(msg[1]) - 3
1121 if msg[1] == 1:
1122 msg[2] = msg[2].replace("arguments", "argument")
1123 msg[3] = int(msg[3]) - 3
1124 msg = ''.join(map(str, msg))
1125 raise CmdlnUserError(msg)
1126 else:
1127 raise
1128 else:
1129 raise CmdlnError("incorrect argcount for %s(): takes %d, must "
1130 "take 2 for 'argv' signature or 3+ for 'opts' "
1131 "signature" % (handler.__name__, co_argcount))
1132
1133
1134
1135#---- internal support functions
1136
1137def _format_linedata(linedata, indent, indent_width):
1138 """Format specific linedata into a pleasant layout.
1139
1140 "linedata" is a list of 2-tuples of the form:
1141 (<item-display-string>, <item-docstring>)
1142 "indent" is a string to use for one level of indentation
1143 "indent_width" is a number of columns by which the
1144 formatted data will be indented when printed.
1145
1146 The <item-display-string> column is held to 15 columns.
1147 """
1148 lines = []
1149 WIDTH = 78 - indent_width
1150 SPACING = 2
1151 NAME_WIDTH_LOWER_BOUND = 13
1152 NAME_WIDTH_UPPER_BOUND = 16
1153 NAME_WIDTH = max([len(s) for s,d in linedata])
1154 if NAME_WIDTH < NAME_WIDTH_LOWER_BOUND:
1155 NAME_WIDTH = NAME_WIDTH_LOWER_BOUND
1156 else:
1157 NAME_WIDTH = NAME_WIDTH_UPPER_BOUND
1158
1159 DOC_WIDTH = WIDTH - NAME_WIDTH - SPACING
1160 for namestr, doc in linedata:
1161 line = indent + namestr
1162 if len(namestr) <= NAME_WIDTH:
1163 line += ' ' * (NAME_WIDTH + SPACING - len(namestr))
1164 else:
1165 lines.append(line)
1166 line = indent + ' ' * (NAME_WIDTH + SPACING)
1167 line += _summarize_doc(doc, DOC_WIDTH)
1168 lines.append(line.rstrip())
1169 return lines
1170
1171def _summarize_doc(doc, length=60):
1172 r"""Parse out a short one line summary from the given doclines.
1173
1174 "doc" is the doc string to summarize.
1175 "length" is the max length for the summary
1176
1177 >>> _summarize_doc("this function does this")
1178 'this function does this'
1179 >>> _summarize_doc("this function does this", 10)
1180 'this fu...'
1181 >>> _summarize_doc("this function does this\nand that")
1182 'this function does this and that'
1183 >>> _summarize_doc("this function does this\n\nand that")
1184 'this function does this'
1185 """
1186 import re
1187 if doc is None:
1188 return ""
1189 assert length > 3, "length <= 3 is absurdly short for a doc summary"
1190 doclines = doc.strip().splitlines(0)
1191 if not doclines:
1192 return ""
1193
1194 summlines = []
1195 for i, line in enumerate(doclines):
1196 stripped = line.strip()
1197 if not stripped:
1198 break
1199 summlines.append(stripped)
1200 if len(''.join(summlines)) >= length:
1201 break
1202
1203 summary = ' '.join(summlines)
1204 if len(summary) > length:
1205 summary = summary[:length-3] + "..."
1206 return summary
1207
1208
1209def line2argv(line):
1210 r"""Parse the given line into an argument vector.
1211
1212 "line" is the line of input to parse.
1213
1214 This may get niggly when dealing with quoting and escaping. The
1215 current state of this parsing may not be completely thorough/correct
1216 in this respect.
1217
1218 >>> from cmdln import line2argv
1219 >>> line2argv("foo")
1220 ['foo']
1221 >>> line2argv("foo bar")
1222 ['foo', 'bar']
1223 >>> line2argv("foo bar ")
1224 ['foo', 'bar']
1225 >>> line2argv(" foo bar")
1226 ['foo', 'bar']
1227
1228 Quote handling:
1229
1230 >>> line2argv("'foo bar'")
1231 ['foo bar']
1232 >>> line2argv('"foo bar"')
1233 ['foo bar']
1234 >>> line2argv(r'"foo\"bar"')
1235 ['foo"bar']
1236 >>> line2argv("'foo bar' spam")
1237 ['foo bar', 'spam']
1238 >>> line2argv("'foo 'bar spam")
1239 ['foo bar', 'spam']
1240
1241 >>> line2argv('some\tsimple\ttests')
1242 ['some', 'simple', 'tests']
1243 >>> line2argv('a "more complex" test')
1244 ['a', 'more complex', 'test']
1245 >>> line2argv('a more="complex test of " quotes')
1246 ['a', 'more=complex test of ', 'quotes']
1247 >>> line2argv('a more" complex test of " quotes')
1248 ['a', 'more complex test of ', 'quotes']
1249 >>> line2argv('an "embedded \\"quote\\""')
1250 ['an', 'embedded "quote"']
1251
1252 # Komodo bug 48027
1253 >>> line2argv('foo bar C:\\')
1254 ['foo', 'bar', 'C:\\']
1255
1256 # Komodo change 127581
1257 >>> line2argv(r'"\test\slash" "foo bar" "foo\"bar"')
1258 ['\\test\\slash', 'foo bar', 'foo"bar']
1259
1260 # Komodo change 127629
1261 >>> if sys.platform == "win32":
1262 ... line2argv(r'\foo\bar') == ['\\foo\\bar']
1263 ... line2argv(r'\\foo\\bar') == ['\\\\foo\\\\bar']
1264 ... line2argv('"foo') == ['foo']
1265 ... else:
1266 ... line2argv(r'\foo\bar') == ['foobar']
1267 ... line2argv(r'\\foo\\bar') == ['\\foo\\bar']
1268 ... try:
1269 ... line2argv('"foo')
1270 ... except ValueError, ex:
1271 ... "not terminated" in str(ex)
1272 True
1273 True
1274 True
1275 """
1276 import string
1277 line = line.strip()
1278 argv = []
1279 state = "default"
1280 arg = None # the current argument being parsed
1281 i = -1
1282 while 1:
1283 i += 1
1284 if i >= len(line): break
1285 ch = line[i]
1286
1287 if ch == "\\" and i+1 < len(line):
1288 # escaped char always added to arg, regardless of state
1289 if arg is None: arg = ""
1290 if (sys.platform == "win32"
1291 or state in ("double-quoted", "single-quoted")
1292 ) and line[i+1] not in tuple('"\''):
1293 arg += ch
1294 i += 1
1295 arg += line[i]
1296 continue
1297
1298 if state == "single-quoted":
1299 if ch == "'":
1300 state = "default"
1301 else:
1302 arg += ch
1303 elif state == "double-quoted":
1304 if ch == '"':
1305 state = "default"
1306 else:
1307 arg += ch
1308 elif state == "default":
1309 if ch == '"':
1310 if arg is None: arg = ""
1311 state = "double-quoted"
1312 elif ch == "'":
1313 if arg is None: arg = ""
1314 state = "single-quoted"
1315 elif ch in string.whitespace:
1316 if arg is not None:
1317 argv.append(arg)
1318 arg = None
1319 else:
1320 if arg is None: arg = ""
1321 arg += ch
1322 if arg is not None:
1323 argv.append(arg)
1324 if not sys.platform == "win32" and state != "default":
1325 raise ValueError("command line is not terminated: unfinished %s "
1326 "segment" % state)
1327 return argv
1328
1329
1330def argv2line(argv):
1331 r"""Put together the given argument vector into a command line.
1332
1333 "argv" is the argument vector to process.
1334
1335 >>> from cmdln import argv2line
1336 >>> argv2line(['foo'])
1337 'foo'
1338 >>> argv2line(['foo', 'bar'])
1339 'foo bar'
1340 >>> argv2line(['foo', 'bar baz'])
1341 'foo "bar baz"'
1342 >>> argv2line(['foo"bar'])
1343 'foo"bar'
1344 >>> print argv2line(['foo" bar'])
1345 'foo" bar'
1346 >>> print argv2line(["foo' bar"])
1347 "foo' bar"
1348 >>> argv2line(["foo'bar"])
1349 "foo'bar"
1350 """
1351 escapedArgs = []
1352 for arg in argv:
1353 if ' ' in arg and '"' not in arg:
1354 arg = '"'+arg+'"'
1355 elif ' ' in arg and "'" not in arg:
1356 arg = "'"+arg+"'"
1357 elif ' ' in arg:
1358 arg = arg.replace('"', r'\"')
1359 arg = '"'+arg+'"'
1360 escapedArgs.append(arg)
1361 return ' '.join(escapedArgs)
1362
1363
1364# Recipe: dedent (0.1) in /Users/trentm/tm/recipes/cookbook
1365def _dedentlines(lines, tabsize=8, skip_first_line=False):
1366 """_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines
1367
1368 "lines" is a list of lines to dedent.
1369 "tabsize" is the tab width to use for indent width calculations.
1370 "skip_first_line" is a boolean indicating if the first line should
1371 be skipped for calculating the indent width and for dedenting.
1372 This is sometimes useful for docstrings and similar.
1373
1374 Same as dedent() except operates on a sequence of lines. Note: the
1375 lines list is modified **in-place**.
1376 """
1377 DEBUG = False
1378 if DEBUG:
1379 print "dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\
1380 % (tabsize, skip_first_line)
1381 indents = []
1382 margin = None
1383 for i, line in enumerate(lines):
1384 if i == 0 and skip_first_line: continue
1385 indent = 0
1386 for ch in line:
1387 if ch == ' ':
1388 indent += 1
1389 elif ch == '\t':
1390 indent += tabsize - (indent % tabsize)
1391 elif ch in '\r\n':
1392 continue # skip all-whitespace lines
1393 else:
1394 break
1395 else:
1396 continue # skip all-whitespace lines
1397 if DEBUG: print "dedent: indent=%d: %r" % (indent, line)
1398 if margin is None:
1399 margin = indent
1400 else:
1401 margin = min(margin, indent)
1402 if DEBUG: print "dedent: margin=%r" % margin
1403
1404 if margin is not None and margin > 0:
1405 for i, line in enumerate(lines):
1406 if i == 0 and skip_first_line: continue
1407 removed = 0
1408 for j, ch in enumerate(line):
1409 if ch == ' ':
1410 removed += 1
1411 elif ch == '\t':
1412 removed += tabsize - (removed % tabsize)
1413 elif ch in '\r\n':
1414 if DEBUG: print "dedent: %r: EOL -> strip up to EOL" % line
1415 lines[i] = lines[i][j:]
1416 break
1417 else:
1418 raise ValueError("unexpected non-whitespace char %r in "
1419 "line %r while removing %d-space margin"
1420 % (ch, line, margin))
1421 if DEBUG:
1422 print "dedent: %r: %r -> removed %d/%d"\
1423 % (line, ch, removed, margin)
1424 if removed == margin:
1425 lines[i] = lines[i][j+1:]
1426 break
1427 elif removed > margin:
1428 lines[i] = ' '*(removed-margin) + lines[i][j+1:]
1429 break
1430 return lines
1431
1432def _dedent(text, tabsize=8, skip_first_line=False):
1433 """_dedent(text, tabsize=8, skip_first_line=False) -> dedented text
1434
1435 "text" is the text to dedent.
1436 "tabsize" is the tab width to use for indent width calculations.
1437 "skip_first_line" is a boolean indicating if the first line should
1438 be skipped for calculating the indent width and for dedenting.
1439 This is sometimes useful for docstrings and similar.
1440
1441 textwrap.dedent(s), but don't expand tabs to spaces
1442 """
1443 lines = text.splitlines(1)
1444 _dedentlines(lines, tabsize=tabsize, skip_first_line=skip_first_line)
1445 return ''.join(lines)
1446
1447
1448def _get_indent(marker, s, tab_width=8):
1449 """_get_indent(marker, s, tab_width=8) ->
1450 (<indentation-of-'marker'>, <indentation-width>)"""
1451 # Figure out how much the marker is indented.
1452 INDENT_CHARS = tuple(' \t')
1453 start = s.index(marker)
1454 i = start
1455 while i > 0:
1456 if s[i-1] not in INDENT_CHARS:
1457 break
1458 i -= 1
1459 indent = s[i:start]
1460 indent_width = 0
1461 for ch in indent:
1462 if ch == ' ':
1463 indent_width += 1
1464 elif ch == '\t':
1465 indent_width += tab_width - (indent_width % tab_width)
1466 return indent, indent_width
1467
1468def _get_trailing_whitespace(marker, s):
1469 """Return the whitespace content trailing the given 'marker' in string 's',
1470 up to and including a newline.
1471 """
1472 suffix = ''
1473 start = s.index(marker) + len(marker)
1474 i = start
1475 while i < len(s):
1476 if s[i] in ' \t':
1477 suffix += s[i]
1478 elif s[i] in '\r\n':
1479 suffix += s[i]
1480 if s[i] == '\r' and i+1 < len(s) and s[i+1] == '\n':
1481 suffix += s[i+1]
1482 break
1483 else:
1484 break
1485 i += 1
1486 return suffix
1487
1488
1489
1490#---- bash completion support
1491# Note: This is still experimental. I expect to change this
1492# significantly.
1493#
1494# To get Bash completion for a cmdln.Cmdln class, run the following
1495# bash command:
1496# $ complete -C 'python -m cmdln /path/to/script.py CmdlnClass' cmdname
1497# For example:
1498# $ complete -C 'python -m cmdln ~/bin/svn.py SVN' svn
1499#
1500#TODO: Simplify the above so don't have to given path to script (try to
1501# find it on PATH, if possible). Could also make class name
1502# optional if there is only one in the module (common case).
1503
1504if __name__ == "__main__" and len(sys.argv) == 6:
1505 def _log(s):
1506 return # no-op, comment out for debugging
1507 from os.path import expanduser
1508 fout = open(expanduser("~/tmp/bashcpln.log"), 'a')
1509 fout.write(str(s) + '\n')
1510 fout.close()
1511
1512 # Recipe: module_from_path (1.0.1+)
1513 def _module_from_path(path):
1514 import imp, os, sys
1515 path = os.path.expanduser(path)
1516 dir = os.path.dirname(path) or os.curdir
1517 name = os.path.splitext(os.path.basename(path))[0]
1518 sys.path.insert(0, dir)
1519 try:
1520 iinfo = imp.find_module(name, [dir])
1521 return imp.load_module(name, *iinfo)
1522 finally:
1523 sys.path.remove(dir)
1524
1525 def _get_bash_cplns(script_path, class_name, cmd_name,
1526 token, preceding_token):
1527 _log('--')
1528 _log('get_cplns(%r, %r, %r, %r, %r)'
1529 % (script_path, class_name, cmd_name, token, preceding_token))
1530 comp_line = os.environ["COMP_LINE"]
1531 comp_point = int(os.environ["COMP_POINT"])
1532 _log("COMP_LINE: %r" % comp_line)
1533 _log("COMP_POINT: %r" % comp_point)
1534
1535 try:
1536 script = _module_from_path(script_path)
1537 except ImportError, ex:
1538 _log("error importing `%s': %s" % (script_path, ex))
1539 return []
1540 shell = getattr(script, class_name)()
1541 cmd_map = shell._get_canonical_map()
1542 del cmd_map["EOF"]
1543
1544 # Determine if completing the sub-command name.
1545 parts = comp_line[:comp_point].split(None, 1)
1546 _log(parts)
1547 if len(parts) == 1 or not (' ' in parts[1] or '\t' in parts[1]):
1548 #TODO: if parts[1].startswith('-'): handle top-level opts
1549 _log("complete sub-command names")
1550 matches = {}
1551 for name, canon_name in cmd_map.items():
1552 if name.startswith(token):
1553 matches[name] = canon_name
1554 if not matches:
1555 return []
1556 elif len(matches) == 1:
1557 return matches.keys()
1558 elif len(set(matches.values())) == 1:
1559 return [matches.values()[0]]
1560 else:
1561 return matches.keys()
1562
1563 # Otherwise, complete options for the given sub-command.
1564 #TODO: refine this so it does the right thing with option args
1565 if token.startswith('-'):
1566 cmd_name = comp_line.split(None, 2)[1]
1567 try:
1568 cmd_canon_name = cmd_map[cmd_name]
1569 except KeyError:
1570 return []
1571 handler = shell._get_cmd_handler(cmd_canon_name)
1572 optparser = getattr(handler, "optparser", None)
1573 if optparser is None:
1574 optparser = SubCmdOptionParser()
1575 opt_strs = []
1576 for option in optparser.option_list:
1577 for opt_str in option._short_opts + option._long_opts:
1578 if opt_str.startswith(token):
1579 opt_strs.append(opt_str)
1580 return opt_strs
1581
1582 return []
1583
1584 for cpln in _get_bash_cplns(*sys.argv[1:]):
1585 print cpln
1586
diff --git a/scripts/lib/mic/utils/errors.py b/scripts/lib/mic/utils/errors.py
new file mode 100644
index 0000000000..8d720f9080
--- /dev/null
+++ b/scripts/lib/mic/utils/errors.py
@@ -0,0 +1,71 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2007 Red Hat, Inc.
4# Copyright (c) 2011 Intel, Inc.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the Free
8# Software Foundation; version 2 of the License
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13# for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc., 59
17# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19class CreatorError(Exception):
20 """An exception base class for all imgcreate errors."""
21 keyword = '<creator>'
22
23 def __init__(self, msg):
24 self.msg = msg
25
26 def __str__(self):
27 if isinstance(self.msg, unicode):
28 self.msg = self.msg.encode('utf-8', 'ignore')
29 else:
30 self.msg = str(self.msg)
31 return self.keyword + self.msg
32
33class Usage(CreatorError):
34 keyword = '<usage>'
35
36 def __str__(self):
37 if isinstance(self.msg, unicode):
38 self.msg = self.msg.encode('utf-8', 'ignore')
39 else:
40 self.msg = str(self.msg)
41 return self.keyword + self.msg + ', please use "--help" for more info'
42
43class Abort(CreatorError):
44 keyword = ''
45
46class ConfigError(CreatorError):
47 keyword = '<config>'
48
49class KsError(CreatorError):
50 keyword = '<kickstart>'
51
52class RepoError(CreatorError):
53 keyword = '<repo>'
54
55class RpmError(CreatorError):
56 keyword = '<rpm>'
57
58class MountError(CreatorError):
59 keyword = '<mount>'
60
61class SnapshotError(CreatorError):
62 keyword = '<snapshot>'
63
64class SquashfsError(CreatorError):
65 keyword = '<squashfs>'
66
67class BootstrapError(CreatorError):
68 keyword = '<bootstrap>'
69
70class RuntimeError(CreatorError):
71 keyword = '<runtime>'
diff --git a/scripts/lib/mic/utils/fs_related.py b/scripts/lib/mic/utils/fs_related.py
new file mode 100644
index 0000000000..b9b9a97175
--- /dev/null
+++ b/scripts/lib/mic/utils/fs_related.py
@@ -0,0 +1,1029 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2007, Red Hat, Inc.
4# Copyright (c) 2009, 2010, 2011 Intel, Inc.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the Free
8# Software Foundation; version 2 of the License
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13# for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc., 59
17# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19from __future__ import with_statement
20import os
21import sys
22import errno
23import stat
24import random
25import string
26import time
27import uuid
28
29from mic import msger
30from mic.utils import runner
31from mic.utils.errors import *
32
33
34def find_binary_inchroot(binary, chroot):
35 paths = ["/usr/sbin",
36 "/usr/bin",
37 "/sbin",
38 "/bin"
39 ]
40
41 for path in paths:
42 bin_path = "%s/%s" % (path, binary)
43 if os.path.exists("%s/%s" % (chroot, bin_path)):
44 return bin_path
45 return None
46
47def find_binary_path(binary):
48 if os.environ.has_key("PATH"):
49 paths = os.environ["PATH"].split(":")
50 else:
51 paths = []
52 if os.environ.has_key("HOME"):
53 paths += [os.environ["HOME"] + "/bin"]
54 paths += ["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"]
55
56 for path in paths:
57 bin_path = "%s/%s" % (path, binary)
58 if os.path.exists(bin_path):
59 return bin_path
60 raise CreatorError("Command '%s' is not available." % binary)
61
62def makedirs(dirname):
63 """A version of os.makedirs() that doesn't throw an
64 exception if the leaf directory already exists.
65 """
66 try:
67 os.makedirs(dirname)
68 except OSError, err:
69 if err.errno != errno.EEXIST:
70 raise
71
72def mksquashfs(in_img, out_img):
73 fullpathmksquashfs = find_binary_path("mksquashfs")
74 args = [fullpathmksquashfs, in_img, out_img]
75
76 if not sys.stdout.isatty():
77 args.append("-no-progress")
78
79 ret = runner.show(args)
80 if ret != 0:
81 raise SquashfsError("'%s' exited with error (%d)" % (' '.join(args), ret))
82
83def resize2fs(fs, size):
84 resize2fs = find_binary_path("resize2fs")
85 if size == 0:
86 # it means to minimalize it
87 return runner.show([resize2fs, '-M', fs])
88 else:
89 return runner.show([resize2fs, fs, "%sK" % (size / 1024,)])
90
91def my_fuser(fp):
92 fuser = find_binary_path("fuser")
93 if not os.path.exists(fp):
94 return False
95
96 rc = runner.quiet([fuser, "-s", fp])
97 if rc == 0:
98 for pid in runner.outs([fuser, fp]).split():
99 fd = open("/proc/%s/cmdline" % pid, "r")
100 cmdline = fd.read()
101 fd.close()
102 if cmdline[:-1] == "/bin/bash":
103 return True
104
105 # not found
106 return False
107
108class BindChrootMount:
109 """Represents a bind mount of a directory into a chroot."""
110 def __init__(self, src, chroot, dest = None, option = None):
111 self.root = os.path.abspath(os.path.expanduser(chroot))
112 self.option = option
113
114 self.orig_src = self.src = src
115 if os.path.islink(src):
116 self.src = os.readlink(src)
117 if not self.src.startswith('/'):
118 self.src = os.path.abspath(os.path.join(os.path.dirname(src),
119 self.src))
120
121 if not dest:
122 dest = self.src
123 self.dest = os.path.join(self.root, dest.lstrip('/'))
124
125 self.mounted = False
126 self.mountcmd = find_binary_path("mount")
127 self.umountcmd = find_binary_path("umount")
128
129 def ismounted(self):
130 with open('/proc/mounts') as f:
131 for line in f:
132 if line.split()[1] == os.path.abspath(self.dest):
133 return True
134
135 return False
136
137 def has_chroot_instance(self):
138 lock = os.path.join(self.root, ".chroot.lock")
139 return my_fuser(lock)
140
141 def mount(self):
142 if self.mounted or self.ismounted():
143 return
144
145 makedirs(self.dest)
146 rc = runner.show([self.mountcmd, "--bind", self.src, self.dest])
147 if rc != 0:
148 raise MountError("Bind-mounting '%s' to '%s' failed" %
149 (self.src, self.dest))
150 if self.option:
151 rc = runner.show([self.mountcmd, "--bind", "-o", "remount,%s" % self.option, self.dest])
152 if rc != 0:
153 raise MountError("Bind-remounting '%s' failed" % self.dest)
154
155 self.mounted = True
156 if os.path.islink(self.orig_src):
157 dest = os.path.join(self.root, self.orig_src.lstrip('/'))
158 if not os.path.exists(dest):
159 os.symlink(self.src, dest)
160
161 def unmount(self):
162 if self.has_chroot_instance():
163 return
164
165 if self.ismounted():
166 runner.show([self.umountcmd, "-l", self.dest])
167 self.mounted = False
168
169class LoopbackMount:
170 """LoopbackMount compatibility layer for old API"""
171 def __init__(self, lofile, mountdir, fstype = None):
172 self.diskmount = DiskMount(LoopbackDisk(lofile,size = 0),mountdir,fstype,rmmountdir = True)
173 self.losetup = False
174 self.losetupcmd = find_binary_path("losetup")
175
176 def cleanup(self):
177 self.diskmount.cleanup()
178
179 def unmount(self):
180 self.diskmount.unmount()
181
182 def lounsetup(self):
183 if self.losetup:
184 runner.show([self.losetupcmd, "-d", self.loopdev])
185 self.losetup = False
186 self.loopdev = None
187
188 def loopsetup(self):
189 if self.losetup:
190 return
191
192 self.loopdev = get_loop_device(self.losetupcmd, self.lofile)
193 self.losetup = True
194
195 def mount(self):
196 self.diskmount.mount()
197
198class SparseLoopbackMount(LoopbackMount):
199 """SparseLoopbackMount compatibility layer for old API"""
200 def __init__(self, lofile, mountdir, size, fstype = None):
201 self.diskmount = DiskMount(SparseLoopbackDisk(lofile,size),mountdir,fstype,rmmountdir = True)
202
203 def expand(self, create = False, size = None):
204 self.diskmount.disk.expand(create, size)
205
206 def truncate(self, size = None):
207 self.diskmount.disk.truncate(size)
208
209 def create(self):
210 self.diskmount.disk.create()
211
212class SparseExtLoopbackMount(SparseLoopbackMount):
213 """SparseExtLoopbackMount compatibility layer for old API"""
214 def __init__(self, lofile, mountdir, size, fstype, blocksize, fslabel):
215 self.diskmount = ExtDiskMount(SparseLoopbackDisk(lofile,size), mountdir, fstype, blocksize, fslabel, rmmountdir = True)
216
217
218 def __format_filesystem(self):
219 self.diskmount.__format_filesystem()
220
221 def create(self):
222 self.diskmount.disk.create()
223
224 def resize(self, size = None):
225 return self.diskmount.__resize_filesystem(size)
226
227 def mount(self):
228 self.diskmount.mount()
229
230 def __fsck(self):
231 self.extdiskmount.__fsck()
232
233 def __get_size_from_filesystem(self):
234 return self.diskmount.__get_size_from_filesystem()
235
236 def __resize_to_minimal(self):
237 return self.diskmount.__resize_to_minimal()
238
239 def resparse(self, size = None):
240 return self.diskmount.resparse(size)
241
242class Disk:
243 """Generic base object for a disk
244
245 The 'create' method must make the disk visible as a block device - eg
246 by calling losetup. For RawDisk, this is obviously a no-op. The 'cleanup'
247 method must undo the 'create' operation.
248 """
249 def __init__(self, size, device = None):
250 self._device = device
251 self._size = size
252
253 def create(self):
254 pass
255
256 def cleanup(self):
257 pass
258
259 def get_device(self):
260 return self._device
261 def set_device(self, path):
262 self._device = path
263 device = property(get_device, set_device)
264
265 def get_size(self):
266 return self._size
267 size = property(get_size)
268
269
270class RawDisk(Disk):
271 """A Disk backed by a block device.
272 Note that create() is a no-op.
273 """
274 def __init__(self, size, device):
275 Disk.__init__(self, size, device)
276
277 def fixed(self):
278 return True
279
280 def exists(self):
281 return True
282
283class LoopbackDisk(Disk):
284 """A Disk backed by a file via the loop module."""
285 def __init__(self, lofile, size):
286 Disk.__init__(self, size)
287 self.lofile = lofile
288 self.losetupcmd = find_binary_path("losetup")
289
290 def fixed(self):
291 return False
292
293 def exists(self):
294 return os.path.exists(self.lofile)
295
296 def create(self):
297 if self.device is not None:
298 return
299
300 self.device = get_loop_device(self.losetupcmd, self.lofile)
301
302 def cleanup(self):
303 if self.device is None:
304 return
305 msger.debug("Losetup remove %s" % self.device)
306 rc = runner.show([self.losetupcmd, "-d", self.device])
307 self.device = None
308
309class SparseLoopbackDisk(LoopbackDisk):
310 """A Disk backed by a sparse file via the loop module."""
311 def __init__(self, lofile, size):
312 LoopbackDisk.__init__(self, lofile, size)
313
314 def expand(self, create = False, size = None):
315 flags = os.O_WRONLY
316 if create:
317 flags |= os.O_CREAT
318 if not os.path.exists(self.lofile):
319 makedirs(os.path.dirname(self.lofile))
320
321 if size is None:
322 size = self.size
323
324 msger.debug("Extending sparse file %s to %d" % (self.lofile, size))
325 if create:
326 fd = os.open(self.lofile, flags, 0644)
327 else:
328 fd = os.open(self.lofile, flags)
329
330 if size <= 0:
331 size = 1
332 try:
333 os.ftruncate(fd, size)
334 except:
335 # may be limited by 2G in 32bit env
336 os.ftruncate(fd, 2**31L)
337
338 os.close(fd)
339
340 def truncate(self, size = None):
341 if size is None:
342 size = self.size
343
344 msger.debug("Truncating sparse file %s to %d" % (self.lofile, size))
345 fd = os.open(self.lofile, os.O_WRONLY)
346 os.ftruncate(fd, size)
347 os.close(fd)
348
349 def create(self):
350 self.expand(create = True)
351 LoopbackDisk.create(self)
352
353class Mount:
354 """A generic base class to deal with mounting things."""
355 def __init__(self, mountdir):
356 self.mountdir = mountdir
357
358 def cleanup(self):
359 self.unmount()
360
361 def mount(self, options = None):
362 pass
363
364 def unmount(self):
365 pass
366
367class DiskMount(Mount):
368 """A Mount object that handles mounting of a Disk."""
369 def __init__(self, disk, mountdir, fstype = None, rmmountdir = True):
370 Mount.__init__(self, mountdir)
371
372 self.disk = disk
373 self.fstype = fstype
374 self.rmmountdir = rmmountdir
375
376 self.mounted = False
377 self.rmdir = False
378 if fstype:
379 self.mkfscmd = find_binary_path("mkfs." + self.fstype)
380 else:
381 self.mkfscmd = None
382 self.mountcmd = find_binary_path("mount")
383 self.umountcmd = find_binary_path("umount")
384
385 def cleanup(self):
386 Mount.cleanup(self)
387 self.disk.cleanup()
388
389 def unmount(self):
390 if self.mounted:
391 msger.debug("Unmounting directory %s" % self.mountdir)
392 runner.quiet('sync') # sync the data on this mount point
393 rc = runner.show([self.umountcmd, "-l", self.mountdir])
394 if rc == 0:
395 self.mounted = False
396 else:
397 raise MountError("Failed to umount %s" % self.mountdir)
398 if self.rmdir and not self.mounted:
399 try:
400 os.rmdir(self.mountdir)
401 except OSError, e:
402 pass
403 self.rmdir = False
404
405
406 def __create(self):
407 self.disk.create()
408
409
410 def mount(self, options = None):
411 if self.mounted:
412 return
413
414 if not os.path.isdir(self.mountdir):
415 msger.debug("Creating mount point %s" % self.mountdir)
416 os.makedirs(self.mountdir)
417 self.rmdir = self.rmmountdir
418
419 self.__create()
420
421 msger.debug("Mounting %s at %s" % (self.disk.device, self.mountdir))
422 if options:
423 args = [ self.mountcmd, "-o", options, self.disk.device, self.mountdir ]
424 else:
425 args = [ self.mountcmd, self.disk.device, self.mountdir ]
426 if self.fstype:
427 args.extend(["-t", self.fstype])
428
429 rc = runner.show(args)
430 if rc != 0:
431 raise MountError("Failed to mount '%s' to '%s' with command '%s'. Retval: %s" %
432 (self.disk.device, self.mountdir, " ".join(args), rc))
433
434 self.mounted = True
435
436class ExtDiskMount(DiskMount):
437 """A DiskMount object that is able to format/resize ext[23] filesystems."""
438 def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
439 DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
440 self.blocksize = blocksize
441 self.fslabel = fslabel.replace("/", "")
442 self.uuid = str(uuid.uuid4())
443 self.skipformat = skipformat
444 self.fsopts = fsopts
445 self.extopts = None
446 self.dumpe2fs = find_binary_path("dumpe2fs")
447 self.tune2fs = find_binary_path("tune2fs")
448
449 def __parse_field(self, output, field):
450 for line in output.split("\n"):
451 if line.startswith(field + ":"):
452 return line[len(field) + 1:].strip()
453
454 raise KeyError("Failed to find field '%s' in output" % field)
455
456 def __format_filesystem(self):
457 if self.skipformat:
458 msger.debug("Skip filesystem format.")
459 return
460
461 msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
462 cmdlist = [self.mkfscmd, "-F", "-L", self.fslabel, "-m", "1", "-b",
463 str(self.blocksize), "-U", self.uuid]
464 if self.extopts:
465 cmdlist.extend(self.extopts.split())
466 cmdlist.extend([self.disk.device])
467
468 rc, errout = runner.runtool(cmdlist, catch=2)
469 if rc != 0:
470 raise MountError("Error creating %s filesystem on disk %s:\n%s" %
471 (self.fstype, self.disk.device, errout))
472
473 if not self.extopts:
474 msger.debug("Tuning filesystem on %s" % self.disk.device)
475 runner.show([self.tune2fs, "-c0", "-i0", "-Odir_index", "-ouser_xattr,acl", self.disk.device])
476
477 def __resize_filesystem(self, size = None):
478 current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
479
480 if size is None:
481 size = self.disk.size
482
483 if size == current_size:
484 return
485
486 if size > current_size:
487 self.disk.expand(size)
488
489 self.__fsck()
490
491 resize2fs(self.disk.lofile, size)
492 return size
493
494 def __create(self):
495 resize = False
496 if not self.disk.fixed() and self.disk.exists():
497 resize = True
498
499 self.disk.create()
500
501 if resize:
502 self.__resize_filesystem()
503 else:
504 self.__format_filesystem()
505
506 def mount(self, options = None):
507 self.__create()
508 DiskMount.mount(self, options)
509
510 def __fsck(self):
511 msger.info("Checking filesystem %s" % self.disk.lofile)
512 runner.quiet(["/sbin/e2fsck", "-f", "-y", self.disk.lofile])
513
514 def __get_size_from_filesystem(self):
515 return int(self.__parse_field(runner.outs([self.dumpe2fs, '-h', self.disk.lofile]),
516 "Block count")) * self.blocksize
517
518 def __resize_to_minimal(self):
519 self.__fsck()
520
521 #
522 # Use a binary search to find the minimal size
523 # we can resize the image to
524 #
525 bot = 0
526 top = self.__get_size_from_filesystem()
527 while top != (bot + 1):
528 t = bot + ((top - bot) / 2)
529
530 if not resize2fs(self.disk.lofile, t):
531 top = t
532 else:
533 bot = t
534 return top
535
536 def resparse(self, size = None):
537 self.cleanup()
538 if size == 0:
539 minsize = 0
540 else:
541 minsize = self.__resize_to_minimal()
542 self.disk.truncate(minsize)
543
544 self.__resize_filesystem(size)
545 return minsize
546
547class VfatDiskMount(DiskMount):
548 """A DiskMount object that is able to format vfat/msdos filesystems."""
549 def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
550 DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
551 self.blocksize = blocksize
552 self.fslabel = fslabel.replace("/", "")
553 rand1 = random.randint(0, 2**16 - 1)
554 rand2 = random.randint(0, 2**16 - 1)
555 self.uuid = "%04X-%04X" % (rand1, rand2)
556 self.skipformat = skipformat
557 self.fsopts = fsopts
558 self.fsckcmd = find_binary_path("fsck." + self.fstype)
559
560 def __format_filesystem(self):
561 if self.skipformat:
562 msger.debug("Skip filesystem format.")
563 return
564
565 msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
566 rc = runner.show([self.mkfscmd, "-n", self.fslabel,
567 "-i", self.uuid.replace("-", ""), self.disk.device])
568 if rc != 0:
569 raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device))
570
571 msger.verbose("Tuning filesystem on %s" % self.disk.device)
572
573 def __resize_filesystem(self, size = None):
574 current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
575
576 if size is None:
577 size = self.disk.size
578
579 if size == current_size:
580 return
581
582 if size > current_size:
583 self.disk.expand(size)
584
585 self.__fsck()
586
587 #resize2fs(self.disk.lofile, size)
588 return size
589
590 def __create(self):
591 resize = False
592 if not self.disk.fixed() and self.disk.exists():
593 resize = True
594
595 self.disk.create()
596
597 if resize:
598 self.__resize_filesystem()
599 else:
600 self.__format_filesystem()
601
602 def mount(self, options = None):
603 self.__create()
604 DiskMount.mount(self, options)
605
606 def __fsck(self):
607 msger.debug("Checking filesystem %s" % self.disk.lofile)
608 runner.show([self.fsckcmd, "-y", self.disk.lofile])
609
610 def __get_size_from_filesystem(self):
611 return self.disk.size
612
613 def __resize_to_minimal(self):
614 self.__fsck()
615
616 #
617 # Use a binary search to find the minimal size
618 # we can resize the image to
619 #
620 bot = 0
621 top = self.__get_size_from_filesystem()
622 return top
623
624 def resparse(self, size = None):
625 self.cleanup()
626 minsize = self.__resize_to_minimal()
627 self.disk.truncate(minsize)
628 self.__resize_filesystem(size)
629 return minsize
630
631class BtrfsDiskMount(DiskMount):
632 """A DiskMount object that is able to format/resize btrfs filesystems."""
633 def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None):
634 self.__check_btrfs()
635 DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
636 self.blocksize = blocksize
637 self.fslabel = fslabel.replace("/", "")
638 self.uuid = None
639 self.skipformat = skipformat
640 self.fsopts = fsopts
641 self.blkidcmd = find_binary_path("blkid")
642 self.btrfsckcmd = find_binary_path("btrfsck")
643
644 def __check_btrfs(self):
645 found = False
646 """ Need to load btrfs module to mount it """
647 load_module("btrfs")
648 for line in open("/proc/filesystems").xreadlines():
649 if line.find("btrfs") > -1:
650 found = True
651 break
652 if not found:
653 raise MountError("Your system can't mount btrfs filesystem, please make sure your kernel has btrfs support and the module btrfs.ko has been loaded.")
654
655 # disable selinux, selinux will block write
656 if os.path.exists("/usr/sbin/setenforce"):
657 runner.show(["/usr/sbin/setenforce", "0"])
658
659 def __parse_field(self, output, field):
660 for line in output.split(" "):
661 if line.startswith(field + "="):
662 return line[len(field) + 1:].strip().replace("\"", "")
663
664 raise KeyError("Failed to find field '%s' in output" % field)
665
666 def __format_filesystem(self):
667 if self.skipformat:
668 msger.debug("Skip filesystem format.")
669 return
670
671 msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device))
672 rc = runner.show([self.mkfscmd, "-L", self.fslabel, self.disk.device])
673 if rc != 0:
674 raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device))
675
676 self.uuid = self.__parse_field(runner.outs([self.blkidcmd, self.disk.device]), "UUID")
677
678 def __resize_filesystem(self, size = None):
679 current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
680
681 if size is None:
682 size = self.disk.size
683
684 if size == current_size:
685 return
686
687 if size > current_size:
688 self.disk.expand(size)
689
690 self.__fsck()
691 return size
692
693 def __create(self):
694 resize = False
695 if not self.disk.fixed() and self.disk.exists():
696 resize = True
697
698 self.disk.create()
699
700 if resize:
701 self.__resize_filesystem()
702 else:
703 self.__format_filesystem()
704
705 def mount(self, options = None):
706 self.__create()
707 DiskMount.mount(self, options)
708
709 def __fsck(self):
710 msger.debug("Checking filesystem %s" % self.disk.lofile)
711 runner.quiet([self.btrfsckcmd, self.disk.lofile])
712
713 def __get_size_from_filesystem(self):
714 return self.disk.size
715
716 def __resize_to_minimal(self):
717 self.__fsck()
718
719 return self.__get_size_from_filesystem()
720
721 def resparse(self, size = None):
722 self.cleanup()
723 minsize = self.__resize_to_minimal()
724 self.disk.truncate(minsize)
725 self.__resize_filesystem(size)
726 return minsize
727
728class DeviceMapperSnapshot(object):
729 def __init__(self, imgloop, cowloop):
730 self.imgloop = imgloop
731 self.cowloop = cowloop
732
733 self.__created = False
734 self.__name = None
735 self.dmsetupcmd = find_binary_path("dmsetup")
736
737 """Load dm_snapshot if it isn't loaded"""
738 load_module("dm_snapshot")
739
740 def get_path(self):
741 if self.__name is None:
742 return None
743 return os.path.join("/dev/mapper", self.__name)
744 path = property(get_path)
745
746 def create(self):
747 if self.__created:
748 return
749
750 self.imgloop.create()
751 self.cowloop.create()
752
753 self.__name = "imgcreate-%d-%d" % (os.getpid(),
754 random.randint(0, 2**16))
755
756 size = os.stat(self.imgloop.lofile)[stat.ST_SIZE]
757
758 table = "0 %d snapshot %s %s p 8" % (size / 512,
759 self.imgloop.device,
760 self.cowloop.device)
761
762 args = [self.dmsetupcmd, "create", self.__name, "--table", table]
763 if runner.show(args) != 0:
764 self.cowloop.cleanup()
765 self.imgloop.cleanup()
766 raise SnapshotError("Could not create snapshot device using: " + ' '.join(args))
767
768 self.__created = True
769
770 def remove(self, ignore_errors = False):
771 if not self.__created:
772 return
773
774 time.sleep(2)
775 rc = runner.show([self.dmsetupcmd, "remove", self.__name])
776 if not ignore_errors and rc != 0:
777 raise SnapshotError("Could not remove snapshot device")
778
779 self.__name = None
780 self.__created = False
781
782 self.cowloop.cleanup()
783 self.imgloop.cleanup()
784
785 def get_cow_used(self):
786 if not self.__created:
787 return 0
788
789 #
790 # dmsetup status on a snapshot returns e.g.
791 # "0 8388608 snapshot 416/1048576"
792 # or, more generally:
793 # "A B snapshot C/D"
794 # where C is the number of 512 byte sectors in use
795 #
796 out = runner.outs([self.dmsetupcmd, "status", self.__name])
797 try:
798 return int((out.split()[3]).split('/')[0]) * 512
799 except ValueError:
800 raise SnapshotError("Failed to parse dmsetup status: " + out)
801
802def create_image_minimizer(path, image, minimal_size):
803 """
804 Builds a copy-on-write image which can be used to
805 create a device-mapper snapshot of an image where
806 the image's filesystem is as small as possible
807
808 The steps taken are:
809 1) Create a sparse COW
810 2) Loopback mount the image and the COW
811 3) Create a device-mapper snapshot of the image
812 using the COW
813 4) Resize the filesystem to the minimal size
814 5) Determine the amount of space used in the COW
815 6) Restroy the device-mapper snapshot
816 7) Truncate the COW, removing unused space
817 8) Create a squashfs of the COW
818 """
819 imgloop = LoopbackDisk(image, None) # Passing bogus size - doesn't matter
820
821 cowloop = SparseLoopbackDisk(os.path.join(os.path.dirname(path), "osmin"),
822 64L * 1024L * 1024L)
823
824 snapshot = DeviceMapperSnapshot(imgloop, cowloop)
825
826 try:
827 snapshot.create()
828
829 resize2fs(snapshot.path, minimal_size)
830
831 cow_used = snapshot.get_cow_used()
832 finally:
833 snapshot.remove(ignore_errors = (not sys.exc_info()[0] is None))
834
835 cowloop.truncate(cow_used)
836
837 mksquashfs(cowloop.lofile, path)
838
839 os.unlink(cowloop.lofile)
840
841def load_module(module):
842 found = False
843 for line in open('/proc/modules').xreadlines():
844 if line.startswith("%s " % module):
845 found = True
846 break
847 if not found:
848 msger.info("Loading %s..." % module)
849 runner.quiet(['modprobe', module])
850
851class LoopDevice(object):
852 def __init__(self, loopid=None):
853 self.device = None
854 self.loopid = loopid
855 self.created = False
856 self.kpartxcmd = find_binary_path("kpartx")
857 self.losetupcmd = find_binary_path("losetup")
858
859 def register(self, device):
860 self.device = device
861 self.loopid = None
862 self.created = True
863
864 def reg_atexit(self):
865 import atexit
866 atexit.register(self.close)
867
868 def _genloopid(self):
869 import glob
870 if not glob.glob("/dev/loop[0-9]*"):
871 return 10
872
873 fint = lambda x: x[9:].isdigit() and int(x[9:]) or 0
874 maxid = 1 + max(filter(lambda x: x<100,
875 map(fint, glob.glob("/dev/loop[0-9]*"))))
876 if maxid < 10: maxid = 10
877 if maxid >= 100: raise
878 return maxid
879
880 def _kpseek(self, device):
881 rc, out = runner.runtool([self.kpartxcmd, '-l', '-v', device])
882 if rc != 0:
883 raise MountError("Can't query dm snapshot on %s" % device)
884 for line in out.splitlines():
885 if line and line.startswith("loop"):
886 return True
887 return False
888
889 def _loseek(self, device):
890 import re
891 rc, out = runner.runtool([self.losetupcmd, '-a'])
892 if rc != 0:
893 raise MountError("Failed to run 'losetup -a'")
894 for line in out.splitlines():
895 m = re.match("([^:]+): .*", line)
896 if m and m.group(1) == device:
897 return True
898 return False
899
900 def create(self):
901 if not self.created:
902 if not self.loopid:
903 self.loopid = self._genloopid()
904 self.device = "/dev/loop%d" % self.loopid
905 if os.path.exists(self.device):
906 if self._loseek(self.device):
907 raise MountError("Device busy: %s" % self.device)
908 else:
909 self.created = True
910 return
911
912 mknod = find_binary_path('mknod')
913 rc = runner.show([mknod, '-m664', self.device, 'b', '7', str(self.loopid)])
914 if rc != 0:
915 raise MountError("Failed to create device %s" % self.device)
916 else:
917 self.created = True
918
919 def close(self):
920 if self.created:
921 try:
922 self.cleanup()
923 self.device = None
924 except MountError, e:
925 msger.error("%s" % e)
926
927 def cleanup(self):
928
929 if self.device is None:
930 return
931
932
933 if self._kpseek(self.device):
934 if self.created:
935 for i in range(3, os.sysconf("SC_OPEN_MAX")):
936 try:
937 os.close(i)
938 except:
939 pass
940 runner.quiet([self.kpartxcmd, "-d", self.device])
941 if self._loseek(self.device):
942 runner.quiet([self.losetupcmd, "-d", self.device])
943 # FIXME: should sleep a while between two loseek
944 if self._loseek(self.device):
945 msger.warning("Can't cleanup loop device %s" % self.device)
946 elif self.loopid:
947 os.unlink(self.device)
948
949DEVICE_PIDFILE_DIR = "/var/tmp/mic/device"
950DEVICE_LOCKFILE = "/var/lock/__mic_loopdev.lock"
951
952def get_loop_device(losetupcmd, lofile):
953 global DEVICE_PIDFILE_DIR
954 global DEVICE_LOCKFILE
955
956 import fcntl
957 makedirs(os.path.dirname(DEVICE_LOCKFILE))
958 fp = open(DEVICE_LOCKFILE, 'w')
959 fcntl.flock(fp, fcntl.LOCK_EX)
960 try:
961 loopdev = None
962 devinst = LoopDevice()
963
964 # clean up left loop device first
965 clean_loop_devices()
966
967 # provide an avaible loop device
968 rc, out = runner.runtool([losetupcmd, "--find"])
969 if rc == 0:
970 loopdev = out.split()[0]
971 devinst.register(loopdev)
972 if not loopdev or not os.path.exists(loopdev):
973 devinst.create()
974 loopdev = devinst.device
975
976 # setup a loop device for image file
977 rc = runner.show([losetupcmd, loopdev, lofile])
978 if rc != 0:
979 raise MountError("Failed to setup loop device for '%s'" % lofile)
980
981 devinst.reg_atexit()
982
983 # try to save device and pid
984 makedirs(DEVICE_PIDFILE_DIR)
985 pidfile = os.path.join(DEVICE_PIDFILE_DIR, os.path.basename(loopdev))
986 if os.path.exists(pidfile):
987 os.unlink(pidfile)
988 with open(pidfile, 'w') as wf:
989 wf.write(str(os.getpid()))
990
991 except MountError, err:
992 raise CreatorError("%s" % str(err))
993 except:
994 raise
995 finally:
996 try:
997 fcntl.flock(fp, fcntl.LOCK_UN)
998 fp.close()
999 os.unlink(DEVICE_LOCKFILE)
1000 except:
1001 pass
1002
1003 return loopdev
1004
1005def clean_loop_devices(piddir=DEVICE_PIDFILE_DIR):
1006 if not os.path.exists(piddir) or not os.path.isdir(piddir):
1007 return
1008
1009 for loopdev in os.listdir(piddir):
1010 pidfile = os.path.join(piddir, loopdev)
1011 try:
1012 with open(pidfile, 'r') as rf:
1013 devpid = int(rf.read())
1014 except:
1015 devpid = None
1016
1017 # if the process using this device is alive, skip it
1018 if not devpid or os.path.exists(os.path.join('/proc', str(devpid))):
1019 continue
1020
1021 # try to clean it up
1022 try:
1023 devinst = LoopDevice()
1024 devinst.register(os.path.join('/dev', loopdev))
1025 devinst.cleanup()
1026 os.unlink(pidfile)
1027 except:
1028 pass
1029
diff --git a/scripts/lib/mic/utils/gpt_parser.py b/scripts/lib/mic/utils/gpt_parser.py
new file mode 100644
index 0000000000..5d43b70778
--- /dev/null
+++ b/scripts/lib/mic/utils/gpt_parser.py
@@ -0,0 +1,331 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2013 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18""" This module implements a simple GPT partitions parser which can read the
19GPT header and the GPT partition table. """
20
21import struct
22import uuid
23import binascii
24from mic.utils.errors import MountError
25
26_GPT_HEADER_FORMAT = "<8s4sIIIQQQQ16sQIII"
27_GPT_HEADER_SIZE = struct.calcsize(_GPT_HEADER_FORMAT)
28_GPT_ENTRY_FORMAT = "<16s16sQQQ72s"
29_GPT_ENTRY_SIZE = struct.calcsize(_GPT_ENTRY_FORMAT)
30_SUPPORTED_GPT_REVISION = '\x00\x00\x01\x00'
31
32def _stringify_uuid(binary_uuid):
33 """ A small helper function to transform a binary UUID into a string
34 format. """
35
36 uuid_str = str(uuid.UUID(bytes_le = binary_uuid))
37
38 return uuid_str.upper()
39
40def _calc_header_crc(raw_hdr):
41 """ Calculate GPT header CRC32 checksum. The 'raw_hdr' parameter has to
42 be a list or a tuple containing all the elements of the GPT header in a
43 "raw" form, meaning that it should simply contain "unpacked" disk data.
44 """
45
46 raw_hdr = list(raw_hdr)
47 raw_hdr[3] = 0
48 raw_hdr = struct.pack(_GPT_HEADER_FORMAT, *raw_hdr)
49
50 return binascii.crc32(raw_hdr) & 0xFFFFFFFF
51
52def _validate_header(raw_hdr):
53 """ Validate the GPT header. The 'raw_hdr' parameter has to be a list or a
54 tuple containing all the elements of the GPT header in a "raw" form,
55 meaning that it should simply contain "unpacked" disk data. """
56
57 # Validate the signature
58 if raw_hdr[0] != 'EFI PART':
59 raise MountError("GPT partition table not found")
60
61 # Validate the revision
62 if raw_hdr[1] != _SUPPORTED_GPT_REVISION:
63 raise MountError("Unsupported GPT revision '%s', supported revision " \
64 "is '%s'" % \
65 (binascii.hexlify(raw_hdr[1]),
66 binascii.hexlify(_SUPPORTED_GPT_REVISION)))
67
68 # Validate header size
69 if raw_hdr[2] != _GPT_HEADER_SIZE:
70 raise MountError("Bad GPT header size: %d bytes, expected %d" % \
71 (raw_hdr[2], _GPT_HEADER_SIZE))
72
73 crc = _calc_header_crc(raw_hdr)
74 if raw_hdr[3] != crc:
75 raise MountError("GPT header crc mismatch: %#x, should be %#x" % \
76 (crc, raw_hdr[3]))
77
78class GptParser:
79 """ GPT partition table parser. Allows reading the GPT header and the
80 partition table, as well as modifying the partition table records. """
81
82 def __init__(self, disk_path, sector_size = 512):
83 """ The class constructor which accepts the following parameters:
84 * disk_path - full path to the disk image or device node
85 * sector_size - size of a disk sector in bytes """
86
87 self.sector_size = sector_size
88 self.disk_path = disk_path
89
90 try:
91 self._disk_obj = open(disk_path, 'r+b')
92 except IOError as err:
93 raise MountError("Cannot open file '%s' for reading GPT " \
94 "partitions: %s" % (disk_path, err))
95
96 def __del__(self):
97 """ The class destructor. """
98
99 self._disk_obj.close()
100
101 def _read_disk(self, offset, size):
102 """ A helper function which reads 'size' bytes from offset 'offset' of
103 the disk and checks all the error conditions. """
104
105 self._disk_obj.seek(offset)
106 try:
107 data = self._disk_obj.read(size)
108 except IOError as err:
109 raise MountError("cannot read from '%s': %s" % \
110 (self.disk_path, err))
111
112 if len(data) != size:
113 raise MountError("cannot read %d bytes from offset '%d' of '%s', " \
114 "read only %d bytes" % \
115 (size, offset, self.disk_path, len(data)))
116
117 return data
118
119 def _write_disk(self, offset, buf):
120 """ A helper function which writes buffer 'buf' to offset 'offset' of
121 the disk. This function takes care of unaligned writes and checks all
122 the error conditions. """
123
124 # Since we may be dealing with a block device, we only can write in
125 # 'self.sector_size' chunks. Find the aligned starting and ending
126 # disk offsets to read.
127 start = (offset / self.sector_size) * self.sector_size
128 end = ((start + len(buf)) / self.sector_size + 1) * self.sector_size
129
130 data = self._read_disk(start, end - start)
131 off = offset - start
132 data = data[:off] + buf + data[off + len(buf):]
133
134 self._disk_obj.seek(start)
135 try:
136 self._disk_obj.write(data)
137 except IOError as err:
138 raise MountError("cannot write to '%s': %s" % (self.disk_path, err))
139
140 def read_header(self, primary = True):
141 """ Read and verify the GPT header and return a dictionary containing
142 the following elements:
143
144 'signature' : header signature
145 'revision' : header revision
146 'hdr_size' : header size in bytes
147 'hdr_crc' : header CRC32
148 'hdr_lba' : LBA of this header
149 'hdr_offs' : byte disk offset of this header
150 'backup_lba' : backup header LBA
151 'backup_offs' : byte disk offset of backup header
152 'first_lba' : first usable LBA for partitions
153 'first_offs' : first usable byte disk offset for partitions
154 'last_lba' : last usable LBA for partitions
155 'last_offs' : last usable byte disk offset for partitions
156 'disk_uuid' : UUID of the disk
157 'ptable_lba' : starting LBA of array of partition entries
158 'ptable_offs' : disk byte offset of the start of the partition table
159 'ptable_size' : partition table size in bytes
160 'entries_cnt' : number of available partition table entries
161 'entry_size' : size of a single partition entry
162 'ptable_crc' : CRC32 of the partition table
163 'primary' : a boolean, if 'True', this is the primary GPT header,
164 if 'False' - the secondary
165 'primary_str' : contains string "primary" if this is the primary GPT
166 header, and "backup" otherwise
167
168 This dictionary corresponds to the GPT header format. Please, see the
169 UEFI standard for the description of these fields.
170
171 If the 'primary' parameter is 'True', the primary GPT header is read,
172 otherwise the backup GPT header is read instead. """
173
174 # Read and validate the primary GPT header
175 raw_hdr = self._read_disk(self.sector_size, _GPT_HEADER_SIZE)
176 raw_hdr = struct.unpack(_GPT_HEADER_FORMAT, raw_hdr)
177 _validate_header(raw_hdr)
178 primary_str = "primary"
179
180 if not primary:
181 # Read and validate the backup GPT header
182 raw_hdr = self._read_disk(raw_hdr[6] * self.sector_size, _GPT_HEADER_SIZE)
183 raw_hdr = struct.unpack(_GPT_HEADER_FORMAT, raw_hdr)
184 _validate_header(raw_hdr)
185 primary_str = "backup"
186
187 return { 'signature' : raw_hdr[0],
188 'revision' : raw_hdr[1],
189 'hdr_size' : raw_hdr[2],
190 'hdr_crc' : raw_hdr[3],
191 'hdr_lba' : raw_hdr[5],
192 'hdr_offs' : raw_hdr[5] * self.sector_size,
193 'backup_lba' : raw_hdr[6],
194 'backup_offs' : raw_hdr[6] * self.sector_size,
195 'first_lba' : raw_hdr[7],
196 'first_offs' : raw_hdr[7] * self.sector_size,
197 'last_lba' : raw_hdr[8],
198 'last_offs' : raw_hdr[8] * self.sector_size,
199 'disk_uuid' :_stringify_uuid(raw_hdr[9]),
200 'ptable_lba' : raw_hdr[10],
201 'ptable_offs' : raw_hdr[10] * self.sector_size,
202 'ptable_size' : raw_hdr[11] * raw_hdr[12],
203 'entries_cnt' : raw_hdr[11],
204 'entry_size' : raw_hdr[12],
205 'ptable_crc' : raw_hdr[13],
206 'primary' : primary,
207 'primary_str' : primary_str }
208
209 def _read_raw_ptable(self, header):
210 """ Read and validate primary or backup partition table. The 'header'
211 argument is the GPT header. If it is the primary GPT header, then the
212 primary partition table is read and validated, otherwise - the backup
213 one. The 'header' argument is a dictionary which is returned by the
214 'read_header()' method. """
215
216 raw_ptable = self._read_disk(header['ptable_offs'],
217 header['ptable_size'])
218
219 crc = binascii.crc32(raw_ptable) & 0xFFFFFFFF
220 if crc != header['ptable_crc']:
221 raise MountError("Partition table at LBA %d (%s) is corrupted" % \
222 (header['ptable_lba'], header['primary_str']))
223
224 return raw_ptable
225
226 def get_partitions(self, primary = True):
227 """ This is a generator which parses the GPT partition table and
228 generates the following dictionary for each partition:
229
230 'index' : the index of the partition table endry
231 'offs' : byte disk offset of the partition table entry
232 'type_uuid' : partition type UUID
233 'part_uuid' : partition UUID
234 'first_lba' : the first LBA
235 'last_lba' : the last LBA
236 'flags' : attribute flags
237 'name' : partition name
238 'primary' : a boolean, if 'True', this is the primary partition
239 table, if 'False' - the secondary
240 'primary_str' : contains string "primary" if this is the primary GPT
241 header, and "backup" otherwise
242
243 This dictionary corresponds to the GPT header format. Please, see the
244 UEFI standard for the description of these fields.
245
246 If the 'primary' parameter is 'True', partitions from the primary GPT
247 partition table are generated, otherwise partitions from the backup GPT
248 partition table are generated. """
249
250 if primary:
251 primary_str = "primary"
252 else:
253 primary_str = "backup"
254
255 header = self.read_header(primary)
256 raw_ptable = self._read_raw_ptable(header)
257
258 for index in xrange(0, header['entries_cnt']):
259 start = header['entry_size'] * index
260 end = start + header['entry_size']
261 raw_entry = struct.unpack(_GPT_ENTRY_FORMAT, raw_ptable[start:end])
262
263 if raw_entry[2] == 0 or raw_entry[3] == 0:
264 continue
265
266 part_name = str(raw_entry[5].decode('UTF-16').split('\0', 1)[0])
267
268 yield { 'index' : index,
269 'offs' : header['ptable_offs'] + start,
270 'type_uuid' : _stringify_uuid(raw_entry[0]),
271 'part_uuid' : _stringify_uuid(raw_entry[1]),
272 'first_lba' : raw_entry[2],
273 'last_lba' : raw_entry[3],
274 'flags' : raw_entry[4],
275 'name' : part_name,
276 'primary' : primary,
277 'primary_str' : primary_str }
278
279 def _change_partition(self, header, entry):
280 """ A helper function for 'change_partitions()' which changes a
281 a paricular instance of the partition table (primary or backup). """
282
283 if entry['index'] >= header['entries_cnt']:
284 raise MountError("Partition table at LBA %d has only %d " \
285 "records cannot change record number %d" % \
286 (header['entries_cnt'], entry['index']))
287 # Read raw GPT header
288 raw_hdr = self._read_disk(header['hdr_offs'], _GPT_HEADER_SIZE)
289 raw_hdr = list(struct.unpack(_GPT_HEADER_FORMAT, raw_hdr))
290 _validate_header(raw_hdr)
291
292 # Prepare the new partition table entry
293 raw_entry = struct.pack(_GPT_ENTRY_FORMAT,
294 uuid.UUID(entry['type_uuid']).bytes_le,
295 uuid.UUID(entry['part_uuid']).bytes_le,
296 entry['first_lba'],
297 entry['last_lba'],
298 entry['flags'],
299 entry['name'].encode('UTF-16'))
300
301 # Write the updated entry to the disk
302 entry_offs = header['ptable_offs'] + \
303 header['entry_size'] * entry['index']
304 self._write_disk(entry_offs, raw_entry)
305
306 # Calculate and update partition table CRC32
307 raw_ptable = self._read_disk(header['ptable_offs'],
308 header['ptable_size'])
309 raw_hdr[13] = binascii.crc32(raw_ptable) & 0xFFFFFFFF
310
311 # Calculate and update the GPT header CRC
312 raw_hdr[3] = _calc_header_crc(raw_hdr)
313
314 # Write the updated header to the disk
315 raw_hdr = struct.pack(_GPT_HEADER_FORMAT, *raw_hdr)
316 self._write_disk(header['hdr_offs'], raw_hdr)
317
318 def change_partition(self, entry):
319 """ Change a GPT partition. The 'entry' argument has the same format as
320 'get_partitions()' returns. This function simply changes the partition
321 table record corresponding to 'entry' in both, the primary and the
322 backup GPT partition tables. The parition table CRC is re-calculated
323 and the GPT headers are modified accordingly. """
324
325 # Change the primary partition table
326 header = self.read_header(True)
327 self._change_partition(header, entry)
328
329 # Change the backup partition table
330 header = self.read_header(False)
331 self._change_partition(header, entry)
diff --git a/scripts/lib/mic/utils/grabber.py b/scripts/lib/mic/utils/grabber.py
new file mode 100644
index 0000000000..45e30b4fb0
--- /dev/null
+++ b/scripts/lib/mic/utils/grabber.py
@@ -0,0 +1,97 @@
1#!/usr/bin/python
2
3import os
4import sys
5import rpm
6import fcntl
7import struct
8import termios
9
10from mic import msger
11from mic.utils import runner
12from mic.utils.errors import CreatorError
13
14from urlgrabber import grabber
15from urlgrabber import __version__ as grabber_version
16
17if rpm.labelCompare(grabber_version.split('.'), '3.9.0'.split('.')) == -1:
18 msger.warning("Version of python-urlgrabber is %s, lower than '3.9.0', "
19 "you may encounter some network issues" % grabber_version)
20
21def myurlgrab(url, filename, proxies, progress_obj = None):
22 g = grabber.URLGrabber()
23 if progress_obj is None:
24 progress_obj = TextProgress()
25
26 if url.startswith("file:/"):
27 filepath = "/%s" % url.replace("file:", "").lstrip('/')
28 if not os.path.exists(filepath):
29 raise CreatorError("URLGrabber error: can't find file %s" % url)
30 if url.endswith('.rpm'):
31 return filepath
32 else:
33 # untouch repometadata in source path
34 runner.show(['cp', '-f', filepath, filename])
35
36 else:
37 try:
38 filename = g.urlgrab(url=str(url),
39 filename=filename,
40 ssl_verify_host=False,
41 ssl_verify_peer=False,
42 proxies=proxies,
43 http_headers=(('Pragma', 'no-cache'),),
44 quote=0,
45 progress_obj=progress_obj)
46 except grabber.URLGrabError, err:
47 msg = str(err)
48 if msg.find(url) < 0:
49 msg += ' on %s' % url
50 raise CreatorError(msg)
51
52 return filename
53
54def terminal_width(fd=1):
55 """ Get the real terminal width """
56 try:
57 buf = 'abcdefgh'
58 buf = fcntl.ioctl(fd, termios.TIOCGWINSZ, buf)
59 return struct.unpack('hhhh', buf)[1]
60 except: # IOError
61 return 80
62
63def truncate_url(url, width):
64 return os.path.basename(url)[0:width]
65
66class TextProgress(object):
67 # make the class as singleton
68 _instance = None
69 def __new__(cls, *args, **kwargs):
70 if not cls._instance:
71 cls._instance = super(TextProgress, cls).__new__(cls, *args, **kwargs)
72
73 return cls._instance
74
75 def __init__(self, totalnum = None):
76 self.total = totalnum
77 self.counter = 1
78
79 def start(self, filename, url, *args, **kwargs):
80 self.url = url
81 self.termwidth = terminal_width()
82 msger.info("\r%-*s" % (self.termwidth, " "))
83 if self.total is None:
84 msger.info("\rRetrieving %s ..." % truncate_url(self.url, self.termwidth - 15))
85 else:
86 msger.info("\rRetrieving %s [%d/%d] ..." % (truncate_url(self.url, self.termwidth - 25), self.counter, self.total))
87
88 def update(self, *args):
89 pass
90
91 def end(self, *args):
92 if self.counter == self.total:
93 msger.raw("\n")
94
95 if self.total is not None:
96 self.counter += 1
97
diff --git a/scripts/lib/mic/utils/misc.py b/scripts/lib/mic/utils/misc.py
new file mode 100644
index 0000000000..63024346a9
--- /dev/null
+++ b/scripts/lib/mic/utils/misc.py
@@ -0,0 +1,1067 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2010, 2011 Intel Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import sys
20import time
21import tempfile
22import re
23import shutil
24import glob
25import hashlib
26import subprocess
27import platform
28import traceback
29
30
31try:
32 import sqlite3 as sqlite
33except ImportError:
34 import sqlite
35
36try:
37 from xml.etree import cElementTree
38except ImportError:
39 import cElementTree
40xmlparse = cElementTree.parse
41
42from mic import msger
43from mic.utils.errors import CreatorError, SquashfsError
44from mic.utils.fs_related import find_binary_path, makedirs
45from mic.utils.grabber import myurlgrab
46from mic.utils.proxy import get_proxy_for
47from mic.utils import runner
48from mic.utils import rpmmisc
49
50
51RPM_RE = re.compile("(.*)\.(.*) (.*)-(.*)")
52RPM_FMT = "%(name)s.%(arch)s %(version)s-%(release)s"
53SRPM_RE = re.compile("(.*)-(\d+.*)-(\d+\.\d+).src.rpm")
54
55
56def build_name(kscfg, release=None, prefix = None, suffix = None):
57 """Construct and return an image name string.
58
59 This is a utility function to help create sensible name and fslabel
60 strings. The name is constructed using the sans-prefix-and-extension
61 kickstart filename and the supplied prefix and suffix.
62
63 kscfg -- a path to a kickstart file
64 release -- a replacement to suffix for image release
65 prefix -- a prefix to prepend to the name; defaults to None, which causes
66 no prefix to be used
67 suffix -- a suffix to append to the name; defaults to None, which causes
68 a YYYYMMDDHHMM suffix to be used
69
70 Note, if maxlen is less then the len(suffix), you get to keep both pieces.
71
72 """
73 name = os.path.basename(kscfg)
74 idx = name.rfind('.')
75 if idx >= 0:
76 name = name[:idx]
77
78 if release is not None:
79 suffix = ""
80 if prefix is None:
81 prefix = ""
82 if suffix is None:
83 suffix = time.strftime("%Y%m%d%H%M")
84
85 if name.startswith(prefix):
86 name = name[len(prefix):]
87
88 prefix = "%s-" % prefix if prefix else ""
89 suffix = "-%s" % suffix if suffix else ""
90
91 ret = prefix + name + suffix
92 return ret
93
94def get_distro():
95 """Detect linux distribution, support "meego"
96 """
97
98 support_dists = ('SuSE',
99 'debian',
100 'fedora',
101 'redhat',
102 'centos',
103 'meego',
104 'moblin',
105 'tizen')
106 try:
107 (dist, ver, id) = platform.linux_distribution( \
108 supported_dists = support_dists)
109 except:
110 (dist, ver, id) = platform.dist( \
111 supported_dists = support_dists)
112
113 return (dist, ver, id)
114
115def get_distro_str():
116 """Get composited string for current linux distribution
117 """
118 (dist, ver, id) = get_distro()
119
120 if not dist:
121 return 'Unknown Linux Distro'
122 else:
123 distro_str = ' '.join(map(str.strip, (dist, ver, id)))
124 return distro_str.strip()
125
126_LOOP_RULE_PTH = None
127
128def hide_loopdev_presentation():
129 udev_rules = "80-prevent-loop-present.rules"
130 udev_rules_dir = [
131 '/usr/lib/udev/rules.d/',
132 '/lib/udev/rules.d/',
133 '/etc/udev/rules.d/'
134 ]
135
136 global _LOOP_RULE_PTH
137
138 for rdir in udev_rules_dir:
139 if os.path.exists(rdir):
140 _LOOP_RULE_PTH = os.path.join(rdir, udev_rules)
141
142 if not _LOOP_RULE_PTH:
143 return
144
145 try:
146 with open(_LOOP_RULE_PTH, 'w') as wf:
147 wf.write('KERNEL=="loop*", ENV{UDISKS_PRESENTATION_HIDE}="1"')
148
149 runner.quiet('udevadm trigger')
150 except:
151 pass
152
153def unhide_loopdev_presentation():
154 global _LOOP_RULE_PTH
155
156 if not _LOOP_RULE_PTH:
157 return
158
159 try:
160 os.unlink(_LOOP_RULE_PTH)
161 runner.quiet('udevadm trigger')
162 except:
163 pass
164
165def extract_rpm(rpmfile, targetdir):
166 rpm2cpio = find_binary_path("rpm2cpio")
167 cpio = find_binary_path("cpio")
168
169 olddir = os.getcwd()
170 os.chdir(targetdir)
171
172 msger.verbose("Extract rpm file with cpio: %s" % rpmfile)
173 p1 = subprocess.Popen([rpm2cpio, rpmfile], stdout=subprocess.PIPE)
174 p2 = subprocess.Popen([cpio, "-idv"], stdin=p1.stdout,
175 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
176 (sout, serr) = p2.communicate()
177 msger.verbose(sout or serr)
178
179 os.chdir(olddir)
180
181def compressing(fpath, method):
182 comp_map = {
183 "gz": "gzip",
184 "bz2": "bzip2"
185 }
186 if method not in comp_map:
187 raise CreatorError("Unsupport compress format: %s, valid values: %s"
188 % (method, ','.join(comp_map.keys())))
189 cmd = find_binary_path(comp_map[method])
190 rc = runner.show([cmd, "-f", fpath])
191 if rc:
192 raise CreatorError("Failed to %s file: %s" % (comp_map[method], fpath))
193
194def taring(dstfile, target):
195 import tarfile
196 basen, ext = os.path.splitext(dstfile)
197 comp = {".tar": None,
198 ".gz": "gz", # for .tar.gz
199 ".bz2": "bz2", # for .tar.bz2
200 ".tgz": "gz",
201 ".tbz": "bz2"}[ext]
202
203 # specify tarball file path
204 if not comp:
205 tarpath = dstfile
206 elif basen.endswith(".tar"):
207 tarpath = basen
208 else:
209 tarpath = basen + ".tar"
210 wf = tarfile.open(tarpath, 'w')
211
212 if os.path.isdir(target):
213 for item in os.listdir(target):
214 wf.add(os.path.join(target, item), item)
215 else:
216 wf.add(target, os.path.basename(target))
217 wf.close()
218
219 if comp:
220 compressing(tarpath, comp)
221 # when dstfile ext is ".tgz" and ".tbz", should rename
222 if not basen.endswith(".tar"):
223 shutil.move("%s.%s" % (tarpath, comp), dstfile)
224
225def ziping(dstfile, target):
226 import zipfile
227 wf = zipfile.ZipFile(dstfile, 'w', compression=zipfile.ZIP_DEFLATED)
228 if os.path.isdir(target):
229 for item in os.listdir(target):
230 fpath = os.path.join(target, item)
231 if not os.path.isfile(fpath):
232 continue
233 wf.write(fpath, item, zipfile.ZIP_DEFLATED)
234 else:
235 wf.write(target, os.path.basename(target), zipfile.ZIP_DEFLATED)
236 wf.close()
237
238pack_formats = {
239 ".tar": taring,
240 ".tar.gz": taring,
241 ".tar.bz2": taring,
242 ".tgz": taring,
243 ".tbz": taring,
244 ".zip": ziping,
245}
246
247def packing(dstfile, target):
248 (base, ext) = os.path.splitext(dstfile)
249 if ext in (".gz", ".bz2") and base.endswith(".tar"):
250 ext = ".tar" + ext
251 if ext not in pack_formats:
252 raise CreatorError("Unsupport pack format: %s, valid values: %s"
253 % (ext, ','.join(pack_formats.keys())))
254 func = pack_formats[ext]
255 # func should be callable
256 func(dstfile, target)
257
258def human_size(size):
259 """Return human readable string for Bytes size
260 """
261
262 if size <= 0:
263 return "0M"
264 import math
265 measure = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
266 expo = int(math.log(size, 1024))
267 mant = float(size/math.pow(1024, expo))
268 return "{0:.1f}{1:s}".format(mant, measure[expo])
269
270def get_block_size(file_obj):
271 """ Returns block size for file object 'file_obj'. Errors are indicated by
272 the 'IOError' exception. """
273
274 from fcntl import ioctl
275 import struct
276
277 # Get the block size of the host file-system for the image file by calling
278 # the FIGETBSZ ioctl (number 2).
279 binary_data = ioctl(file_obj, 2, struct.pack('I', 0))
280 return struct.unpack('I', binary_data)[0]
281
282def check_space_pre_cp(src, dst):
283 """Check whether disk space is enough before 'cp' like
284 operations, else exception will be raised.
285 """
286
287 srcsize = get_file_size(src) * 1024 * 1024
288 freesize = get_filesystem_avail(dst)
289 if srcsize > freesize:
290 raise CreatorError("space on %s(%s) is not enough for about %s files"
291 % (dst, human_size(freesize), human_size(srcsize)))
292
293def calc_hashes(file_path, hash_names, start = 0, end = None):
294 """ Calculate hashes for a file. The 'file_path' argument is the file
295 to calculate hash functions for, 'start' and 'end' are the starting and
296 ending file offset to calculate the has functions for. The 'hash_names'
297 argument is a list of hash names to calculate. Returns the the list
298 of calculated hash values in the hexadecimal form in the same order
299 as 'hash_names'.
300 """
301 if end == None:
302 end = os.path.getsize(file_path)
303
304 chunk_size = 65536
305 to_read = end - start
306 read = 0
307
308 hashes = []
309 for hash_name in hash_names:
310 hashes.append(hashlib.new(hash_name))
311
312 with open(file_path, "rb") as f:
313 f.seek(start)
314
315 while read < to_read:
316 if read + chunk_size > to_read:
317 chunk_size = to_read - read
318 chunk = f.read(chunk_size)
319 for hash_obj in hashes:
320 hash_obj.update(chunk)
321 read += chunk_size
322
323 result = []
324 for hash_obj in hashes:
325 result.append(hash_obj.hexdigest())
326
327 return result
328
329def get_md5sum(fpath):
330 return calc_hashes(fpath, ('md5', ))[0]
331
332
333def normalize_ksfile(ksconf, release, arch):
334 '''
335 Return the name of a normalized ks file in which macro variables
336 @BUILD_ID@ and @ARCH@ are replace with real values.
337
338 The original ks file is returned if no special macro is used, otherwise
339 a temp file is created and returned, which will be deleted when program
340 exits normally.
341 '''
342
343 if not release:
344 release = "latest"
345 if not arch or re.match(r'i.86', arch):
346 arch = "ia32"
347
348 with open(ksconf) as f:
349 ksc = f.read()
350
351 if "@ARCH@" not in ksc and "@BUILD_ID@" not in ksc:
352 return ksconf
353
354 msger.info("Substitute macro variable @BUILD_ID@/@ARCH@ in ks: %s" % ksconf)
355 ksc = ksc.replace("@ARCH@", arch)
356 ksc = ksc.replace("@BUILD_ID@", release)
357
358 fd, ksconf = tempfile.mkstemp(prefix=os.path.basename(ksconf))
359 os.write(fd, ksc)
360 os.close(fd)
361
362 msger.debug('normalized ks file:%s' % ksconf)
363
364 def remove_temp_ks():
365 try:
366 os.unlink(ksconf)
367 except OSError, err:
368 msger.warning('Failed to remove temp ks file:%s:%s' % (ksconf, err))
369
370 import atexit
371 atexit.register(remove_temp_ks)
372
373 return ksconf
374
375
376def _check_mic_chroot(rootdir):
377 def _path(path):
378 return rootdir.rstrip('/') + path
379
380 release_files = map(_path, [ "/etc/moblin-release",
381 "/etc/meego-release",
382 "/etc/tizen-release"])
383
384 if not any(map(os.path.exists, release_files)):
385 msger.warning("Dir %s is not a MeeGo/Tizen chroot env" % rootdir)
386
387 if not glob.glob(rootdir + "/boot/vmlinuz-*"):
388 msger.warning("Failed to find kernel module under %s" % rootdir)
389
390 return
391
392def selinux_check(arch, fstypes):
393 try:
394 getenforce = find_binary_path('getenforce')
395 except CreatorError:
396 return
397
398 selinux_status = runner.outs([getenforce])
399 if arch and arch.startswith("arm") and selinux_status == "Enforcing":
400 raise CreatorError("Can't create arm image if selinux is enabled, "
401 "please run 'setenforce 0' to disable selinux")
402
403 use_btrfs = filter(lambda typ: typ == 'btrfs', fstypes)
404 if use_btrfs and selinux_status == "Enforcing":
405 raise CreatorError("Can't create btrfs image if selinux is enabled,"
406 " please run 'setenforce 0' to disable selinux")
407
408def get_image_type(path):
409 def _get_extension_name(path):
410 match = re.search("(?<=\.)\w+$", path)
411 if match:
412 return match.group(0)
413 else:
414 return None
415
416 if os.path.isdir(path):
417 _check_mic_chroot(path)
418 return "fs"
419
420 maptab = {
421 "tar": "loop",
422 "raw":"raw",
423 "vmdk":"vmdk",
424 "vdi":"vdi",
425 "iso":"livecd",
426 "usbimg":"liveusb",
427 }
428
429 extension = _get_extension_name(path)
430 if extension in maptab:
431 return maptab[extension]
432
433 fd = open(path, "rb")
434 file_header = fd.read(1024)
435 fd.close()
436 vdi_flag = "<<< Sun VirtualBox Disk Image >>>"
437 if file_header[0:len(vdi_flag)] == vdi_flag:
438 return maptab["vdi"]
439
440 output = runner.outs(['file', path])
441 isoptn = re.compile(r".*ISO 9660 CD-ROM filesystem.*(bootable).*")
442 usbimgptn = re.compile(r".*x86 boot sector.*active.*")
443 rawptn = re.compile(r".*x86 boot sector.*")
444 vmdkptn = re.compile(r".*VMware. disk image.*")
445 ext3fsimgptn = re.compile(r".*Linux.*ext3 filesystem data.*")
446 ext4fsimgptn = re.compile(r".*Linux.*ext4 filesystem data.*")
447 btrfsimgptn = re.compile(r".*BTRFS.*")
448 if isoptn.match(output):
449 return maptab["iso"]
450 elif usbimgptn.match(output):
451 return maptab["usbimg"]
452 elif rawptn.match(output):
453 return maptab["raw"]
454 elif vmdkptn.match(output):
455 return maptab["vmdk"]
456 elif ext3fsimgptn.match(output):
457 return "ext3fsimg"
458 elif ext4fsimgptn.match(output):
459 return "ext4fsimg"
460 elif btrfsimgptn.match(output):
461 return "btrfsimg"
462 else:
463 raise CreatorError("Cannot detect the type of image: %s" % path)
464
465
466def get_file_size(filename):
467 """ Return size in MB unit """
468 cmd = ['du', "-s", "-b", "-B", "1M", filename]
469 rc, duOutput = runner.runtool(cmd)
470 if rc != 0:
471 raise CreatorError("Failed to run: %s" % ' '.join(cmd))
472 size1 = int(duOutput.split()[0])
473
474 cmd = ['du', "-s", "-B", "1M", filename]
475 rc, duOutput = runner.runtool(cmd)
476 if rc != 0:
477 raise CreatorError("Failed to run: %s" % ' '.join(cmd))
478
479 size2 = int(duOutput.split()[0])
480 return max(size1, size2)
481
482
483def get_filesystem_avail(fs):
484 vfstat = os.statvfs(fs)
485 return vfstat.f_bavail * vfstat.f_bsize
486
487def convert_image(srcimg, srcfmt, dstimg, dstfmt):
488 #convert disk format
489 if dstfmt != "raw":
490 raise CreatorError("Invalid destination image format: %s" % dstfmt)
491 msger.debug("converting %s image to %s" % (srcimg, dstimg))
492 if srcfmt == "vmdk":
493 path = find_binary_path("qemu-img")
494 argv = [path, "convert", "-f", "vmdk", srcimg, "-O", dstfmt, dstimg]
495 elif srcfmt == "vdi":
496 path = find_binary_path("VBoxManage")
497 argv = [path, "internalcommands", "converttoraw", srcimg, dstimg]
498 else:
499 raise CreatorError("Invalid soure image format: %s" % srcfmt)
500
501 rc = runner.show(argv)
502 if rc == 0:
503 msger.debug("convert successful")
504 if rc != 0:
505 raise CreatorError("Unable to convert disk to %s" % dstfmt)
506
507def uncompress_squashfs(squashfsimg, outdir):
508 """Uncompress file system from squshfs image"""
509 unsquashfs = find_binary_path("unsquashfs")
510 args = [ unsquashfs, "-d", outdir, squashfsimg ]
511 rc = runner.show(args)
512 if (rc != 0):
513 raise SquashfsError("Failed to uncompress %s." % squashfsimg)
514
515def mkdtemp(dir = "/var/tmp", prefix = "mic-tmp-"):
516 """ FIXME: use the dir in mic.conf instead """
517
518 makedirs(dir)
519 return tempfile.mkdtemp(dir = dir, prefix = prefix)
520
521def get_repostrs_from_ks(ks):
522 def _get_temp_reponame(baseurl):
523 md5obj = hashlib.md5(baseurl)
524 tmpreponame = "%s" % md5obj.hexdigest()
525 return tmpreponame
526
527 kickstart_repos = []
528
529 for repodata in ks.handler.repo.repoList:
530 repo = {}
531 for attr in ('name',
532 'baseurl',
533 'mirrorlist',
534 'includepkgs', # val is list
535 'excludepkgs', # val is list
536 'cost', # int
537 'priority',# int
538 'save',
539 'proxy',
540 'proxyuser',
541 'proxypasswd',
542 'proxypasswd',
543 'debuginfo',
544 'source',
545 'gpgkey',
546 'ssl_verify'):
547 if hasattr(repodata, attr) and getattr(repodata, attr):
548 repo[attr] = getattr(repodata, attr)
549
550 if 'name' not in repo:
551 repo['name'] = _get_temp_reponame(repodata.baseurl)
552
553 kickstart_repos.append(repo)
554
555 return kickstart_repos
556
557def _get_uncompressed_data_from_url(url, filename, proxies):
558 filename = myurlgrab(url, filename, proxies)
559 suffix = None
560 if filename.endswith(".gz"):
561 suffix = ".gz"
562 runner.quiet(['gunzip', "-f", filename])
563 elif filename.endswith(".bz2"):
564 suffix = ".bz2"
565 runner.quiet(['bunzip2', "-f", filename])
566 if suffix:
567 filename = filename.replace(suffix, "")
568 return filename
569
570def _get_metadata_from_repo(baseurl, proxies, cachedir, reponame, filename,
571 sumtype=None, checksum=None):
572 url = os.path.join(baseurl, filename)
573 filename_tmp = str("%s/%s/%s" % (cachedir, reponame, os.path.basename(filename)))
574 if os.path.splitext(filename_tmp)[1] in (".gz", ".bz2"):
575 filename = os.path.splitext(filename_tmp)[0]
576 else:
577 filename = filename_tmp
578 if sumtype and checksum and os.path.exists(filename):
579 try:
580 sumcmd = find_binary_path("%ssum" % sumtype)
581 except:
582 file_checksum = None
583 else:
584 file_checksum = runner.outs([sumcmd, filename]).split()[0]
585
586 if file_checksum and file_checksum == checksum:
587 return filename
588
589 return _get_uncompressed_data_from_url(url,filename_tmp,proxies)
590
591def get_metadata_from_repos(repos, cachedir):
592 my_repo_metadata = []
593 for repo in repos:
594 reponame = repo['name']
595 baseurl = repo['baseurl']
596
597
598 if 'proxy' in repo:
599 proxy = repo['proxy']
600 else:
601 proxy = get_proxy_for(baseurl)
602
603 proxies = None
604 if proxy:
605 proxies = {str(baseurl.split(":")[0]):str(proxy)}
606
607 makedirs(os.path.join(cachedir, reponame))
608 url = os.path.join(baseurl, "repodata/repomd.xml")
609 filename = os.path.join(cachedir, reponame, 'repomd.xml')
610 repomd = myurlgrab(url, filename, proxies)
611 try:
612 root = xmlparse(repomd)
613 except SyntaxError:
614 raise CreatorError("repomd.xml syntax error.")
615
616 ns = root.getroot().tag
617 ns = ns[0:ns.rindex("}")+1]
618
619 filepaths = {}
620 checksums = {}
621 sumtypes = {}
622
623 for elm in root.getiterator("%sdata" % ns):
624 if elm.attrib["type"] == "patterns":
625 filepaths['patterns'] = elm.find("%slocation" % ns).attrib['href']
626 checksums['patterns'] = elm.find("%sopen-checksum" % ns).text
627 sumtypes['patterns'] = elm.find("%sopen-checksum" % ns).attrib['type']
628 break
629
630 for elm in root.getiterator("%sdata" % ns):
631 if elm.attrib["type"] in ("group_gz", "group"):
632 filepaths['comps'] = elm.find("%slocation" % ns).attrib['href']
633 checksums['comps'] = elm.find("%sopen-checksum" % ns).text
634 sumtypes['comps'] = elm.find("%sopen-checksum" % ns).attrib['type']
635 break
636
637 primary_type = None
638 for elm in root.getiterator("%sdata" % ns):
639 if elm.attrib["type"] in ("primary_db", "primary"):
640 primary_type = elm.attrib["type"]
641 filepaths['primary'] = elm.find("%slocation" % ns).attrib['href']
642 checksums['primary'] = elm.find("%sopen-checksum" % ns).text
643 sumtypes['primary'] = elm.find("%sopen-checksum" % ns).attrib['type']
644 break
645
646 if not primary_type:
647 continue
648
649 for item in ("primary", "patterns", "comps"):
650 if item not in filepaths:
651 filepaths[item] = None
652 continue
653 if not filepaths[item]:
654 continue
655 filepaths[item] = _get_metadata_from_repo(baseurl,
656 proxies,
657 cachedir,
658 reponame,
659 filepaths[item],
660 sumtypes[item],
661 checksums[item])
662
663 """ Get repo key """
664 try:
665 repokey = _get_metadata_from_repo(baseurl,
666 proxies,
667 cachedir,
668 reponame,
669 "repodata/repomd.xml.key")
670 except CreatorError:
671 repokey = None
672 msger.debug("\ncan't get %s/%s" % (baseurl, "repodata/repomd.xml.key"))
673
674 my_repo_metadata.append({"name":reponame,
675 "baseurl":baseurl,
676 "repomd":repomd,
677 "primary":filepaths['primary'],
678 "cachedir":cachedir,
679 "proxies":proxies,
680 "patterns":filepaths['patterns'],
681 "comps":filepaths['comps'],
682 "repokey":repokey})
683
684 return my_repo_metadata
685
686def get_rpmver_in_repo(repometadata):
687 for repo in repometadata:
688 if repo["primary"].endswith(".xml"):
689 root = xmlparse(repo["primary"])
690 ns = root.getroot().tag
691 ns = ns[0:ns.rindex("}")+1]
692
693 versionlist = []
694 for elm in root.getiterator("%spackage" % ns):
695 if elm.find("%sname" % ns).text == 'rpm':
696 for node in elm.getchildren():
697 if node.tag == "%sversion" % ns:
698 versionlist.append(node.attrib['ver'])
699
700 if versionlist:
701 return reversed(
702 sorted(
703 versionlist,
704 key = lambda ver: map(int, ver.split('.')))).next()
705
706 elif repo["primary"].endswith(".sqlite"):
707 con = sqlite.connect(repo["primary"])
708 for row in con.execute("select version from packages where "
709 "name=\"rpm\" ORDER by version DESC"):
710 con.close()
711 return row[0]
712
713 return None
714
715def get_arch(repometadata):
716 archlist = []
717 for repo in repometadata:
718 if repo["primary"].endswith(".xml"):
719 root = xmlparse(repo["primary"])
720 ns = root.getroot().tag
721 ns = ns[0:ns.rindex("}")+1]
722 for elm in root.getiterator("%spackage" % ns):
723 if elm.find("%sarch" % ns).text not in ("noarch", "src"):
724 arch = elm.find("%sarch" % ns).text
725 if arch not in archlist:
726 archlist.append(arch)
727 elif repo["primary"].endswith(".sqlite"):
728 con = sqlite.connect(repo["primary"])
729 for row in con.execute("select arch from packages where arch not in (\"src\", \"noarch\")"):
730 if row[0] not in archlist:
731 archlist.append(row[0])
732
733 con.close()
734
735 uniq_arch = []
736 for i in range(len(archlist)):
737 if archlist[i] not in rpmmisc.archPolicies.keys():
738 continue
739 need_append = True
740 j = 0
741 while j < len(uniq_arch):
742 if archlist[i] in rpmmisc.archPolicies[uniq_arch[j]].split(':'):
743 need_append = False
744 break
745 if uniq_arch[j] in rpmmisc.archPolicies[archlist[i]].split(':'):
746 if need_append:
747 uniq_arch[j] = archlist[i]
748 need_append = False
749 else:
750 uniq_arch.remove(uniq_arch[j])
751 continue
752 j += 1
753 if need_append:
754 uniq_arch.append(archlist[i])
755
756 return uniq_arch, archlist
757
758def get_package(pkg, repometadata, arch = None):
759 ver = ""
760 target_repo = None
761 if not arch:
762 arches = []
763 elif arch not in rpmmisc.archPolicies:
764 arches = [arch]
765 else:
766 arches = rpmmisc.archPolicies[arch].split(':')
767 arches.append('noarch')
768
769 for repo in repometadata:
770 if repo["primary"].endswith(".xml"):
771 root = xmlparse(repo["primary"])
772 ns = root.getroot().tag
773 ns = ns[0:ns.rindex("}")+1]
774 for elm in root.getiterator("%spackage" % ns):
775 if elm.find("%sname" % ns).text == pkg:
776 if elm.find("%sarch" % ns).text in arches:
777 version = elm.find("%sversion" % ns)
778 tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel'])
779 if tmpver > ver:
780 ver = tmpver
781 location = elm.find("%slocation" % ns)
782 pkgpath = "%s" % location.attrib['href']
783 target_repo = repo
784 break
785 if repo["primary"].endswith(".sqlite"):
786 con = sqlite.connect(repo["primary"])
787 if arch:
788 sql = 'select version, release, location_href from packages ' \
789 'where name = "%s" and arch IN ("%s")' % \
790 (pkg, '","'.join(arches))
791 for row in con.execute(sql):
792 tmpver = "%s-%s" % (row[0], row[1])
793 if tmpver > ver:
794 ver = tmpver
795 pkgpath = "%s" % row[2]
796 target_repo = repo
797 break
798 else:
799 sql = 'select version, release, location_href from packages ' \
800 'where name = "%s"' % pkg
801 for row in con.execute(sql):
802 tmpver = "%s-%s" % (row[0], row[1])
803 if tmpver > ver:
804 ver = tmpver
805 pkgpath = "%s" % row[2]
806 target_repo = repo
807 break
808 con.close()
809 if target_repo:
810 makedirs("%s/packages/%s" % (target_repo["cachedir"], target_repo["name"]))
811 url = os.path.join(target_repo["baseurl"], pkgpath)
812 filename = str("%s/packages/%s/%s" % (target_repo["cachedir"], target_repo["name"], os.path.basename(pkgpath)))
813 if os.path.exists(filename):
814 ret = rpmmisc.checkRpmIntegrity('rpm', filename)
815 if ret == 0:
816 return filename
817
818 msger.warning("package %s is damaged: %s" %
819 (os.path.basename(filename), filename))
820 os.unlink(filename)
821
822 pkg = myurlgrab(str(url), filename, target_repo["proxies"])
823 return pkg
824 else:
825 return None
826
827def get_source_name(pkg, repometadata):
828
829 def get_bin_name(pkg):
830 m = RPM_RE.match(pkg)
831 if m:
832 return m.group(1)
833 return None
834
835 def get_src_name(srpm):
836 m = SRPM_RE.match(srpm)
837 if m:
838 return m.group(1)
839 return None
840
841 ver = ""
842 target_repo = None
843
844 pkg_name = get_bin_name(pkg)
845 if not pkg_name:
846 return None
847
848 for repo in repometadata:
849 if repo["primary"].endswith(".xml"):
850 root = xmlparse(repo["primary"])
851 ns = root.getroot().tag
852 ns = ns[0:ns.rindex("}")+1]
853 for elm in root.getiterator("%spackage" % ns):
854 if elm.find("%sname" % ns).text == pkg_name:
855 if elm.find("%sarch" % ns).text != "src":
856 version = elm.find("%sversion" % ns)
857 tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel'])
858 if tmpver > ver:
859 ver = tmpver
860 fmt = elm.find("%sformat" % ns)
861 if fmt:
862 fns = fmt.getchildren()[0].tag
863 fns = fns[0:fns.rindex("}")+1]
864 pkgpath = fmt.find("%ssourcerpm" % fns).text
865 target_repo = repo
866 break
867
868 if repo["primary"].endswith(".sqlite"):
869 con = sqlite.connect(repo["primary"])
870 for row in con.execute("select version, release, rpm_sourcerpm from packages where name = \"%s\" and arch != \"src\"" % pkg_name):
871 tmpver = "%s-%s" % (row[0], row[1])
872 if tmpver > ver:
873 pkgpath = "%s" % row[2]
874 target_repo = repo
875 break
876 con.close()
877 if target_repo:
878 return get_src_name(pkgpath)
879 else:
880 return None
881
882def get_pkglist_in_patterns(group, patterns):
883 found = False
884 pkglist = []
885 try:
886 root = xmlparse(patterns)
887 except SyntaxError:
888 raise SyntaxError("%s syntax error." % patterns)
889
890 for elm in list(root.getroot()):
891 ns = elm.tag
892 ns = ns[0:ns.rindex("}")+1]
893 name = elm.find("%sname" % ns)
894 summary = elm.find("%ssummary" % ns)
895 if name.text == group or summary.text == group:
896 found = True
897 break
898
899 if not found:
900 return pkglist
901
902 found = False
903 for requires in list(elm):
904 if requires.tag.endswith("requires"):
905 found = True
906 break
907
908 if not found:
909 return pkglist
910
911 for pkg in list(requires):
912 pkgname = pkg.attrib["name"]
913 if pkgname not in pkglist:
914 pkglist.append(pkgname)
915
916 return pkglist
917
918def get_pkglist_in_comps(group, comps):
919 found = False
920 pkglist = []
921 try:
922 root = xmlparse(comps)
923 except SyntaxError:
924 raise SyntaxError("%s syntax error." % comps)
925
926 for elm in root.getiterator("group"):
927 id = elm.find("id")
928 name = elm.find("name")
929 if id.text == group or name.text == group:
930 packagelist = elm.find("packagelist")
931 found = True
932 break
933
934 if not found:
935 return pkglist
936
937 for require in elm.getiterator("packagereq"):
938 if require.tag.endswith("packagereq"):
939 pkgname = require.text
940 if pkgname not in pkglist:
941 pkglist.append(pkgname)
942
943 return pkglist
944
945def is_statically_linked(binary):
946 return ", statically linked, " in runner.outs(['file', binary])
947
948def setup_qemu_emulator(rootdir, arch):
949 # mount binfmt_misc if it doesn't exist
950 if not os.path.exists("/proc/sys/fs/binfmt_misc"):
951 modprobecmd = find_binary_path("modprobe")
952 runner.show([modprobecmd, "binfmt_misc"])
953 if not os.path.exists("/proc/sys/fs/binfmt_misc/register"):
954 mountcmd = find_binary_path("mount")
955 runner.show([mountcmd, "-t", "binfmt_misc", "none", "/proc/sys/fs/binfmt_misc"])
956
957 # qemu_emulator is a special case, we can't use find_binary_path
958 # qemu emulator should be a statically-linked executable file
959 qemu_emulator = "/usr/bin/qemu-arm"
960 if not os.path.exists(qemu_emulator) or not is_statically_linked(qemu_emulator):
961 qemu_emulator = "/usr/bin/qemu-arm-static"
962 if not os.path.exists(qemu_emulator):
963 raise CreatorError("Please install a statically-linked qemu-arm")
964
965 # qemu emulator version check
966 armv7_list = [arch for arch in rpmmisc.archPolicies.keys() if arch.startswith('armv7')]
967 if arch in armv7_list: # need qemu (>=0.13.0)
968 qemuout = runner.outs([qemu_emulator, "-h"])
969 m = re.search("version\s*([.\d]+)", qemuout)
970 if m:
971 qemu_version = m.group(1)
972 if qemu_version < "0.13":
973 raise CreatorError("Requires %s version >=0.13 for %s" % (qemu_emulator, arch))
974 else:
975 msger.warning("Can't get version info of %s, please make sure it's higher than 0.13.0" % qemu_emulator)
976
977 if not os.path.exists(rootdir + "/usr/bin"):
978 makedirs(rootdir + "/usr/bin")
979 shutil.copy(qemu_emulator, rootdir + "/usr/bin/qemu-arm-static")
980 qemu_emulator = "/usr/bin/qemu-arm-static"
981
982 # disable selinux, selinux will block qemu emulator to run
983 if os.path.exists("/usr/sbin/setenforce"):
984 msger.info('Try to disable selinux')
985 runner.show(["/usr/sbin/setenforce", "0"])
986
987 # unregister it if it has been registered and is a dynamically-linked executable
988 node = "/proc/sys/fs/binfmt_misc/arm"
989 if os.path.exists(node):
990 qemu_unregister_string = "-1\n"
991 fd = open("/proc/sys/fs/binfmt_misc/arm", "w")
992 fd.write(qemu_unregister_string)
993 fd.close()
994
995 # register qemu emulator for interpreting other arch executable file
996 if not os.path.exists(node):
997 qemu_arm_string = ":arm:M::\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28\\x00:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfa\\xff\\xff\\xff:%s:\n" % qemu_emulator
998 fd = open("/proc/sys/fs/binfmt_misc/register", "w")
999 fd.write(qemu_arm_string)
1000 fd.close()
1001
1002 return qemu_emulator
1003
1004def SrcpkgsDownload(pkgs, repometadata, instroot, cachedir):
1005 def get_source_repometadata(repometadata):
1006 src_repometadata=[]
1007 for repo in repometadata:
1008 if repo["name"].endswith("-source"):
1009 src_repometadata.append(repo)
1010 if src_repometadata:
1011 return src_repometadata
1012 return None
1013
1014 def get_src_name(srpm):
1015 m = SRPM_RE.match(srpm)
1016 if m:
1017 return m.group(1)
1018 return None
1019
1020 src_repometadata = get_source_repometadata(repometadata)
1021
1022 if not src_repometadata:
1023 msger.warning("No source repo found")
1024 return None
1025
1026 src_pkgs = []
1027 lpkgs_dict = {}
1028 lpkgs_path = []
1029 for repo in src_repometadata:
1030 cachepath = "%s/%s/packages/*.src.rpm" %(cachedir, repo["name"])
1031 lpkgs_path += glob.glob(cachepath)
1032
1033 for lpkg in lpkgs_path:
1034 lpkg_name = get_src_name(os.path.basename(lpkg))
1035 lpkgs_dict[lpkg_name] = lpkg
1036 localpkgs = lpkgs_dict.keys()
1037
1038 cached_count = 0
1039 destdir = instroot+'/usr/src/SRPMS'
1040 if not os.path.exists(destdir):
1041 os.makedirs(destdir)
1042
1043 srcpkgset = set()
1044 for _pkg in pkgs:
1045 srcpkg_name = get_source_name(_pkg, repometadata)
1046 if not srcpkg_name:
1047 continue
1048 srcpkgset.add(srcpkg_name)
1049
1050 for pkg in list(srcpkgset):
1051 if pkg in localpkgs:
1052 cached_count += 1
1053 shutil.copy(lpkgs_dict[pkg], destdir)
1054 src_pkgs.append(os.path.basename(lpkgs_dict[pkg]))
1055 else:
1056 src_pkg = get_package(pkg, src_repometadata, 'src')
1057 if src_pkg:
1058 shutil.copy(src_pkg, destdir)
1059 src_pkgs.append(src_pkg)
1060 msger.info("%d source packages gotten from cache" % cached_count)
1061
1062 return src_pkgs
1063
1064def strip_end(text, suffix):
1065 if not text.endswith(suffix):
1066 return text
1067 return text[:-len(suffix)]
diff --git a/scripts/lib/mic/utils/partitionedfs.py b/scripts/lib/mic/utils/partitionedfs.py
new file mode 100644
index 0000000000..04758440e1
--- /dev/null
+++ b/scripts/lib/mic/utils/partitionedfs.py
@@ -0,0 +1,790 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2009, 2010, 2011 Intel, Inc.
4# Copyright (c) 2007, 2008 Red Hat, Inc.
5# Copyright (c) 2008 Daniel P. Berrange
6# Copyright (c) 2008 David P. Huff
7#
8# This program is free software; you can redistribute it and/or modify it
9# under the terms of the GNU General Public License as published by the Free
10# Software Foundation; version 2 of the License
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15# for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc., 59
19# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21import os
22
23from mic import msger
24from mic.utils import runner
25from mic.utils.errors import MountError
26from mic.utils.fs_related import *
27from mic.utils.gpt_parser import GptParser
28
29# Overhead of the MBR partitioning scheme (just one sector)
30MBR_OVERHEAD = 1
31# Overhead of the GPT partitioning scheme
32GPT_OVERHEAD = 34
33
34# Size of a sector in bytes
35SECTOR_SIZE = 512
36
37class PartitionedMount(Mount):
38 def __init__(self, mountdir, skipformat = False):
39 Mount.__init__(self, mountdir)
40 self.disks = {}
41 self.partitions = []
42 self.subvolumes = []
43 self.mapped = False
44 self.mountOrder = []
45 self.unmountOrder = []
46 self.parted = find_binary_path("parted")
47 self.kpartx = find_binary_path("kpartx")
48 self.mkswap = find_binary_path("mkswap")
49 self.btrfscmd=None
50 self.mountcmd = find_binary_path("mount")
51 self.umountcmd = find_binary_path("umount")
52 self.skipformat = skipformat
53 self.snapshot_created = self.skipformat
54 # Size of a sector used in calculations
55 self.sector_size = SECTOR_SIZE
56 self._partitions_layed_out = False
57
58 def __add_disk(self, disk_name):
59 """ Add a disk 'disk_name' to the internal list of disks. Note,
60 'disk_name' is the name of the disk in the target system
61 (e.g., sdb). """
62
63 if disk_name in self.disks:
64 # We already have this disk
65 return
66
67 assert not self._partitions_layed_out
68
69 self.disks[disk_name] = \
70 { 'disk': None, # Disk object
71 'mapped': False, # True if kpartx mapping exists
72 'numpart': 0, # Number of allocate partitions
73 'partitions': [], # Indexes to self.partitions
74 'offset': 0, # Offset of next partition (in sectors)
75 # Minimum required disk size to fit all partitions (in bytes)
76 'min_size': 0,
77 'ptable_format': "msdos" } # Partition table format
78
79 def add_disk(self, disk_name, disk_obj):
80 """ Add a disk object which have to be partitioned. More than one disk
81 can be added. In case of multiple disks, disk partitions have to be
82 added for each disk separately with 'add_partition()". """
83
84 self.__add_disk(disk_name)
85 self.disks[disk_name]['disk'] = disk_obj
86
87 def __add_partition(self, part):
88 """ This is a helper function for 'add_partition()' which adds a
89 partition to the internal list of partitions. """
90
91 assert not self._partitions_layed_out
92
93 self.partitions.append(part)
94 self.__add_disk(part['disk_name'])
95
96 def add_partition(self, size, disk_name, mountpoint, fstype = None,
97 label=None, fsopts = None, boot = False, align = None,
98 part_type = None):
99 """ Add the next partition. Prtitions have to be added in the
100 first-to-last order. """
101
102 ks_pnum = len(self.partitions)
103
104 # Converting MB to sectors for parted
105 size = size * 1024 * 1024 / self.sector_size
106
107 # We need to handle subvolumes for btrfs
108 if fstype == "btrfs" and fsopts and fsopts.find("subvol=") != -1:
109 self.btrfscmd=find_binary_path("btrfs")
110 subvol = None
111 opts = fsopts.split(",")
112 for opt in opts:
113 if opt.find("subvol=") != -1:
114 subvol = opt.replace("subvol=", "").strip()
115 break
116 if not subvol:
117 raise MountError("No subvolume: %s" % fsopts)
118 self.subvolumes.append({'size': size, # In sectors
119 'mountpoint': mountpoint, # Mount relative to chroot
120 'fstype': fstype, # Filesystem type
121 'fsopts': fsopts, # Filesystem mount options
122 'disk_name': disk_name, # physical disk name holding partition
123 'device': None, # kpartx device node for partition
124 'mount': None, # Mount object
125 'subvol': subvol, # Subvolume name
126 'boot': boot, # Bootable flag
127 'mounted': False # Mount flag
128 })
129
130 # We still need partition for "/" or non-subvolume
131 if mountpoint == "/" or not fsopts or fsopts.find("subvol=") == -1:
132 # Don't need subvolume for "/" because it will be set as default subvolume
133 if fsopts and fsopts.find("subvol=") != -1:
134 opts = fsopts.split(",")
135 for opt in opts:
136 if opt.strip().startswith("subvol="):
137 opts.remove(opt)
138 break
139 fsopts = ",".join(opts)
140
141 part = { 'ks_pnum' : ks_pnum, # Partition number in the KS file
142 'size': size, # In sectors
143 'mountpoint': mountpoint, # Mount relative to chroot
144 'fstype': fstype, # Filesystem type
145 'fsopts': fsopts, # Filesystem mount options
146 'label': label, # Partition label
147 'disk_name': disk_name, # physical disk name holding partition
148 'device': None, # kpartx device node for partition
149 'mount': None, # Mount object
150 'num': None, # Partition number
151 'boot': boot, # Bootable flag
152 'align': align, # Partition alignment
153 'part_type' : part_type, # Partition type
154 'partuuid': None } # Partition UUID (GPT-only)
155
156 self.__add_partition(part)
157
158 def layout_partitions(self, ptable_format = "msdos"):
159 """ Layout the partitions, meaning calculate the position of every
160 partition on the disk. The 'ptable_format' parameter defines the
161 partition table format, and may be either "msdos" or "gpt". """
162
163 msger.debug("Assigning %s partitions to disks" % ptable_format)
164
165 if ptable_format not in ('msdos', 'gpt'):
166 raise MountError("Unknown partition table format '%s', supported " \
167 "formats are: 'msdos' and 'gpt'" % ptable_format)
168
169 if self._partitions_layed_out:
170 return
171
172 self._partitions_layed_out = True
173
174 # Go through partitions in the order they are added in .ks file
175 for n in range(len(self.partitions)):
176 p = self.partitions[n]
177
178 if not self.disks.has_key(p['disk_name']):
179 raise MountError("No disk %s for partition %s" \
180 % (p['disk_name'], p['mountpoint']))
181
182 if p['part_type'] and ptable_format != 'gpt':
183 # The --part-type can also be implemented for MBR partitions,
184 # in which case it would map to the 1-byte "partition type"
185 # filed at offset 3 of the partition entry.
186 raise MountError("setting custom partition type is only " \
187 "imlemented for GPT partitions")
188
189 # Get the disk where the partition is located
190 d = self.disks[p['disk_name']]
191 d['numpart'] += 1
192 d['ptable_format'] = ptable_format
193
194 if d['numpart'] == 1:
195 if ptable_format == "msdos":
196 overhead = MBR_OVERHEAD
197 else:
198 overhead = GPT_OVERHEAD
199
200 # Skip one sector required for the partitioning scheme overhead
201 d['offset'] += overhead
202 # Steal few sectors from the first partition to offset for the
203 # partitioning overhead
204 p['size'] -= overhead
205
206 if p['align']:
207 # If not first partition and we do have alignment set we need
208 # to align the partition.
209 # FIXME: This leaves a empty spaces to the disk. To fill the
210 # gaps we could enlargea the previous partition?
211
212 # Calc how much the alignment is off.
213 align_sectors = d['offset'] % (p['align'] * 1024 / self.sector_size)
214 # We need to move forward to the next alignment point
215 align_sectors = (p['align'] * 1024 / self.sector_size) - align_sectors
216
217 msger.debug("Realignment for %s%s with %s sectors, original"
218 " offset %s, target alignment is %sK." %
219 (p['disk_name'], d['numpart'], align_sectors,
220 d['offset'], p['align']))
221
222 # increase the offset so we actually start the partition on right alignment
223 d['offset'] += align_sectors
224
225 p['start'] = d['offset']
226 d['offset'] += p['size']
227
228 p['type'] = 'primary'
229 p['num'] = d['numpart']
230
231 if d['ptable_format'] == "msdos":
232 if d['numpart'] > 2:
233 # Every logical partition requires an additional sector for
234 # the EBR, so steal the last sector from the end of each
235 # partition starting from the 3rd one for the EBR. This
236 # will make sure the logical partitions are aligned
237 # correctly.
238 p['size'] -= 1
239
240 if d['numpart'] > 3:
241 p['type'] = 'logical'
242 p['num'] = d['numpart'] + 1
243
244 d['partitions'].append(n)
245 msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
246 "sectors (%d bytes)." \
247 % (p['mountpoint'], p['disk_name'], p['num'],
248 p['start'], p['start'] + p['size'] - 1,
249 p['size'], p['size'] * self.sector_size))
250
251 # Once all the partitions have been layed out, we can calculate the
252 # minumim disk sizes.
253 for disk_name, d in self.disks.items():
254 d['min_size'] = d['offset']
255 if d['ptable_format'] == 'gpt':
256 # Account for the backup partition table at the end of the disk
257 d['min_size'] += GPT_OVERHEAD
258
259 d['min_size'] *= self.sector_size
260
261 def __run_parted(self, args):
262 """ Run parted with arguments specified in the 'args' list. """
263
264 args.insert(0, self.parted)
265 msger.debug(args)
266
267 rc, out = runner.runtool(args, catch = 3)
268 out = out.strip()
269 if out:
270 msger.debug('"parted" output: %s' % out)
271
272 if rc != 0:
273 # We don't throw exception when return code is not 0, because
274 # parted always fails to reload part table with loop devices. This
275 # prevents us from distinguishing real errors based on return
276 # code.
277 msger.debug("WARNING: parted returned '%s' instead of 0" % rc)
278
279 def __create_partition(self, device, parttype, fstype, start, size):
280 """ Create a partition on an image described by the 'device' object. """
281
282 # Start is included to the size so we need to substract one from the end.
283 end = start + size - 1
284 msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" %
285 (parttype, start, end, size))
286
287 args = ["-s", device, "unit", "s", "mkpart", parttype]
288 if fstype:
289 args.extend([fstype])
290 args.extend(["%d" % start, "%d" % end])
291
292 return self.__run_parted(args)
293
294 def __format_disks(self):
295 self.layout_partitions()
296
297 if self.skipformat:
298 msger.debug("Skipping disk format, because skipformat flag is set.")
299 return
300
301 for dev in self.disks.keys():
302 d = self.disks[dev]
303 msger.debug("Initializing partition table for %s" % \
304 (d['disk'].device))
305 self.__run_parted(["-s", d['disk'].device, "mklabel",
306 d['ptable_format']])
307
308 msger.debug("Creating partitions")
309
310 for p in self.partitions:
311 d = self.disks[p['disk_name']]
312 if d['ptable_format'] == "msdos" and p['num'] == 5:
313 # The last sector of the 3rd partition was reserved for the EBR
314 # of the first _logical_ partition. This is why the extended
315 # partition should start one sector before the first logical
316 # partition.
317 self.__create_partition(d['disk'].device, "extended",
318 None, p['start'] - 1,
319 d['offset'] - p['start'])
320
321 if p['fstype'] == "swap":
322 parted_fs_type = "linux-swap"
323 elif p['fstype'] == "vfat":
324 parted_fs_type = "fat32"
325 elif p['fstype'] == "msdos":
326 parted_fs_type = "fat16"
327 else:
328 # Type for ext2/ext3/ext4/btrfs
329 parted_fs_type = "ext2"
330
331 # Boot ROM of OMAP boards require vfat boot partition to have an
332 # even number of sectors.
333 if p['mountpoint'] == "/boot" and p['fstype'] in ["vfat", "msdos"] \
334 and p['size'] % 2:
335 msger.debug("Substracting one sector from '%s' partition to " \
336 "get even number of sectors for the partition" % \
337 p['mountpoint'])
338 p['size'] -= 1
339
340 self.__create_partition(d['disk'].device, p['type'],
341 parted_fs_type, p['start'], p['size'])
342
343 if p['boot']:
344 if d['ptable_format'] == 'gpt':
345 flag_name = "legacy_boot"
346 else:
347 flag_name = "boot"
348 msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \
349 (flag_name, p['num'], d['disk'].device))
350 self.__run_parted(["-s", d['disk'].device, "set",
351 "%d" % p['num'], flag_name, "on"])
352
353 # If the partition table format is "gpt", find out PARTUUIDs for all
354 # the partitions. And if users specified custom parition type UUIDs,
355 # set them.
356 for disk_name, disk in self.disks.items():
357 if disk['ptable_format'] != 'gpt':
358 continue
359
360 pnum = 0
361 gpt_parser = GptParser(d['disk'].device, SECTOR_SIZE)
362 # Iterate over all GPT partitions on this disk
363 for entry in gpt_parser.get_partitions():
364 pnum += 1
365 # Find the matching partition in the 'self.partitions' list
366 for n in d['partitions']:
367 p = self.partitions[n]
368 if p['num'] == pnum:
369 # Found, fetch PARTUUID (partition's unique ID)
370 p['partuuid'] = entry['part_uuid']
371 msger.debug("PARTUUID for partition %d on disk '%s' " \
372 "(mount point '%s') is '%s'" % (pnum, \
373 disk_name, p['mountpoint'], p['partuuid']))
374 if p['part_type']:
375 entry['type_uuid'] = p['part_type']
376 msger.debug("Change type of partition %d on disk " \
377 "'%s' (mount point '%s') to '%s'" % \
378 (pnum, disk_name, p['mountpoint'],
379 p['part_type']))
380 gpt_parser.change_partition(entry)
381
382 del gpt_parser
383
384 def __map_partitions(self):
385 """Load it if dm_snapshot isn't loaded. """
386 load_module("dm_snapshot")
387
388 for dev in self.disks.keys():
389 d = self.disks[dev]
390 if d['mapped']:
391 continue
392
393 msger.debug("Running kpartx on %s" % d['disk'].device )
394 rc, kpartxOutput = runner.runtool([self.kpartx, "-l", "-v", d['disk'].device])
395 kpartxOutput = kpartxOutput.splitlines()
396
397 if rc != 0:
398 raise MountError("Failed to query partition mapping for '%s'" %
399 d['disk'].device)
400
401 # Strip trailing blank and mask verbose output
402 i = 0
403 while i < len(kpartxOutput) and kpartxOutput[i][0:4] != "loop":
404 i = i + 1
405 kpartxOutput = kpartxOutput[i:]
406
407 # Make sure kpartx reported the right count of partitions
408 if len(kpartxOutput) != d['numpart']:
409 # If this disk has more than 3 partitions, then in case of MBR
410 # paritions there is an extended parition. Different versions
411 # of kpartx behave differently WRT the extended partition -
412 # some map it, some ignore it. This is why we do the below hack
413 # - if kpartx reported one more partition and the partition
414 # table type is "msdos" and the amount of partitions is more
415 # than 3, we just assume kpartx mapped the extended parition
416 # and we remove it.
417 if len(kpartxOutput) == d['numpart'] + 1 \
418 and d['ptable_format'] == 'msdos' and len(kpartxOutput) > 3:
419 kpartxOutput.pop(3)
420 else:
421 raise MountError("Unexpected number of partitions from " \
422 "kpartx: %d != %d" % \
423 (len(kpartxOutput), d['numpart']))
424
425 for i in range(len(kpartxOutput)):
426 line = kpartxOutput[i]
427 newdev = line.split()[0]
428 mapperdev = "/dev/mapper/" + newdev
429 loopdev = d['disk'].device + newdev[-1]
430
431 msger.debug("Dev %s: %s -> %s" % (newdev, loopdev, mapperdev))
432 pnum = d['partitions'][i]
433 self.partitions[pnum]['device'] = loopdev
434
435 # grub's install wants partitions to be named
436 # to match their parent device + partition num
437 # kpartx doesn't work like this, so we add compat
438 # symlinks to point to /dev/mapper
439 if os.path.lexists(loopdev):
440 os.unlink(loopdev)
441 os.symlink(mapperdev, loopdev)
442
443 msger.debug("Adding partx mapping for %s" % d['disk'].device)
444 rc = runner.show([self.kpartx, "-v", "-a", d['disk'].device])
445
446 if rc != 0:
447 # Make sure that the device maps are also removed on error case.
448 # The d['mapped'] isn't set to True if the kpartx fails so
449 # failed mapping will not be cleaned on cleanup either.
450 runner.quiet([self.kpartx, "-d", d['disk'].device])
451 raise MountError("Failed to map partitions for '%s'" %
452 d['disk'].device)
453
454 # FIXME: there is a bit delay for multipath device setup,
455 # wait 10ms for the setup
456 import time
457 time.sleep(10)
458 d['mapped'] = True
459
460 def __unmap_partitions(self):
461 for dev in self.disks.keys():
462 d = self.disks[dev]
463 if not d['mapped']:
464 continue
465
466 msger.debug("Removing compat symlinks")
467 for pnum in d['partitions']:
468 if self.partitions[pnum]['device'] != None:
469 os.unlink(self.partitions[pnum]['device'])
470 self.partitions[pnum]['device'] = None
471
472 msger.debug("Unmapping %s" % d['disk'].device)
473 rc = runner.quiet([self.kpartx, "-d", d['disk'].device])
474 if rc != 0:
475 raise MountError("Failed to unmap partitions for '%s'" %
476 d['disk'].device)
477
478 d['mapped'] = False
479
480 def __calculate_mountorder(self):
481 msger.debug("Calculating mount order")
482 for p in self.partitions:
483 if p['mountpoint']:
484 self.mountOrder.append(p['mountpoint'])
485 self.unmountOrder.append(p['mountpoint'])
486
487 self.mountOrder.sort()
488 self.unmountOrder.sort()
489 self.unmountOrder.reverse()
490
491 def cleanup(self):
492 Mount.cleanup(self)
493 if self.disks:
494 self.__unmap_partitions()
495 for dev in self.disks.keys():
496 d = self.disks[dev]
497 try:
498 d['disk'].cleanup()
499 except:
500 pass
501
502 def unmount(self):
503 self.__unmount_subvolumes()
504 for mp in self.unmountOrder:
505 if mp == 'swap':
506 continue
507 p = None
508 for p1 in self.partitions:
509 if p1['mountpoint'] == mp:
510 p = p1
511 break
512
513 if p['mount'] != None:
514 try:
515 # Create subvolume snapshot here
516 if p['fstype'] == "btrfs" and p['mountpoint'] == "/" and not self.snapshot_created:
517 self.__create_subvolume_snapshots(p, p["mount"])
518 p['mount'].cleanup()
519 except:
520 pass
521 p['mount'] = None
522
523 # Only for btrfs
524 def __get_subvolume_id(self, rootpath, subvol):
525 if not self.btrfscmd:
526 self.btrfscmd=find_binary_path("btrfs")
527 argv = [ self.btrfscmd, "subvolume", "list", rootpath ]
528
529 rc, out = runner.runtool(argv)
530 msger.debug(out)
531
532 if rc != 0:
533 raise MountError("Failed to get subvolume id from %s', return code: %d." % (rootpath, rc))
534
535 subvolid = -1
536 for line in out.splitlines():
537 if line.endswith(" path %s" % subvol):
538 subvolid = line.split()[1]
539 if not subvolid.isdigit():
540 raise MountError("Invalid subvolume id: %s" % subvolid)
541 subvolid = int(subvolid)
542 break
543 return subvolid
544
545 def __create_subvolume_metadata(self, p, pdisk):
546 if len(self.subvolumes) == 0:
547 return
548
549 argv = [ self.btrfscmd, "subvolume", "list", pdisk.mountdir ]
550 rc, out = runner.runtool(argv)
551 msger.debug(out)
552
553 if rc != 0:
554 raise MountError("Failed to get subvolume id from %s', return code: %d." % (pdisk.mountdir, rc))
555
556 subvolid_items = out.splitlines()
557 subvolume_metadata = ""
558 for subvol in self.subvolumes:
559 for line in subvolid_items:
560 if line.endswith(" path %s" % subvol["subvol"]):
561 subvolid = line.split()[1]
562 if not subvolid.isdigit():
563 raise MountError("Invalid subvolume id: %s" % subvolid)
564
565 subvolid = int(subvolid)
566 opts = subvol["fsopts"].split(",")
567 for opt in opts:
568 if opt.strip().startswith("subvol="):
569 opts.remove(opt)
570 break
571 fsopts = ",".join(opts)
572 subvolume_metadata += "%d\t%s\t%s\t%s\n" % (subvolid, subvol["subvol"], subvol['mountpoint'], fsopts)
573
574 if subvolume_metadata:
575 fd = open("%s/.subvolume_metadata" % pdisk.mountdir, "w")
576 fd.write(subvolume_metadata)
577 fd.close()
578
579 def __get_subvolume_metadata(self, p, pdisk):
580 subvolume_metadata_file = "%s/.subvolume_metadata" % pdisk.mountdir
581 if not os.path.exists(subvolume_metadata_file):
582 return
583
584 fd = open(subvolume_metadata_file, "r")
585 content = fd.read()
586 fd.close()
587
588 for line in content.splitlines():
589 items = line.split("\t")
590 if items and len(items) == 4:
591 self.subvolumes.append({'size': 0, # In sectors
592 'mountpoint': items[2], # Mount relative to chroot
593 'fstype': "btrfs", # Filesystem type
594 'fsopts': items[3] + ",subvol=%s" % items[1], # Filesystem mount options
595 'disk_name': p['disk_name'], # physical disk name holding partition
596 'device': None, # kpartx device node for partition
597 'mount': None, # Mount object
598 'subvol': items[1], # Subvolume name
599 'boot': False, # Bootable flag
600 'mounted': False # Mount flag
601 })
602
603 def __create_subvolumes(self, p, pdisk):
604 """ Create all the subvolumes. """
605
606 for subvol in self.subvolumes:
607 argv = [ self.btrfscmd, "subvolume", "create", pdisk.mountdir + "/" + subvol["subvol"]]
608
609 rc = runner.show(argv)
610 if rc != 0:
611 raise MountError("Failed to create subvolume '%s', return code: %d." % (subvol["subvol"], rc))
612
613 # Set default subvolume, subvolume for "/" is default
614 subvol = None
615 for subvolume in self.subvolumes:
616 if subvolume["mountpoint"] == "/" and p['disk_name'] == subvolume['disk_name']:
617 subvol = subvolume
618 break
619
620 if subvol:
621 # Get default subvolume id
622 subvolid = self. __get_subvolume_id(pdisk.mountdir, subvol["subvol"])
623 # Set default subvolume
624 if subvolid != -1:
625 rc = runner.show([ self.btrfscmd, "subvolume", "set-default", "%d" % subvolid, pdisk.mountdir])
626 if rc != 0:
627 raise MountError("Failed to set default subvolume id: %d', return code: %d." % (subvolid, rc))
628
629 self.__create_subvolume_metadata(p, pdisk)
630
631 def __mount_subvolumes(self, p, pdisk):
632 if self.skipformat:
633 # Get subvolume info
634 self.__get_subvolume_metadata(p, pdisk)
635 # Set default mount options
636 if len(self.subvolumes) != 0:
637 for subvol in self.subvolumes:
638 if subvol["mountpoint"] == p["mountpoint"] == "/":
639 opts = subvol["fsopts"].split(",")
640 for opt in opts:
641 if opt.strip().startswith("subvol="):
642 opts.remove(opt)
643 break
644 pdisk.fsopts = ",".join(opts)
645 break
646
647 if len(self.subvolumes) == 0:
648 # Return directly if no subvolumes
649 return
650
651 # Remount to make default subvolume mounted
652 rc = runner.show([self.umountcmd, pdisk.mountdir])
653 if rc != 0:
654 raise MountError("Failed to umount %s" % pdisk.mountdir)
655
656 rc = runner.show([self.mountcmd, "-o", pdisk.fsopts, pdisk.disk.device, pdisk.mountdir])
657 if rc != 0:
658 raise MountError("Failed to umount %s" % pdisk.mountdir)
659
660 for subvol in self.subvolumes:
661 if subvol["mountpoint"] == "/":
662 continue
663 subvolid = self. __get_subvolume_id(pdisk.mountdir, subvol["subvol"])
664 if subvolid == -1:
665 msger.debug("WARNING: invalid subvolume %s" % subvol["subvol"])
666 continue
667 # Replace subvolume name with subvolume ID
668 opts = subvol["fsopts"].split(",")
669 for opt in opts:
670 if opt.strip().startswith("subvol="):
671 opts.remove(opt)
672 break
673
674 opts.extend(["subvolrootid=0", "subvol=%s" % subvol["subvol"]])
675 fsopts = ",".join(opts)
676 subvol['fsopts'] = fsopts
677 mountpoint = self.mountdir + subvol['mountpoint']
678 makedirs(mountpoint)
679 rc = runner.show([self.mountcmd, "-o", fsopts, pdisk.disk.device, mountpoint])
680 if rc != 0:
681 raise MountError("Failed to mount subvolume %s to %s" % (subvol["subvol"], mountpoint))
682 subvol["mounted"] = True
683
684 def __unmount_subvolumes(self):
685 """ It may be called multiple times, so we need to chekc if it is still mounted. """
686 for subvol in self.subvolumes:
687 if subvol["mountpoint"] == "/":
688 continue
689 if not subvol["mounted"]:
690 continue
691 mountpoint = self.mountdir + subvol['mountpoint']
692 rc = runner.show([self.umountcmd, mountpoint])
693 if rc != 0:
694 raise MountError("Failed to unmount subvolume %s from %s" % (subvol["subvol"], mountpoint))
695 subvol["mounted"] = False
696
697 def __create_subvolume_snapshots(self, p, pdisk):
698 import time
699
700 if self.snapshot_created:
701 return
702
703 # Remount with subvolid=0
704 rc = runner.show([self.umountcmd, pdisk.mountdir])
705 if rc != 0:
706 raise MountError("Failed to umount %s" % pdisk.mountdir)
707 if pdisk.fsopts:
708 mountopts = pdisk.fsopts + ",subvolid=0"
709 else:
710 mountopts = "subvolid=0"
711 rc = runner.show([self.mountcmd, "-o", mountopts, pdisk.disk.device, pdisk.mountdir])
712 if rc != 0:
713 raise MountError("Failed to umount %s" % pdisk.mountdir)
714
715 # Create all the subvolume snapshots
716 snapshotts = time.strftime("%Y%m%d-%H%M")
717 for subvol in self.subvolumes:
718 subvolpath = pdisk.mountdir + "/" + subvol["subvol"]
719 snapshotpath = subvolpath + "_%s-1" % snapshotts
720 rc = runner.show([ self.btrfscmd, "subvolume", "snapshot", subvolpath, snapshotpath ])
721 if rc != 0:
722 raise MountError("Failed to create subvolume snapshot '%s' for '%s', return code: %d." % (snapshotpath, subvolpath, rc))
723
724 self.snapshot_created = True
725
726 def mount(self):
727 for dev in self.disks.keys():
728 d = self.disks[dev]
729 d['disk'].create()
730
731 self.__format_disks()
732 self.__map_partitions()
733 self.__calculate_mountorder()
734
735 for mp in self.mountOrder:
736 p = None
737 for p1 in self.partitions:
738 if p1['mountpoint'] == mp:
739 p = p1
740 break
741
742 if not p['label']:
743 if p['mountpoint'] == "/":
744 p['label'] = 'platform'
745 else:
746 p['label'] = mp.split('/')[-1]
747
748 if mp == 'swap':
749 import uuid
750 p['uuid'] = str(uuid.uuid1())
751 runner.show([self.mkswap,
752 '-L', p['label'],
753 '-U', p['uuid'],
754 p['device']])
755 continue
756
757 rmmountdir = False
758 if p['mountpoint'] == "/":
759 rmmountdir = True
760 if p['fstype'] == "vfat" or p['fstype'] == "msdos":
761 myDiskMount = VfatDiskMount
762 elif p['fstype'] in ("ext2", "ext3", "ext4"):
763 myDiskMount = ExtDiskMount
764 elif p['fstype'] == "btrfs":
765 myDiskMount = BtrfsDiskMount
766 else:
767 raise MountError("Fail to support file system " + p['fstype'])
768
769 if p['fstype'] == "btrfs" and not p['fsopts']:
770 p['fsopts'] = "subvolid=0"
771
772 pdisk = myDiskMount(RawDisk(p['size'] * self.sector_size, p['device']),
773 self.mountdir + p['mountpoint'],
774 p['fstype'],
775 4096,
776 p['label'],
777 rmmountdir,
778 self.skipformat,
779 fsopts = p['fsopts'])
780 pdisk.mount(pdisk.fsopts)
781 if p['fstype'] == "btrfs" and p['mountpoint'] == "/":
782 if not self.skipformat:
783 self.__create_subvolumes(p, pdisk)
784 self.__mount_subvolumes(p, pdisk)
785 p['mount'] = pdisk
786 p['uuid'] = pdisk.uuid
787
788 def resparse(self, size = None):
789 # Can't re-sparse a disk image - too hard
790 pass
diff --git a/scripts/lib/mic/utils/proxy.py b/scripts/lib/mic/utils/proxy.py
new file mode 100644
index 0000000000..91451a2d01
--- /dev/null
+++ b/scripts/lib/mic/utils/proxy.py
@@ -0,0 +1,183 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2010, 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import urlparse
20
21_my_proxies = {}
22_my_noproxy = None
23_my_noproxy_list = []
24
25def set_proxy_environ():
26 global _my_noproxy, _my_proxies
27 if not _my_proxies:
28 return
29 for key in _my_proxies.keys():
30 os.environ[key + "_proxy"] = _my_proxies[key]
31 if not _my_noproxy:
32 return
33 os.environ["no_proxy"] = _my_noproxy
34
35def unset_proxy_environ():
36 for env in ('http_proxy',
37 'https_proxy',
38 'ftp_proxy',
39 'all_proxy'):
40 if env in os.environ:
41 del os.environ[env]
42
43 ENV=env.upper()
44 if ENV in os.environ:
45 del os.environ[ENV]
46
47def _set_proxies(proxy = None, no_proxy = None):
48 """Return a dictionary of scheme -> proxy server URL mappings.
49 """
50
51 global _my_noproxy, _my_proxies
52 _my_proxies = {}
53 _my_noproxy = None
54 proxies = []
55 if proxy:
56 proxies.append(("http_proxy", proxy))
57 if no_proxy:
58 proxies.append(("no_proxy", no_proxy))
59
60 # Get proxy settings from environment if not provided
61 if not proxy and not no_proxy:
62 proxies = os.environ.items()
63
64 # Remove proxy env variables, urllib2 can't handle them correctly
65 unset_proxy_environ()
66
67 for name, value in proxies:
68 name = name.lower()
69 if value and name[-6:] == '_proxy':
70 if name[0:2] != "no":
71 _my_proxies[name[:-6]] = value
72 else:
73 _my_noproxy = value
74
75def _ip_to_int(ip):
76 ipint=0
77 shift=24
78 for dec in ip.split("."):
79 ipint |= int(dec) << shift
80 shift -= 8
81 return ipint
82
83def _int_to_ip(val):
84 ipaddr=""
85 shift=0
86 for i in range(4):
87 dec = val >> shift
88 dec &= 0xff
89 ipaddr = ".%d%s" % (dec, ipaddr)
90 shift += 8
91 return ipaddr[1:]
92
93def _isip(host):
94 if host.replace(".", "").isdigit():
95 return True
96 return False
97
98def _set_noproxy_list():
99 global _my_noproxy, _my_noproxy_list
100 _my_noproxy_list = []
101 if not _my_noproxy:
102 return
103 for item in _my_noproxy.split(","):
104 item = item.strip()
105 if not item:
106 continue
107
108 if item[0] != '.' and item.find("/") == -1:
109 # Need to match it
110 _my_noproxy_list.append({"match":0,"needle":item})
111
112 elif item[0] == '.':
113 # Need to match at tail
114 _my_noproxy_list.append({"match":1,"needle":item})
115
116 elif item.find("/") > 3:
117 # IP/MASK, need to match at head
118 needle = item[0:item.find("/")].strip()
119 ip = _ip_to_int(needle)
120 netmask = 0
121 mask = item[item.find("/")+1:].strip()
122
123 if mask.isdigit():
124 netmask = int(mask)
125 netmask = ~((1<<(32-netmask)) - 1)
126 ip &= netmask
127 else:
128 shift=24
129 netmask=0
130 for dec in mask.split("."):
131 netmask |= int(dec) << shift
132 shift -= 8
133 ip &= netmask
134
135 _my_noproxy_list.append({"match":2,"needle":ip,"netmask":netmask})
136
137def _isnoproxy(url):
138 (scheme, host, path, parm, query, frag) = urlparse.urlparse(url)
139
140 if '@' in host:
141 user_pass, host = host.split('@', 1)
142
143 if ':' in host:
144 host, port = host.split(':', 1)
145
146 hostisip = _isip(host)
147 for item in _my_noproxy_list:
148 if hostisip and item["match"] <= 1:
149 continue
150
151 if item["match"] == 2 and hostisip:
152 if (_ip_to_int(host) & item["netmask"]) == item["needle"]:
153 return True
154
155 if item["match"] == 0:
156 if host == item["needle"]:
157 return True
158
159 if item["match"] == 1:
160 if host.rfind(item["needle"]) > 0:
161 return True
162
163 return False
164
165def set_proxies(proxy = None, no_proxy = None):
166 _set_proxies(proxy, no_proxy)
167 _set_noproxy_list()
168 set_proxy_environ()
169
170def get_proxy_for(url):
171 if url.startswith('file:') or _isnoproxy(url):
172 return None
173
174 type = url[0:url.index(":")]
175 proxy = None
176 if _my_proxies.has_key(type):
177 proxy = _my_proxies[type]
178 elif _my_proxies.has_key("http"):
179 proxy = _my_proxies["http"]
180 else:
181 proxy = None
182
183 return proxy
diff --git a/scripts/lib/mic/utils/rpmmisc.py b/scripts/lib/mic/utils/rpmmisc.py
new file mode 100644
index 0000000000..af15763e18
--- /dev/null
+++ b/scripts/lib/mic/utils/rpmmisc.py
@@ -0,0 +1,600 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2008, 2009, 2010, 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import sys
20import re
21import rpm
22
23from mic import msger
24from mic.utils.errors import CreatorError
25from mic.utils.proxy import get_proxy_for
26from mic.utils import runner
27
28
29class RPMInstallCallback:
30 """ Command line callback class for callbacks from the RPM library.
31 """
32
33 def __init__(self, ts, output=1):
34 self.output = output
35 self.callbackfilehandles = {}
36 self.total_actions = 0
37 self.total_installed = 0
38 self.installed_pkg_names = []
39 self.total_removed = 0
40 self.mark = "+"
41 self.marks = 40
42 self.lastmsg = None
43 self.tsInfo = None # this needs to be set for anything else to work
44 self.ts = ts
45 self.filelog = False
46 self.logString = []
47 self.headmsg = "Installing"
48
49 def _dopkgtup(self, hdr):
50 tmpepoch = hdr['epoch']
51 if tmpepoch is None: epoch = '0'
52 else: epoch = str(tmpepoch)
53
54 return (hdr['name'], hdr['arch'], epoch, hdr['version'], hdr['release'])
55
56 def _makeHandle(self, hdr):
57 handle = '%s:%s.%s-%s-%s' % (hdr['epoch'], hdr['name'], hdr['version'],
58 hdr['release'], hdr['arch'])
59
60 return handle
61
62 def _localprint(self, msg):
63 if self.output:
64 msger.info(msg)
65
66 def _makefmt(self, percent, progress = True):
67 l = len(str(self.total_actions))
68 size = "%s.%s" % (l, l)
69 fmt_done = "[%" + size + "s/%" + size + "s]"
70 done = fmt_done % (self.total_installed + self.total_removed,
71 self.total_actions)
72 marks = self.marks - (2 * l)
73 width = "%s.%s" % (marks, marks)
74 fmt_bar = "%-" + width + "s"
75 if progress:
76 bar = fmt_bar % (self.mark * int(marks * (percent / 100.0)), )
77 fmt = "\r %-10.10s: %-20.20s " + bar + " " + done
78 else:
79 bar = fmt_bar % (self.mark * marks, )
80 fmt = " %-10.10s: %-20.20s " + bar + " " + done
81 return fmt
82
83 def _logPkgString(self, hdr):
84 """return nice representation of the package for the log"""
85 (n,a,e,v,r) = self._dopkgtup(hdr)
86 if e == '0':
87 pkg = '%s.%s %s-%s' % (n, a, v, r)
88 else:
89 pkg = '%s.%s %s:%s-%s' % (n, a, e, v, r)
90
91 return pkg
92
93 def callback(self, what, bytes, total, h, user):
94 if what == rpm.RPMCALLBACK_TRANS_START:
95 if bytes == 6:
96 self.total_actions = total
97
98 elif what == rpm.RPMCALLBACK_TRANS_PROGRESS:
99 pass
100
101 elif what == rpm.RPMCALLBACK_TRANS_STOP:
102 pass
103
104 elif what == rpm.RPMCALLBACK_INST_OPEN_FILE:
105 self.lastmsg = None
106 hdr = None
107 if h is not None:
108 try:
109 hdr, rpmloc = h
110 except:
111 rpmloc = h
112 hdr = readRpmHeader(self.ts, h)
113
114 handle = self._makeHandle(hdr)
115 fd = os.open(rpmloc, os.O_RDONLY)
116 self.callbackfilehandles[handle]=fd
117 if hdr['name'] not in self.installed_pkg_names:
118 self.installed_pkg_names.append(hdr['name'])
119 self.total_installed += 1
120 return fd
121 else:
122 self._localprint("No header - huh?")
123
124 elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE:
125 hdr = None
126 if h is not None:
127 try:
128 hdr, rpmloc = h
129 except:
130 rpmloc = h
131 hdr = readRpmHeader(self.ts, h)
132
133 handle = self._makeHandle(hdr)
134 os.close(self.callbackfilehandles[handle])
135 fd = 0
136
137 # log stuff
138 #pkgtup = self._dopkgtup(hdr)
139 self.logString.append(self._logPkgString(hdr))
140
141 elif what == rpm.RPMCALLBACK_INST_PROGRESS:
142 if h is not None:
143 percent = (self.total_installed*100L)/self.total_actions
144 if total > 0:
145 try:
146 hdr, rpmloc = h
147 except:
148 rpmloc = h
149
150 m = re.match("(.*)-(\d+.*)-(\d+\.\d+)\.(.+)\.rpm", os.path.basename(rpmloc))
151 if m:
152 pkgname = m.group(1)
153 else:
154 pkgname = os.path.basename(rpmloc)
155 if self.output:
156 fmt = self._makefmt(percent)
157 msg = fmt % (self.headmsg, pkgname)
158 if msg != self.lastmsg:
159 self.lastmsg = msg
160
161 msger.info(msg)
162
163 if self.total_installed == self.total_actions:
164 msger.raw('')
165 msger.verbose('\n'.join(self.logString))
166
167 elif what == rpm.RPMCALLBACK_UNINST_START:
168 pass
169
170 elif what == rpm.RPMCALLBACK_UNINST_PROGRESS:
171 pass
172
173 elif what == rpm.RPMCALLBACK_UNINST_STOP:
174 self.total_removed += 1
175
176 elif what == rpm.RPMCALLBACK_REPACKAGE_START:
177 pass
178
179 elif what == rpm.RPMCALLBACK_REPACKAGE_STOP:
180 pass
181
182 elif what == rpm.RPMCALLBACK_REPACKAGE_PROGRESS:
183 pass
184
185def readRpmHeader(ts, filename):
186 """ Read an rpm header. """
187
188 fd = os.open(filename, os.O_RDONLY)
189 h = ts.hdrFromFdno(fd)
190 os.close(fd)
191 return h
192
193def splitFilename(filename):
194 """ Pass in a standard style rpm fullname
195
196 Return a name, version, release, epoch, arch, e.g.::
197 foo-1.0-1.i386.rpm returns foo, 1.0, 1, i386
198 1:bar-9-123a.ia64.rpm returns bar, 9, 123a, 1, ia64
199 """
200
201 if filename[-4:] == '.rpm':
202 filename = filename[:-4]
203
204 archIndex = filename.rfind('.')
205 arch = filename[archIndex+1:]
206
207 relIndex = filename[:archIndex].rfind('-')
208 rel = filename[relIndex+1:archIndex]
209
210 verIndex = filename[:relIndex].rfind('-')
211 ver = filename[verIndex+1:relIndex]
212
213 epochIndex = filename.find(':')
214 if epochIndex == -1:
215 epoch = ''
216 else:
217 epoch = filename[:epochIndex]
218
219 name = filename[epochIndex + 1:verIndex]
220 return name, ver, rel, epoch, arch
221
222def getCanonX86Arch(arch):
223 #
224 if arch == "i586":
225 f = open("/proc/cpuinfo", "r")
226 lines = f.readlines()
227 f.close()
228 for line in lines:
229 if line.startswith("model name") and line.find("Geode(TM)") != -1:
230 return "geode"
231 return arch
232 # only athlon vs i686 isn't handled with uname currently
233 if arch != "i686":
234 return arch
235
236 # if we're i686 and AuthenticAMD, then we should be an athlon
237 f = open("/proc/cpuinfo", "r")
238 lines = f.readlines()
239 f.close()
240 for line in lines:
241 if line.startswith("vendor") and line.find("AuthenticAMD") != -1:
242 return "athlon"
243 # i686 doesn't guarantee cmov, but we depend on it
244 elif line.startswith("flags") and line.find("cmov") == -1:
245 return "i586"
246
247 return arch
248
249def getCanonX86_64Arch(arch):
250 if arch != "x86_64":
251 return arch
252
253 vendor = None
254 f = open("/proc/cpuinfo", "r")
255 lines = f.readlines()
256 f.close()
257 for line in lines:
258 if line.startswith("vendor_id"):
259 vendor = line.split(':')[1]
260 break
261 if vendor is None:
262 return arch
263
264 if vendor.find("Authentic AMD") != -1 or vendor.find("AuthenticAMD") != -1:
265 return "amd64"
266 if vendor.find("GenuineIntel") != -1:
267 return "ia32e"
268 return arch
269
270def getCanonArch():
271 arch = os.uname()[4]
272
273 if (len(arch) == 4 and arch[0] == "i" and arch[2:4] == "86"):
274 return getCanonX86Arch(arch)
275
276 if arch == "x86_64":
277 return getCanonX86_64Arch(arch)
278
279 return arch
280
281# Copy from libsatsolver:poolarch.c, with cleanup
282archPolicies = {
283 "x86_64": "x86_64:i686:i586:i486:i386",
284 "i686": "i686:i586:i486:i386",
285 "i586": "i586:i486:i386",
286 "ia64": "ia64:i686:i586:i486:i386",
287 "armv7tnhl": "armv7tnhl:armv7thl:armv7nhl:armv7hl",
288 "armv7thl": "armv7thl:armv7hl",
289 "armv7nhl": "armv7nhl:armv7hl",
290 "armv7hl": "armv7hl",
291 "armv7l": "armv7l:armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
292 "armv6l": "armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
293 "armv5tejl": "armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l",
294 "armv5tel": "armv5tel:armv5l:armv4tl:armv4l:armv3l",
295 "armv5l": "armv5l:armv4tl:armv4l:armv3l",
296}
297
298# dict mapping arch -> ( multicompat, best personality, biarch personality )
299multilibArches = {
300 "x86_64": ( "athlon", "x86_64", "athlon" ),
301}
302
303# from yumUtils.py
304arches = {
305 # ia32
306 "athlon": "i686",
307 "i686": "i586",
308 "geode": "i586",
309 "i586": "i486",
310 "i486": "i386",
311 "i386": "noarch",
312
313 # amd64
314 "x86_64": "athlon",
315 "amd64": "x86_64",
316 "ia32e": "x86_64",
317
318 # arm
319 "armv7tnhl": "armv7nhl",
320 "armv7nhl": "armv7hl",
321 "armv7hl": "noarch",
322 "armv7l": "armv6l",
323 "armv6l": "armv5tejl",
324 "armv5tejl": "armv5tel",
325 "armv5tel": "noarch",
326
327 #itanium
328 "ia64": "noarch",
329}
330
331def isMultiLibArch(arch=None):
332 """returns true if arch is a multilib arch, false if not"""
333 if arch is None:
334 arch = getCanonArch()
335
336 if not arches.has_key(arch): # or we could check if it is noarch
337 return False
338
339 if multilibArches.has_key(arch):
340 return True
341
342 if multilibArches.has_key(arches[arch]):
343 return True
344
345 return False
346
347def getBaseArch():
348 myarch = getCanonArch()
349 if not arches.has_key(myarch):
350 return myarch
351
352 if isMultiLibArch(arch=myarch):
353 if multilibArches.has_key(myarch):
354 return myarch
355 else:
356 return arches[myarch]
357
358 if arches.has_key(myarch):
359 basearch = myarch
360 value = arches[basearch]
361 while value != 'noarch':
362 basearch = value
363 value = arches[basearch]
364
365 return basearch
366
367def checkRpmIntegrity(bin_rpm, package):
368 return runner.quiet([bin_rpm, "-K", "--nosignature", package])
369
370def checkSig(ts, package):
371 """ Takes a transaction set and a package, check it's sigs,
372 return 0 if they are all fine
373 return 1 if the gpg key can't be found
374 return 2 if the header is in someway damaged
375 return 3 if the key is not trusted
376 return 4 if the pkg is not gpg or pgp signed
377 """
378
379 value = 0
380 currentflags = ts.setVSFlags(0)
381 fdno = os.open(package, os.O_RDONLY)
382 try:
383 hdr = ts.hdrFromFdno(fdno)
384
385 except rpm.error, e:
386 if str(e) == "public key not availaiable":
387 value = 1
388 if str(e) == "public key not available":
389 value = 1
390 if str(e) == "public key not trusted":
391 value = 3
392 if str(e) == "error reading package header":
393 value = 2
394 else:
395 error, siginfo = getSigInfo(hdr)
396 if error == 101:
397 os.close(fdno)
398 del hdr
399 value = 4
400 else:
401 del hdr
402
403 try:
404 os.close(fdno)
405 except OSError:
406 pass
407
408 ts.setVSFlags(currentflags) # put things back like they were before
409 return value
410
411def getSigInfo(hdr):
412 """ checks signature from an hdr hand back signature information and/or
413 an error code
414 """
415
416 import locale
417 locale.setlocale(locale.LC_ALL, 'C')
418
419 string = '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|'
420 siginfo = hdr.sprintf(string)
421 if siginfo != '(none)':
422 error = 0
423 sigtype, sigdate, sigid = siginfo.split(',')
424 else:
425 error = 101
426 sigtype = 'MD5'
427 sigdate = 'None'
428 sigid = 'None'
429
430 infotuple = (sigtype, sigdate, sigid)
431 return error, infotuple
432
433def checkRepositoryEULA(name, repo):
434 """ This function is to check the EULA file if provided.
435 return True: no EULA or accepted
436 return False: user declined the EULA
437 """
438
439 import tempfile
440 import shutil
441 import urlparse
442 import urllib2 as u2
443 import httplib
444 from mic.utils.errors import CreatorError
445
446 def _check_and_download_url(u2opener, url, savepath):
447 try:
448 if u2opener:
449 f = u2opener.open(url)
450 else:
451 f = u2.urlopen(url)
452 except u2.HTTPError, httperror:
453 if httperror.code in (404, 503):
454 return None
455 else:
456 raise CreatorError(httperror)
457 except OSError, oserr:
458 if oserr.errno == 2:
459 return None
460 else:
461 raise CreatorError(oserr)
462 except IOError, oserr:
463 if hasattr(oserr, "reason") and oserr.reason.errno == 2:
464 return None
465 else:
466 raise CreatorError(oserr)
467 except u2.URLError, err:
468 raise CreatorError(err)
469 except httplib.HTTPException, e:
470 raise CreatorError(e)
471
472 # save to file
473 licf = open(savepath, "w")
474 licf.write(f.read())
475 licf.close()
476 f.close()
477
478 return savepath
479
480 def _pager_file(savepath):
481
482 if os.path.splitext(savepath)[1].upper() in ('.HTM', '.HTML'):
483 pagers = ('w3m', 'links', 'lynx', 'less', 'more')
484 else:
485 pagers = ('less', 'more')
486
487 file_showed = False
488 for pager in pagers:
489 cmd = "%s %s" % (pager, savepath)
490 try:
491 os.system(cmd)
492 except OSError:
493 continue
494 else:
495 file_showed = True
496 break
497
498 if not file_showed:
499 f = open(savepath)
500 msger.raw(f.read())
501 f.close()
502 msger.pause()
503
504 # when proxy needed, make urllib2 follow it
505 proxy = repo.proxy
506 proxy_username = repo.proxy_username
507 proxy_password = repo.proxy_password
508
509 if not proxy:
510 proxy = get_proxy_for(repo.baseurl[0])
511
512 handlers = []
513 auth_handler = u2.HTTPBasicAuthHandler(u2.HTTPPasswordMgrWithDefaultRealm())
514 u2opener = None
515 if proxy:
516 if proxy_username:
517 proxy_netloc = urlparse.urlsplit(proxy).netloc
518 if proxy_password:
519 proxy_url = 'http://%s:%s@%s' % (proxy_username, proxy_password, proxy_netloc)
520 else:
521 proxy_url = 'http://%s@%s' % (proxy_username, proxy_netloc)
522 else:
523 proxy_url = proxy
524
525 proxy_support = u2.ProxyHandler({'http': proxy_url,
526 'https': proxy_url,
527 'ftp': proxy_url})
528 handlers.append(proxy_support)
529
530 # download all remote files to one temp dir
531 baseurl = None
532 repo_lic_dir = tempfile.mkdtemp(prefix = 'repolic')
533
534 for url in repo.baseurl:
535 tmphandlers = handlers[:]
536
537 (scheme, host, path, parm, query, frag) = urlparse.urlparse(url.rstrip('/') + '/')
538 if scheme not in ("http", "https", "ftp", "ftps", "file"):
539 raise CreatorError("Error: invalid url %s" % url)
540
541 if '@' in host:
542 try:
543 user_pass, host = host.split('@', 1)
544 if ':' in user_pass:
545 user, password = user_pass.split(':', 1)
546 except ValueError, e:
547 raise CreatorError('Bad URL: %s' % url)
548
549 msger.verbose("adding HTTP auth: %s, XXXXXXXX" %(user))
550 auth_handler.add_password(None, host, user, password)
551 tmphandlers.append(auth_handler)
552 url = scheme + "://" + host + path + parm + query + frag
553
554 if tmphandlers:
555 u2opener = u2.build_opener(*tmphandlers)
556
557 # try to download
558 repo_eula_url = urlparse.urljoin(url, "LICENSE.txt")
559 repo_eula_path = _check_and_download_url(
560 u2opener,
561 repo_eula_url,
562 os.path.join(repo_lic_dir, repo.id + '_LICENSE.txt'))
563 if repo_eula_path:
564 # found
565 baseurl = url
566 break
567
568 if not baseurl:
569 shutil.rmtree(repo_lic_dir) #cleanup
570 return True
571
572 # show the license file
573 msger.info('For the software packages in this yum repo:')
574 msger.info(' %s: %s' % (name, baseurl))
575 msger.info('There is an "End User License Agreement" file that need to be checked.')
576 msger.info('Please read the terms and conditions outlined in it and answer the followed qustions.')
577 msger.pause()
578
579 _pager_file(repo_eula_path)
580
581 # Asking for the "Accept/Decline"
582 if not msger.ask('Would you agree to the terms and conditions outlined in the above End User License Agreement?'):
583 msger.warning('Will not install pkgs from this repo.')
584 shutil.rmtree(repo_lic_dir) #cleanup
585 return False
586
587 # try to find support_info.html for extra infomation
588 repo_info_url = urlparse.urljoin(baseurl, "support_info.html")
589 repo_info_path = _check_and_download_url(
590 u2opener,
591 repo_info_url,
592 os.path.join(repo_lic_dir, repo.id + '_support_info.html'))
593 if repo_info_path:
594 msger.info('There is one more file in the repo for additional support information, please read it')
595 msger.pause()
596 _pager_file(repo_info_path)
597
598 #cleanup
599 shutil.rmtree(repo_lic_dir)
600 return True
diff --git a/scripts/lib/mic/utils/runner.py b/scripts/lib/mic/utils/runner.py
new file mode 100644
index 0000000000..fded3c93fa
--- /dev/null
+++ b/scripts/lib/mic/utils/runner.py
@@ -0,0 +1,109 @@
1#!/usr/bin/python -tt
2#
3# Copyright (c) 2011 Intel, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; version 2 of the License
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12# for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc., 59
16# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18import os
19import subprocess
20
21from mic import msger
22
23def runtool(cmdln_or_args, catch=1):
24 """ wrapper for most of the subprocess calls
25 input:
26 cmdln_or_args: can be both args and cmdln str (shell=True)
27 catch: 0, quitely run
28 1, only STDOUT
29 2, only STDERR
30 3, both STDOUT and STDERR
31 return:
32 (rc, output)
33 if catch==0: the output will always None
34 """
35
36 if catch not in (0, 1, 2, 3):
37 # invalid catch selection, will cause exception, that's good
38 return None
39
40 if isinstance(cmdln_or_args, list):
41 cmd = cmdln_or_args[0]
42 shell = False
43 else:
44 import shlex
45 cmd = shlex.split(cmdln_or_args)[0]
46 shell = True
47
48 if catch != 3:
49 dev_null = os.open("/dev/null", os.O_WRONLY)
50
51 if catch == 0:
52 sout = dev_null
53 serr = dev_null
54 elif catch == 1:
55 sout = subprocess.PIPE
56 serr = dev_null
57 elif catch == 2:
58 sout = dev_null
59 serr = subprocess.PIPE
60 elif catch == 3:
61 sout = subprocess.PIPE
62 serr = subprocess.STDOUT
63
64 try:
65 p = subprocess.Popen(cmdln_or_args, stdout=sout,
66 stderr=serr, shell=shell)
67 (sout, serr) = p.communicate()
68 # combine stdout and stderr, filter None out
69 out = ''.join(filter(None, [sout, serr]))
70 except OSError, e:
71 if e.errno == 2:
72 # [Errno 2] No such file or directory
73 msger.error('Cannot run command: %s, lost dependency?' % cmd)
74 else:
75 raise # relay
76 finally:
77 if catch != 3:
78 os.close(dev_null)
79
80 return (p.returncode, out)
81
82def show(cmdln_or_args):
83 # show all the message using msger.verbose
84
85 rc, out = runtool(cmdln_or_args, catch=3)
86
87 if isinstance(cmdln_or_args, list):
88 cmd = ' '.join(cmdln_or_args)
89 else:
90 cmd = cmdln_or_args
91
92 msg = 'running command: "%s"' % cmd
93 if out: out = out.strip()
94 if out:
95 msg += ', with output::'
96 msg += '\n +----------------'
97 for line in out.splitlines():
98 msg += '\n | %s' % line
99 msg += '\n +----------------'
100
101 msger.verbose(msg)
102 return rc
103
104def outs(cmdln_or_args, catch=1):
105 # get the outputs of tools
106 return runtool(cmdln_or_args, catch)[1].strip()
107
108def quiet(cmdln_or_args):
109 return runtool(cmdln_or_args, catch=0)[0]
diff --git a/scripts/yocto-image.conf b/scripts/yocto-image.conf
new file mode 100644
index 0000000000..3d41eff151
--- /dev/null
+++ b/scripts/yocto-image.conf
@@ -0,0 +1,35 @@
1[common]
2; general settings
3distro_name = Tizen
4
5plugin_dir = @PREFIX@/lib/mic/plugins
6
7[create]
8; settings for create subcommand
9tmpdir= /var/tmp/mic
10cachedir= /var/tmp/mic/cache
11outdir= ./mic-output
12runtime=bootstrap
13
14pkgmgr = auto
15
16# to set global proxy for repos
17#proxy = http://proxy.yourcompany.com:8080/
18#no_proxy = localhost,127.0.0.0/8,.yourcompany.com
19
20# prefix will be added in front of generated files
21#name_prefix = output
22
23# to skip all ssl verification for repos
24#ssl_verify = no
25
26[convert]
27; settings for convert subcommand
28
29[chroot]
30; settings for chroot subcommand
31
32[bootstrap]
33rootdir=/var/tmp/mic-bootstrap
34packages=mic-bootstrap-x86-arm
35