sme-configdb 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/ChangeLog +0 -0
  2. data/README +8 -0
  3. data/lib/smeserver/configdb.rb +371 -0
  4. data/test/test.rb +73 -0
  5. metadata +49 -0
File without changes
data/README ADDED
@@ -0,0 +1,8 @@
1
+ About sme::configdb:
2
+ ====================
3
+
4
+ Release Notes:
5
+ ==============
6
+
7
+ About version 0.1:
8
+ ==================
@@ -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
@@ -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
+