blob: 0894e2b199a299fbbed272f2e1c95e9d692e3ab1 [file] [log] [blame]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001#!/usr/bin/env python -tt
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4#
5# Copyright (c) 2016 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#
20# DESCRIPTION
21# This module provides parser for kickstart format
22#
23# AUTHORS
24# Ed Bartosh <ed.bartosh> (at] linux.intel.com>
25
26"""Kickstart parser module."""
27
28import os
29import shlex
30from argparse import ArgumentParser, ArgumentError, ArgumentTypeError
31
32from wic import msger
33from wic.partition import Partition
34from wic.utils.misc import find_canned
35
36class KickStartError(Exception):
37 """Custom exception."""
38 pass
39
40class KickStartParser(ArgumentParser):
41 """
42 This class overwrites error method to throw exception
43 instead of producing usage message(default argparse behavior).
44 """
45 def error(self, message):
46 raise ArgumentError(None, message)
47
48def sizetype(arg):
49 """
50 Custom type for ArgumentParser
51 Converts size string in <num>[K|k|M|G] format into the integer value
52 """
53 if arg.isdigit():
Patrick Williamsc0f7c042017-02-23 20:41:17 -060054 return int(arg) * 1024
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050055
56 if not arg[:-1].isdigit():
57 raise ArgumentTypeError("Invalid size: %r" % arg)
58
59 size = int(arg[:-1])
60 if arg.endswith("k") or arg.endswith("K"):
61 return size
62 if arg.endswith("M"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060063 return size * 1024
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050064 if arg.endswith("G"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060065 return size * 1024 * 1024
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050066
67 raise ArgumentTypeError("Invalid size: %r" % arg)
68
69def overheadtype(arg):
70 """
71 Custom type for ArgumentParser
72 Converts overhead string to float and checks if it's bigger than 1.0
73 """
74 try:
75 result = float(arg)
76 except ValueError:
77 raise ArgumentTypeError("Invalid value: %r" % arg)
78
79 if result < 1.0:
80 raise ArgumentTypeError("Overhead factor should be > 1.0" % arg)
81
82 return result
83
84def cannedpathtype(arg):
85 """
86 Custom type for ArgumentParser
87 Tries to find file in the list of canned wks paths
88 """
89 scripts_path = os.path.abspath(os.path.dirname(__file__) + '../../..')
90 result = find_canned(scripts_path, arg)
91 if not result:
92 raise ArgumentTypeError("file not found: %s" % arg)
93 return result
94
Patrick Williamsc0f7c042017-02-23 20:41:17 -060095def systemidtype(arg):
96 """
97 Custom type for ArgumentParser
98 Checks if the argument sutisfies system id requirements,
99 i.e. if it's one byte long integer > 0
100 """
101 error = "Invalid system type: %s. must be hex "\
102 "between 0x1 and 0xFF" % arg
103 try:
104 result = int(arg, 16)
105 except ValueError:
106 raise ArgumentTypeError(error)
107
108 if result <= 0 or result > 0xff:
109 raise ArgumentTypeError(error)
110
111 return arg
112
113class KickStart():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500114 """"Kickstart parser implementation."""
115
116 def __init__(self, confpath):
117
118 self.partitions = []
119 self.bootloader = None
120 self.lineno = 0
121 self.partnum = 0
122
123 parser = KickStartParser()
124 subparsers = parser.add_subparsers()
125
126 part = subparsers.add_parser('part')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600127 part.add_argument('mountpoint', nargs='?')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500128 part.add_argument('--active', action='store_true')
129 part.add_argument('--align', type=int)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600130 part.add_argument("--extra-space", type=sizetype, default=10*1024)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500131 part.add_argument('--fsoptions', dest='fsopts')
132 part.add_argument('--fstype')
133 part.add_argument('--label')
134 part.add_argument('--no-table', action='store_true')
135 part.add_argument('--ondisk', '--ondrive', dest='disk')
136 part.add_argument("--overhead-factor", type=overheadtype, default=1.3)
137 part.add_argument('--part-type')
138 part.add_argument('--rootfs-dir')
139 part.add_argument('--size', type=sizetype, default=0)
140 part.add_argument('--source')
141 part.add_argument('--sourceparams')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600142 part.add_argument('--system-id', type=systemidtype)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500143 part.add_argument('--use-uuid', action='store_true')
144 part.add_argument('--uuid')
145
146 bootloader = subparsers.add_parser('bootloader')
147 bootloader.add_argument('--append')
148 bootloader.add_argument('--configfile')
149 bootloader.add_argument('--ptable', choices=('msdos', 'gpt'),
150 default='msdos')
151 bootloader.add_argument('--timeout', type=int)
152 bootloader.add_argument('--source')
153
154 include = subparsers.add_parser('include')
155 include.add_argument('path', type=cannedpathtype)
156
157 self._parse(parser, confpath)
158 if not self.bootloader:
159 msger.warning('bootloader config not specified, using defaults')
160 self.bootloader = bootloader.parse_args([])
161
162 def _parse(self, parser, confpath):
163 """
164 Parse file in .wks format using provided parser.
165 """
166 with open(confpath) as conf:
167 lineno = 0
168 for line in conf:
169 line = line.strip()
170 lineno += 1
171 if line and line[0] != '#':
172 try:
173 parsed = parser.parse_args(shlex.split(line))
174 except ArgumentError as err:
175 raise KickStartError('%s:%d: %s' % \
176 (confpath, lineno, err))
177 if line.startswith('part'):
178 self.partnum += 1
179 self.partitions.append(Partition(parsed, self.partnum))
180 elif line.startswith('include'):
181 self._parse(parser, parsed.path)
182 elif line.startswith('bootloader'):
183 if not self.bootloader:
184 self.bootloader = parsed
185 else:
186 err = "%s:%d: more than one bootloader specified" \
187 % (confpath, lineno)
188 raise KickStartError(err)