blob: 8c3f80882cbe5c139820eadbdfed0349e436a355 [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():
54 return int(arg) * 1024L
55
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"):
63 return size * 1024L
64 if arg.endswith("G"):
65 return size * 1024L * 1024L
66
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
95class KickStart(object):
96 """"Kickstart parser implementation."""
97
98 def __init__(self, confpath):
99
100 self.partitions = []
101 self.bootloader = None
102 self.lineno = 0
103 self.partnum = 0
104
105 parser = KickStartParser()
106 subparsers = parser.add_subparsers()
107
108 part = subparsers.add_parser('part')
109 part.add_argument('mountpoint')
110 part.add_argument('--active', action='store_true')
111 part.add_argument('--align', type=int)
112 part.add_argument("--extra-space", type=sizetype, default=10*1024L)
113 part.add_argument('--fsoptions', dest='fsopts')
114 part.add_argument('--fstype')
115 part.add_argument('--label')
116 part.add_argument('--no-table', action='store_true')
117 part.add_argument('--ondisk', '--ondrive', dest='disk')
118 part.add_argument("--overhead-factor", type=overheadtype, default=1.3)
119 part.add_argument('--part-type')
120 part.add_argument('--rootfs-dir')
121 part.add_argument('--size', type=sizetype, default=0)
122 part.add_argument('--source')
123 part.add_argument('--sourceparams')
124 part.add_argument('--use-uuid', action='store_true')
125 part.add_argument('--uuid')
126
127 bootloader = subparsers.add_parser('bootloader')
128 bootloader.add_argument('--append')
129 bootloader.add_argument('--configfile')
130 bootloader.add_argument('--ptable', choices=('msdos', 'gpt'),
131 default='msdos')
132 bootloader.add_argument('--timeout', type=int)
133 bootloader.add_argument('--source')
134
135 include = subparsers.add_parser('include')
136 include.add_argument('path', type=cannedpathtype)
137
138 self._parse(parser, confpath)
139 if not self.bootloader:
140 msger.warning('bootloader config not specified, using defaults')
141 self.bootloader = bootloader.parse_args([])
142
143 def _parse(self, parser, confpath):
144 """
145 Parse file in .wks format using provided parser.
146 """
147 with open(confpath) as conf:
148 lineno = 0
149 for line in conf:
150 line = line.strip()
151 lineno += 1
152 if line and line[0] != '#':
153 try:
154 parsed = parser.parse_args(shlex.split(line))
155 except ArgumentError as err:
156 raise KickStartError('%s:%d: %s' % \
157 (confpath, lineno, err))
158 if line.startswith('part'):
159 self.partnum += 1
160 self.partitions.append(Partition(parsed, self.partnum))
161 elif line.startswith('include'):
162 self._parse(parser, parsed.path)
163 elif line.startswith('bootloader'):
164 if not self.bootloader:
165 self.bootloader = parsed
166 else:
167 err = "%s:%d: more than one bootloader specified" \
168 % (confpath, lineno)
169 raise KickStartError(err)