sme-configdb 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +0 -0
- data/README +8 -0
- data/lib/smeserver/configdb.rb +371 -0
- data/test/test.rb +73 -0
- metadata +49 -0
data/ChangeLog
ADDED
File without changes
|
data/README
ADDED
@@ -0,0 +1,371 @@
|
|
1
|
+
# This library provides a simple API for managing SME Server style e-smith
|
2
|
+
# database files.
|
3
|
+
|
4
|
+
DbPrefix = "/home/e-smith/db"
|
5
|
+
DEBUG = true
|
6
|
+
|
7
|
+
# = ConfigDBError
|
8
|
+
# The base class for all errors produced by this API.
|
9
|
+
class ConfigDBError < RuntimeError
|
10
|
+
end
|
11
|
+
|
12
|
+
# == assert
|
13
|
+
# Simple assert function making use of a passed code block. Assertions are
|
14
|
+
# disabled if $DEBUG is not set.
|
15
|
+
def assert
|
16
|
+
raise ConfigDBError, "Assertion failed !" unless yield if $DEBUG
|
17
|
+
end
|
18
|
+
|
19
|
+
# == verbose
|
20
|
+
# Used for debug messages. The param is sent to puts if $DEBUG is set.
|
21
|
+
def verbose(msg)
|
22
|
+
puts msg if $DEBUG
|
23
|
+
end
|
24
|
+
|
25
|
+
# == screen_key
|
26
|
+
# Additional checks for a key.
|
27
|
+
def screen_key(key)
|
28
|
+
case key
|
29
|
+
when /=/
|
30
|
+
raise ConfigDBError, "= characters are not permitted in keys: #{key}"
|
31
|
+
end
|
32
|
+
screen_prop key
|
33
|
+
end
|
34
|
+
|
35
|
+
# == screen_prop
|
36
|
+
# Test the incoming parameters for type, or any unsupported characters. We
|
37
|
+
# support ascii with no control characters.
|
38
|
+
def screen_prop(prop)
|
39
|
+
case prop
|
40
|
+
when nil
|
41
|
+
raise ConfigDBError, "nil params are not permitted: #{prop}"
|
42
|
+
when /\|/
|
43
|
+
raise ConfigDBError, "| characters are not permitted: #{prop}"
|
44
|
+
when /[^[:print:]]/
|
45
|
+
raise ConfigDBError, "Invalid characters found: #{prop}"
|
46
|
+
when String
|
47
|
+
return
|
48
|
+
else
|
49
|
+
raise ConfigDBError, "properties must be of type String"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# = ConfigDB
|
54
|
+
# A representation of a single database.
|
55
|
+
class ConfigDB
|
56
|
+
attr_reader :file
|
57
|
+
|
58
|
+
# == initialize
|
59
|
+
# Class constructor. Takes a database name, or a path, defaulting to
|
60
|
+
# the main configuration database.
|
61
|
+
def initialize(dbname = "configuration")
|
62
|
+
@records = Hash.new
|
63
|
+
@file = nil
|
64
|
+
verbose "Initializing db for dbname #{dbname}"
|
65
|
+
|
66
|
+
# Absolute or relative path?
|
67
|
+
if File::exists? dbname
|
68
|
+
@file = dbname
|
69
|
+
lock_and_load
|
70
|
+
else
|
71
|
+
# Ok, try the standard location on disk.
|
72
|
+
dbpath = DbPrefix + '/' + dbname
|
73
|
+
if File::exists? dbpath
|
74
|
+
@file = dbpath
|
75
|
+
lock_and_load
|
76
|
+
else
|
77
|
+
raise ConfigDBError, "#{dbpath} does not exist"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def inspect
|
83
|
+
return "ConfigDB object for #{@file}"
|
84
|
+
end
|
85
|
+
|
86
|
+
# == get_all
|
87
|
+
# This method returns all of the records in the database as an array.
|
88
|
+
def get_all
|
89
|
+
return @records.values
|
90
|
+
end
|
91
|
+
|
92
|
+
# == get
|
93
|
+
# This method returns the record based on the key passed, returning nil if
|
94
|
+
# no record with that key exists.
|
95
|
+
def get(key)
|
96
|
+
return @records[key]
|
97
|
+
end
|
98
|
+
|
99
|
+
# == get_value
|
100
|
+
# This method returns the value, or type, of the record passed. If there
|
101
|
+
# is no such record, it safely returns nil.
|
102
|
+
def get_value(key)
|
103
|
+
rec = @records[key]
|
104
|
+
return rec.value if rec
|
105
|
+
end
|
106
|
+
|
107
|
+
# == get_prop
|
108
|
+
# This method returns the property passed of the record passed. If either
|
109
|
+
# the record or property does not exist, it safely returns nil.
|
110
|
+
def get_prop(key, prop)
|
111
|
+
rec = @records[key]
|
112
|
+
if rec
|
113
|
+
rec.prop(prop)
|
114
|
+
else
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# == new_record
|
120
|
+
# This method creates a new record object in the database, and returns it.
|
121
|
+
# The key for the record is a required parameter, along with the type. For
|
122
|
+
# singleton records, the type is the value associated with the key.
|
123
|
+
def new_record(key, type)
|
124
|
+
assert {key !~ /=/}
|
125
|
+
assert {key != nil and key != ''}
|
126
|
+
assert {type != nil}
|
127
|
+
if @records.has_key? key
|
128
|
+
raise ConfigDBError, "Key #{key} already exists in database"
|
129
|
+
end
|
130
|
+
|
131
|
+
record = ConfigDBRecord.new(key, self)
|
132
|
+
record.type = type
|
133
|
+
@records[key] = record
|
134
|
+
return record
|
135
|
+
end
|
136
|
+
|
137
|
+
# == delete_record
|
138
|
+
# This method deletes the record from the database based on the key
|
139
|
+
# passed.
|
140
|
+
def delete_record(key)
|
141
|
+
@records.delete(key)
|
142
|
+
end
|
143
|
+
|
144
|
+
# == reload
|
145
|
+
# This method reloads the database from the file.
|
146
|
+
def reload
|
147
|
+
@records.clear
|
148
|
+
lock_and_load
|
149
|
+
end
|
150
|
+
|
151
|
+
# == save
|
152
|
+
# This method saves the db object to the file.
|
153
|
+
def save
|
154
|
+
begin
|
155
|
+
file = File.new(@file, "w")
|
156
|
+
unless file.flock(File::LOCK_EX)
|
157
|
+
raise ConfigDBError, "Failed to acquire exclusive lock"
|
158
|
+
end
|
159
|
+
@records.keys.sort.each do |key|
|
160
|
+
file.write(@records[key].to_s + "\n")
|
161
|
+
end
|
162
|
+
ensure
|
163
|
+
file.flock(File::LOCK_UN)
|
164
|
+
file.close
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# == each
|
169
|
+
# This method is an interator for the records in the db.
|
170
|
+
def each
|
171
|
+
for key in @records.keys.sort
|
172
|
+
yield @records[key]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
# == lock_and_load
|
179
|
+
# This method locks the db file, and parses it into an internal list of
|
180
|
+
# records.
|
181
|
+
def lock_and_load
|
182
|
+
begin
|
183
|
+
file = File.new(@file, "r")
|
184
|
+
unless file.flock(File::LOCK_SH)
|
185
|
+
raise ConfigDBError, "Failed to acquire shared lock"
|
186
|
+
end
|
187
|
+
file.each do |line|
|
188
|
+
# Skip any commented or blank lines.
|
189
|
+
next if line =~ /^(\s+)?#/ or line =~ /^(\s*)$/
|
190
|
+
|
191
|
+
key = nil
|
192
|
+
if line =~ /^([^=]+)=/
|
193
|
+
key = Regexp.last_match(1)
|
194
|
+
else
|
195
|
+
raise ConfigDBError, "Parse error on line: #{line}"
|
196
|
+
end
|
197
|
+
|
198
|
+
if @records.has_key? key
|
199
|
+
raise ConfigDBError, "Key #{key} is not unique!"
|
200
|
+
else
|
201
|
+
record = ConfigDBRecord.new(key, self, false)
|
202
|
+
record.parse(line)
|
203
|
+
@records[key] = record
|
204
|
+
end
|
205
|
+
end
|
206
|
+
ensure
|
207
|
+
# No matter what, we always unlock and close the file.
|
208
|
+
file.flock(File::LOCK_UN)
|
209
|
+
file.close
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# = ConfigDBRecord
|
215
|
+
# A representation of a single record in the database.
|
216
|
+
class ConfigDBRecord
|
217
|
+
attr_accessor :type
|
218
|
+
attr_reader :key
|
219
|
+
|
220
|
+
# == initialize
|
221
|
+
# Class constructor. A unique key is the only required parameter.
|
222
|
+
def initialize(key, db, save=true)
|
223
|
+
screen_key key
|
224
|
+
@key = key
|
225
|
+
@type = nil
|
226
|
+
@props = Hash.new
|
227
|
+
@db = db
|
228
|
+
@db.save if save
|
229
|
+
end
|
230
|
+
|
231
|
+
def inspect
|
232
|
+
return "ConfigDBRecord, key is #{key}, type is #{type}"
|
233
|
+
end
|
234
|
+
|
235
|
+
def to_s
|
236
|
+
verbose "In method ConfigDBRecord.to_s"
|
237
|
+
verbose "@key is #{@key}, @type is #{@type}"
|
238
|
+
s = @key + '=' + @type
|
239
|
+
@props.keys.sort.each do |key|
|
240
|
+
s += '|' + key + '|' + @props[key]
|
241
|
+
end
|
242
|
+
return s
|
243
|
+
end
|
244
|
+
|
245
|
+
def parse(line)
|
246
|
+
line.chomp!
|
247
|
+
pieces = line.split('=', 2)
|
248
|
+
|
249
|
+
if pieces.length == 1
|
250
|
+
pieces << ''
|
251
|
+
elsif pieces.length != 2
|
252
|
+
raise ConfigDBError, "Invalid line: #{line}"
|
253
|
+
end
|
254
|
+
|
255
|
+
# @key is set by the constructor.
|
256
|
+
unless pieces[1] =~ /\|/
|
257
|
+
@type = pieces[1]
|
258
|
+
else
|
259
|
+
pieces = pieces[1].split('|')
|
260
|
+
@type = pieces.shift
|
261
|
+
|
262
|
+
unless pieces.length % 2 == 0
|
263
|
+
raise ConfigDBError,
|
264
|
+
"Odd number of name/value pairs in line: #{line}"
|
265
|
+
end
|
266
|
+
|
267
|
+
while pieces.length > 0
|
268
|
+
key = pieces.shift
|
269
|
+
val = pieces.shift
|
270
|
+
screen_key key
|
271
|
+
screen_prop val
|
272
|
+
|
273
|
+
unless @props.has_key? key
|
274
|
+
@props[key] = val
|
275
|
+
else
|
276
|
+
raise ConfigDBError, "Duplicate property #{key} in record"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# == prop
|
283
|
+
# This method returns the value of the property passed. It returns nil if
|
284
|
+
# the property does not exist.
|
285
|
+
def prop(name)
|
286
|
+
return @props[name]
|
287
|
+
end
|
288
|
+
|
289
|
+
# == props
|
290
|
+
# This method returns all of the property names in the record.
|
291
|
+
def props
|
292
|
+
return @props.keys
|
293
|
+
end
|
294
|
+
|
295
|
+
# == set_prop
|
296
|
+
# This method sets the property passed to the value passed.
|
297
|
+
def set_prop(name, value)
|
298
|
+
screen_prop name
|
299
|
+
screen_prop value
|
300
|
+
@props[name] = value
|
301
|
+
@db.save
|
302
|
+
end
|
303
|
+
|
304
|
+
# == type
|
305
|
+
# This method returns the type of the record, also used as a value for
|
306
|
+
# singleton records.
|
307
|
+
def type
|
308
|
+
return @type
|
309
|
+
end
|
310
|
+
|
311
|
+
# == value
|
312
|
+
# This method is an alias for type.
|
313
|
+
def value
|
314
|
+
type
|
315
|
+
end
|
316
|
+
|
317
|
+
# == set_type
|
318
|
+
# This method sets the type of the record, also used as a value for
|
319
|
+
# singleton records.
|
320
|
+
def set_type(type)
|
321
|
+
screen_prop type
|
322
|
+
@type = type
|
323
|
+
end
|
324
|
+
|
325
|
+
# == set_value
|
326
|
+
# This method is an alias for set_type.
|
327
|
+
def set_value(value)
|
328
|
+
set_type(value)
|
329
|
+
end
|
330
|
+
|
331
|
+
# == has_prop?
|
332
|
+
# This method returns true of the property passed exists in this record,
|
333
|
+
# and false otherwise.
|
334
|
+
def has_prop?(name)
|
335
|
+
return @props.has_key?(name)
|
336
|
+
end
|
337
|
+
|
338
|
+
# == delete_prop
|
339
|
+
# This method deletes a property from the given record.
|
340
|
+
def delete_prop(name)
|
341
|
+
@props.delete(name)
|
342
|
+
end
|
343
|
+
|
344
|
+
# == delete
|
345
|
+
# This method deletes the record.
|
346
|
+
def delete
|
347
|
+
@db.delete_record(@key)
|
348
|
+
end
|
349
|
+
|
350
|
+
# == show
|
351
|
+
# This method returns a pretty-printed string of the record.
|
352
|
+
def show
|
353
|
+
s = @key + '=' + @type + "\n"
|
354
|
+
@props.keys.sort.each do |key|
|
355
|
+
s += " #{key}=#{@props[key]}\n"
|
356
|
+
end
|
357
|
+
return s
|
358
|
+
end
|
359
|
+
|
360
|
+
# == merge_props
|
361
|
+
# This method takes a hash, and merges it into the current record. The
|
362
|
+
# hash values will clobber any preexisting properties.
|
363
|
+
def merge_props(props)
|
364
|
+
props.each do |key, val|
|
365
|
+
screen_prop key
|
366
|
+
screen_prop val
|
367
|
+
@props[key] = val
|
368
|
+
end
|
369
|
+
@db.save
|
370
|
+
end
|
371
|
+
end
|
data/test/test.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
4
|
+
require 'smeserver/configdb'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
include FileUtils
|
9
|
+
|
10
|
+
$dbfileorig = File.join(File.dirname(__FILE__), 'configuration')
|
11
|
+
$dbfile = File.join(File.dirname(__FILE__), 'configuration.test')
|
12
|
+
$db = nil
|
13
|
+
|
14
|
+
class TestConfigDB < Test::Unit::TestCase
|
15
|
+
def setup
|
16
|
+
if File.exists? $dbfile
|
17
|
+
rm $dbfile
|
18
|
+
end
|
19
|
+
if File.exists? $dbfileorig
|
20
|
+
cp $dbfileorig, $dbfile
|
21
|
+
else
|
22
|
+
raise "#{$dbfileorig} does not exist!"
|
23
|
+
end
|
24
|
+
|
25
|
+
$db = ConfigDB.new($dbfile)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_simple
|
29
|
+
assert_equal(ConfigDB, $db.class)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_read
|
33
|
+
iface = $db.get('InternalInterface')
|
34
|
+
assert_equal(ConfigDBRecord, iface.class)
|
35
|
+
assert_equal('172.16.98.255', iface.prop('Broadcast'))
|
36
|
+
assert_equal('miimon=200 mode=active-backup',
|
37
|
+
iface.prop('NICBondingOptions'))
|
38
|
+
assert_equal('servergateway', $db.get('SystemMode').value)
|
39
|
+
assert_equal('servergateway', $db.get_value('SystemMode'))
|
40
|
+
assert_equal('172.16.98.255',
|
41
|
+
$db.get_prop('InternalInterface', 'Broadcast'))
|
42
|
+
assert_equal(117, $db.get_all.length)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_write
|
46
|
+
iface = $db.get('InternalInterface')
|
47
|
+
iface.set_prop('IPAddress', '192.168.0.1')
|
48
|
+
$db.reload
|
49
|
+
assert_equal('192.168.0.1',
|
50
|
+
$db.get_prop('InternalInterface', 'IPAddress'))
|
51
|
+
rec = $db.new_record('foo', 'configuration')
|
52
|
+
props = {
|
53
|
+
'bar' => 1,
|
54
|
+
'bash' => 2,
|
55
|
+
'biff' => 3,
|
56
|
+
}
|
57
|
+
assert_raise(ConfigDBError) { rec.merge_props(props) }
|
58
|
+
props = {
|
59
|
+
'bar' => '1',
|
60
|
+
'bash' => '2',
|
61
|
+
'biff' => '3',
|
62
|
+
}
|
63
|
+
assert_nothing_raised() { rec.merge_props(props) }
|
64
|
+
$db.reload
|
65
|
+
assert_equal(118, $db.get_all.length)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_utilities
|
69
|
+
assert_nothing_raised() { screen_prop 'foobar' }
|
70
|
+
assert_nothing_raised() { screen_prop 'foo bar' }
|
71
|
+
assert_nothing_raised() { screen_prop ' foo bar' }
|
72
|
+
end
|
73
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: sme-configdb
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2006-10-16 00:00:00 -04:00
|
8
|
+
summary: A db API for the esmith dbs on the SME Server
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: msoulier@digitaltorque.ca
|
12
|
+
homepage: http://sme-configdb.rubyforge.org
|
13
|
+
rubyforge_project:
|
14
|
+
description: A db API for the esmith dbs on the SME Server.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Michael P. Soulier
|
31
|
+
files:
|
32
|
+
- lib/smeserver/configdb.rb
|
33
|
+
- README
|
34
|
+
- ChangeLog
|
35
|
+
- test/test.rb
|
36
|
+
test_files:
|
37
|
+
- test/test.rb
|
38
|
+
rdoc_options: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
executables: []
|
43
|
+
|
44
|
+
extensions: []
|
45
|
+
|
46
|
+
requirements: []
|
47
|
+
|
48
|
+
dependencies: []
|
49
|
+
|