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.
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
+