iface 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d6fa26ea8fbe2745f309e5c2690bca592b2457b
4
- data.tar.gz: ec6a7071343fa410811212083862d1fc592e67b1
3
+ metadata.gz: fc2fdecabc038ea8a83b38920c8dff34d60a7f4d
4
+ data.tar.gz: afa9611dacfb1807fe1415d9ce3233a143a934db
5
5
  SHA512:
6
- metadata.gz: b7697cbb1c54cf9e06ce114d43b42742137428bdeac500e4962e6625951a430d194498a709fe96f222ab8a94d7ad79bd821094d296a68537e201344f3f5773a9
7
- data.tar.gz: 0d304fd04fb52aa4515a4047578e5d3d61f19a8a9124d7c6d74ebac1761d851d261a43d2f618709be610ac2add9a7cef79e1b348729887a4a8a6d9c0e72191b4
6
+ metadata.gz: 7dab1b175a9bd0e556529562aa060daabd8e56c3e830bfbade0c858ef99d9f0605650ba3879556b03b6c9240df4b8f9fb5a9cdeab502b1bb8e34c7b4cf8de2d8
7
+ data.tar.gz: 5bc90e93bfe86c604d6aadd650bed52a774056788ff5f94ba66a69403f1a9545beab65bc4b9128968b46a7b186a0abd4a5676ce244af7a80a138cd03a26ec50f
@@ -2,3 +2,4 @@
2
2
 
3
3
  require_relative 'iface/config'
4
4
  require_relative 'iface/ip_address'
5
+ require_relative 'iface/value_set'
@@ -22,11 +22,10 @@ module Iface
22
22
  def self.create(filename, io)
23
23
  fname = File.split(filename).last
24
24
  device, range_num, clone_num = parse_filename(fname)
25
- vars = ValueSet.new(io)
26
25
 
27
26
  FILE_TYPES.each do |klass|
28
- if klass.recognize?(device, range_num, clone_num, vars)
29
- return klass.new(filename, device, range_num, clone_num, vars)
27
+ if klass.recognize?(device, range_num, clone_num)
28
+ return klass.new(filename, device, range_num, clone_num, io)
30
29
  end
31
30
  end
32
31
 
@@ -40,7 +39,7 @@ module Iface
40
39
  [device, range_num&.to_i, clone_num&.to_i]
41
40
  end
42
41
 
43
- def self.recognize?(_device, _range_num, _clone_num, _vars)
42
+ def self.recognize?(_device, _range_num, _clone_num)
44
43
  false
45
44
  end
46
45
 
@@ -48,10 +47,10 @@ module Iface
48
47
  name.split('::').last[0..-5].decamelize.to_sym if name.match?(/File\Z/)
49
48
  end
50
49
 
51
- def initialize(filename, device, _range_num, _clone_num, vars)
50
+ def initialize(filename, device, _range_num, _clone_num, io)
52
51
  @filename = filename
53
52
  @device = device
54
- @vars = vars
53
+ @vars = value_set_class.new(io)
55
54
  end
56
55
 
57
56
  def static?
@@ -61,32 +60,80 @@ module Iface
61
60
  def include?(_ip)
62
61
  raise NotImplementedError
63
62
  end
63
+
64
+ def value_set_class
65
+ ValueSet
66
+ end
67
+
68
+ def to_s
69
+ @vars.to_s
70
+ end
64
71
  end
65
72
 
66
73
  # Represents a primary config file (not loopback, range or clone file)
67
74
  #
68
75
  # These are files named like "ifcfg-eth0".
69
76
  class PrimaryFile < ConfigFile
70
- attr_reader :ip_address, :ipv6_address, :ipv6_secondaries
71
-
72
- def self.recognize?(device, range_num, clone_num, _vars)
77
+ def self.recognize?(device, range_num, clone_num)
73
78
  device != 'lo' && range_num.nil? && clone_num.nil?
74
79
  end
75
80
 
76
- def initialize(filename, device, range_num, clone_num, vars)
77
- super
78
- return unless %w[static none].include?(vars['bootproto']) # RHEL6 uses "none"
79
- @ip_address = vars['ipaddr']
80
- @ipv6_address = vars['ipv6addr']
81
- @ipv6_secondaries = vars['ipv6addr_secondaries']&.split(/\s+/)
81
+ def ip_address
82
+ @vars['ipaddr']
83
+ end
84
+
85
+ def ip_address=(new_ip)
86
+ @vars['ipaddr'] = new_ip
87
+ make_static
88
+ disable_nm
89
+ end
90
+
91
+ def ipv6_address
92
+ @vars['ipv6addr']
93
+ end
94
+
95
+ def ipv6_address=(new_ip)
96
+ @vars['ipv6addr'] = new_ip
97
+ make_static
98
+ disable_nm
99
+ end
100
+
101
+ def ipv6_secondaries
102
+ @vars['ipv6addr_secondaries']&.split(/\s+/)
103
+ end
104
+
105
+ def ipv6_secondaries=(new_ips)
106
+ @vars['ipv6addr_secondaries'] = new_ips
107
+ make_static
108
+ disable_nm
109
+ end
110
+
111
+ def value_set_class
112
+ PrimaryInterface
113
+ end
114
+
115
+ def make_static
116
+ @vars.make_static unless static?
117
+ end
118
+
119
+ def disable_nm
120
+ @vars.disable_nm
121
+ end
122
+
123
+ def nm_controlled?
124
+ @vars['nm_controlled'] == 'yes'
125
+ end
126
+
127
+ def use_ipv6
128
+ @vars.use_ipv6
82
129
  end
83
130
 
84
131
  def static?
85
- !@ip_address.nil?
132
+ @vars['bootproto'] == 'none'
86
133
  end
87
134
 
88
135
  def include?(ip)
89
- @ip_address == ip
136
+ ip_address == ip
90
137
  end
91
138
  end
92
139
 
@@ -96,11 +143,11 @@ module Iface
96
143
  class CloneFile < ConfigFile
97
144
  attr_reader :ip_address, :clone_num
98
145
 
99
- def self.recognize?(_device, _range_num, clone_num, _vars)
146
+ def self.recognize?(_device, _range_num, clone_num)
100
147
  !clone_num.nil?
101
148
  end
102
149
 
103
- def initialize(filename, device, _range_num, clone_num, vars)
150
+ def initialize(filename, device, _range_num, clone_num, io)
104
151
  super
105
152
  @ip_address = vars['ipaddr']
106
153
  @clone_num = clone_num
@@ -121,11 +168,11 @@ module Iface
121
168
  class RangeFile < ConfigFile
122
169
  attr_reader :start_clone_num
123
170
 
124
- def self.recognize?(_device, range_num, _clone_num, _vars)
171
+ def self.recognize?(_device, range_num, _clone_num)
125
172
  !range_num.nil?
126
173
  end
127
174
 
128
- def initialize(filename, device, range_num, clone_num, vars)
175
+ def initialize(filename, device, range_num, clone_num, io)
129
176
  super
130
177
  @start_ip_num = string_to_ip_num(vars['ipaddr_start'])
131
178
  @end_ip_num = string_to_ip_num(vars['ipaddr_end'])
@@ -156,11 +203,11 @@ module Iface
156
203
 
157
204
  # Represents a loopback file (device "lo")
158
205
  class LoopbackFile < ConfigFile
159
- def self.recognize?(device, _range_num, _clone_num, _vars)
206
+ def self.recognize?(device, _range_num, _clone_num)
160
207
  device == 'lo'
161
208
  end
162
209
 
163
- def initialize(filename, device, range_num, _clone_num, vars)
210
+ def initialize(filename, device, range_num, _clone_num, io)
164
211
  super
165
212
  @ip_address = vars['ipaddr']
166
213
  end
@@ -3,12 +3,97 @@
3
3
  require 'ipaddr'
4
4
 
5
5
  module Iface
6
- # Represents an IP address including subnet mask
6
+ # Represents an IP address including prefix length
7
7
  class IpAddress
8
- def initialize(*args)
9
- @ipaddr = IPAddr.new(*args)
10
- @_bitmask = @ipaddr.instance_eval { @mask_addr.to_s(2) }
8
+ def self.create(str)
9
+ ipaddr = IPAddr.new(str)
10
+ (ipaddr.ipv4? ? IpV4Address : IpV6Address).new(ipaddr)
11
+ end
12
+
13
+ def initialize(ip_address)
14
+ @ipaddr = ip_address.is_a?(IPAddr) ? ip_address : IPAddr.new(ip_address)
15
+ @mask_addr = @ipaddr.instance_eval { @mask_addr }
16
+ @_bitmask = @mask_addr.to_s(2)
11
17
  @inverse_mask_addr = @_bitmask.tr('01', '10').to_i(2)
12
18
  end
19
+
20
+ def cidr_mask
21
+ case diff = full_mask - @mask_addr
22
+ when 0
23
+ full_cidr_mask
24
+ when 1
25
+ full_cidr_mask - 1
26
+ else
27
+ full_cidr_mask - Math.log2(diff).ceil
28
+ end
29
+ end
30
+
31
+ def to_i
32
+ @ipaddr.to_i
33
+ end
34
+
35
+ def to_s
36
+ @ipaddr.to_s
37
+ end
38
+
39
+ def full_mask
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def full_cidr_mask
44
+ raise NotImplementedError
45
+ end
46
+
47
+ def ==(other)
48
+ self.class == other.class && __state__ == other.__state__
49
+ end
50
+
51
+ protected
52
+
53
+ def __state__
54
+ @ipaddr
55
+ end
56
+ end
57
+
58
+ # Represents an IPv4 address
59
+ class IpV4Address < IpAddress
60
+ def self.from_numeric(numeric)
61
+ new(IPAddr.new_ntoh([numeric].pack('N')))
62
+ end
63
+
64
+ def initialize(ip_address)
65
+ super
66
+ raise ArgumentError, "Expected IPv4, got IPv6: #{ip_address}" unless @ipaddr.ipv4?
67
+ end
68
+
69
+ def full_mask
70
+ IPAddr::IN4MASK
71
+ end
72
+
73
+ def full_cidr_mask
74
+ 32
75
+ end
76
+ end
77
+
78
+ # Represents an IPv6 address
79
+ class IpV6Address < IpAddress
80
+ MASK_32 = 0xffffffffffffffff
81
+
82
+ def self.from_numeric(numeric)
83
+ new(IPAddr.new_ntoh([(numeric >> 96), (numeric >> 64) & MASK_32, (numeric >> 32) & MASK_32, numeric & MASK_32].pack('N*')))
84
+ end
85
+
86
+ def initialize(ip_address)
87
+ super
88
+ raise ArgumentError, "Expected IPv6, got IPv4: #{ip_address}" unless @ipaddr.ipv6?
89
+ end
90
+
91
+ def full_mask
92
+ IPAddr::IN6MASK
93
+ end
94
+
95
+ def full_cidr_mask
96
+ 128
97
+ end
13
98
  end
14
99
  end
@@ -1,30 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'value_set/pair'
4
+
3
5
  module Iface
4
- # Represents a set of NAME=value pairs
6
+ # Represents a generic set of NAME=value pairs
5
7
  class ValueSet
6
- # Represents a NAME=value pair
7
- class Pair
8
- attr_reader :name, :value
9
-
10
- def initialize(line)
11
- match = line.match(/(^[A-Z0-9_]+)="?(.*?)"?$/)
12
- raise ArgumentError, "Expected pattern NAME=value; got #{line.inspect}" unless match
13
- @name = match[1]
14
- @value = match[2]&.sub(/^"/, '')&.sub(/"$/, '')
15
- end
16
-
17
- def to_s
18
- "#{@name}=\"#{@value}\""
19
- end
20
- end
21
-
22
8
  def initialize(io)
23
9
  @vars = {}
24
10
  io.each_line do |line|
25
11
  edited_line = line.sub(/#.*$/, '').strip
26
12
  next if edited_line.empty?
27
- pair = Pair.new(edited_line)
13
+ pair = Pair.create(edited_line, value_set: self)
28
14
  @vars[pair.name] = pair
29
15
  end
30
16
  end
@@ -33,13 +19,42 @@ module Iface
33
19
  @vars[name.upcase]&.value
34
20
  end
35
21
 
22
+ def []=(name, value)
23
+ key = name.upcase
24
+ if @vars.key?(key)
25
+ @vars[key].value = value
26
+ else
27
+ @vars[key] = Pair.create(key, value, value_set: self)
28
+ end
29
+ end
30
+
31
+ def fetch_raw(name)
32
+ @vars[name.upcase]&.raw_value
33
+ end
34
+
36
35
  def key?(name)
37
36
  @vars.key?(name.upcase)
38
37
  end
39
38
  alias has_key? key?
40
39
 
41
40
  def to_s
42
- @vars.values.map(&:to_s).join("\n")
41
+ "#{@vars.values.map(&:to_s).join("\n")}\n"
42
+ end
43
+ end
44
+
45
+ # Represents a set of NAME=value pairs that would appear in a primary
46
+ # interface file, e.g. one named "ifcfg-eth0"
47
+ class PrimaryInterface < ValueSet
48
+ def make_static
49
+ self['BOOTPROTO'] = 'none'
50
+ end
51
+
52
+ def disable_nm
53
+ self['NM_CONTROLLED'] = 'no'
54
+ end
55
+
56
+ def use_ipv6
57
+ self['IPV6INIT'] = 'yes'
43
58
  end
44
59
  end
45
60
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iface
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Cain
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-20 00:00:00.000000000 Z
11
+ date: 2017-09-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: camelotjim@jcain.net