blob: ebc23eda63c20459f24910e583d03e07dbdf4a4e [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
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
146def _check_size(option, opt, value):
147 # Former default was MB
148 if value.isdigit():
149 return int(value) * 1024L
150
151 mapping = {"opt": opt, "value": value}
152 if not value[:-1].isdigit():
153 raise OptionValueError(_("Option %(opt)s: invalid size value: %(value)r") % mapping)
154
155 size = int(value[:-1])
156 if value.endswith("k") or value.endswith("K"):
157 return size
158 if value.endswith("M"):
159 return size * 1024L
160 if value.endswith("G"):
161 return size * 1024L * 1024L
162 raise OptionValueError(_("Option %(opt)s: invalid size value: %(value)r") % mapping)
163
164# Creates a new Option class that supports several new attributes:
165# - required: any option with this attribute must be supplied or an exception
166# is thrown
167# - introduced: the kickstart syntax version that this option first appeared
168# in - an exception will be raised if the option is used and
169# the specified syntax version is less than the value of this
170# attribute
171# - deprecated: the kickstart syntax version that this option was deprecated
172# in - a DeprecationWarning will be thrown if the option is
173# used and the specified syntax version is greater than the
174# value of this attribute
175# - removed: the kickstart syntax version that this option was removed in - an
176# exception will be raised if the option is used and the specified
177# syntax version is greated than the value of this attribute
178# Also creates a new type:
179# - ksboolean: support various kinds of boolean values on an option
180# And two new actions:
181# - map : allows you to define an opt -> val mapping such that dest gets val
182# when opt is seen
183# - map_extend: allows you to define an opt -> [val1, ... valn] mapping such
184# that dest gets a list of vals built up when opt is seen
185class KSOption (Option):
186 ATTRS = Option.ATTRS + ['introduced', 'deprecated', 'removed', 'required']
187 ACTIONS = Option.ACTIONS + ("map", "map_extend",)
188 STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",)
189
190 TYPES = Option.TYPES + ("ksboolean", "string", "size")
191 TYPE_CHECKER = copy(Option.TYPE_CHECKER)
192 TYPE_CHECKER["ksboolean"] = _check_ksboolean
193 TYPE_CHECKER["string"] = _check_string
194 TYPE_CHECKER["size"] = _check_size
195
196 def _check_required(self):
197 if self.required and not self.takes_value():
198 raise OptionError(_("Required flag set for option that doesn't take a value"), self)
199
200 # Make sure _check_required() is called from the constructor!
201 CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
202
203 def process (self, opt, value, values, parser):
204 Option.process(self, opt, value, values, parser)
205 parser.option_seen[self] = 1
206
207 # Override default take_action method to handle our custom actions.
208 def take_action(self, action, dest, opt, value, values, parser):
209 if action == "map":
210 values.ensure_value(dest, parser.map[opt.lstrip('-')])
211 elif action == "map_extend":
212 values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
213 else:
214 Option.take_action(self, action, dest, opt, value, values, parser)
215
216 def takes_value(self):
217 # Deprecated options don't take a value.
218 return Option.takes_value(self) and not self.deprecated
219
220 def __init__(self, *args, **kwargs):
221 self.deprecated = False
222 self.required = False
223 Option.__init__(self, *args, **kwargs)