1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
#
# simpleconifg.py - representation of a simple configuration file (sh-like)
#
# Matt Wilson <msw@redhat.com>
# Jeremy Katz <katzj@redhat.com>
# Will Woods <wwoods@redhat.com>
# Brian C. Lane <bcl@redhat.com>
#
# Copyright 1999-2012 Red Hat, Inc.
#
# This software may be freely redistributed under the terms of the GNU
# library public license.
#
# You should have received a copy of the GNU Library Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import os
import shutil
import string
import shlex
from pipes import _safechars
import tempfile
# use our own ASCII only uppercase function to avoid locale issues
# not going to be fast but not important
def uppercase_ASCII_string(s):
newstr = ""
for c in s:
if c in string.lowercase:
newstr += chr(ord(c)-32)
else:
newstr += c
return newstr
def unquote(s):
return ' '.join(shlex.split(s))
def quote(s, always=False):
""" If always is set it returns a quoted value
"""
if not always:
for c in s:
if c not in _safechars:
break
else:
return s
return '"'+s.replace('"', '\\"')+'"'
class SimpleConfigFile(object):
""" Edit values in a configuration file without changing comments.
Supports KEY=VALUE lines and ignores everything else.
Supports adding new keys.
Supports deleting keys.
Preserves comment, blank lines and comments on KEY lines
Does not support duplicate key entries.
"""
def __init__(self, filename=None, read_unquote=True, write_quote=True,
always_quote=False):
self.filename = filename
self.read_unquote = read_unquote
self.write_quote = write_quote
self.always_quote = always_quote
self.reset()
def reset(self):
self._lines = []
self.info = {}
def read(self, filename=None):
""" passing filename will override the filename passed to init.
save the lines into self._lines and the key/value pairs into
self.info
"""
filename = filename or self.filename
with open(filename) as f:
for line in f:
self._lines.append(line)
key, value = self._parseline(line)
if key:
self.info[key] = value
def write(self, filename=None):
""" passing filename will override the filename passed to init.
"""
filename = filename or self.filename
if not filename:
return None
tmpf = tempfile.NamedTemporaryFile(mode="w", delete=False)
tmpf.write(str(self))
tmpf.close()
# Move the temporary file (with 0600 permissions) over the top of the
# original and preserve the original's permissions
filename = os.path.realpath(filename)
if os.path.exists(filename):
m = os.stat(filename).st_mode
else:
m = int('0100644', 8)
shutil.move(tmpf.name, filename)
os.chmod(filename, m)
def set(self, *args):
for key, value in args:
self.info[uppercase_ASCII_string(key)] = value
def unset(self, *keys):
for key in [uppercase_ASCII_string(k) for k in keys]:
if key in self.info:
del self.info[key]
def get(self, key):
return self.info.get(uppercase_ASCII_string(key), "")
def _parseline(self, line):
""" parse a line into a key, value pair
Handle comments and optionally unquote quoted strings
Returns (key, value) or (None, None)
key is always UPPERCASE
"""
s = line.strip()
if '#' in s:
s = s[:s.find('#')] # remove from comment to EOL
s = s.strip() # and any unnecessary whitespace
key, eq, val = s.partition('=')
if self.read_unquote:
val = unquote(val)
if key != '' and eq == '=':
return (uppercase_ASCII_string(key), val)
else:
return (None, None)
def _kvpair(self, key, comment=""):
value = self.info[key]
if self.write_quote or self.always_quote:
value = quote(value, self.always_quote)
return key + '=' + value + comment + "\n"
def __str__(self):
""" Return the file that was read, replacing existing keys with new values
removing keys that have been deleted and adding new keys.
"""
oldkeys = []
s = ""
for line in self._lines:
key, val = self._parseline(line)
if key is None:
s += line
else:
if key not in self.info:
continue
oldkeys.append(key)
if "#" in line:
comment = " " + line[line.find("#"):]
else:
comment = ""
s += self._kvpair(key, comment)
# Add new keys
for key in self.info:
if key not in oldkeys:
s += self._kvpair(key)
return s
class IfcfgFile(SimpleConfigFile):
def __init__(self, dir, iface):
SimpleConfigFile.__init__(self, always_quote=True)
self.iface = iface
self.dir = dir
@property
def path(self):
return os.path.join(self.dir, "ifcfg-%s" % self.iface)
def clear(self):
SimpleConfigFile.reset(self)
def read(self):
""" Reads values from ifcfg file.
returns: number of values read
"""
SimpleConfigFile.read(self, self.path)
return len(self.info)
# ifcfg-rh is using inotify IN_CLOSE_WRITE event
# so we don't use temporary file for new configuration.
def write(self, dir=None):
""" Writes values into ifcfg file.
"""
if not dir:
path = self.path
else:
path = os.path.join(dir, os.path.basename(self.path))
SimpleConfigFile.write(self, path)
|