pio 0.1.1 → 0.2.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.
@@ -0,0 +1,90 @@
1
+ module Pio
2
+ #
3
+ # A wrapper class to IPAddr
4
+ #
5
+ class IP
6
+ require "ipaddr"
7
+
8
+
9
+ #
10
+ # @return [IPAddr] value object instance of proxied IPAddr.
11
+ #
12
+ attr_reader :value
13
+
14
+
15
+ attr_reader :prefixlen
16
+
17
+
18
+ #
19
+ # Creates a {IP} instance object as a proxy to IPAddr class.
20
+ #
21
+ # @overload initialize(addr)
22
+ #
23
+ # @param [String, Number] addr
24
+ # an IPv4 address specified either as a String or Number.
25
+ #
26
+ # @param [Number] prefixlen
27
+ # masking IPv4 address with given prefixlen.
28
+ #
29
+ # @raise [ArgumentError] invalid address if supplied argument is invalid
30
+ # IPv4 address.
31
+ #
32
+ # @return [IP] self
33
+ # a proxy to IPAddr.
34
+ #
35
+ def initialize addr, prefixlen = 32
36
+ @prefixlen = prefixlen
37
+ case addr
38
+ when Integer
39
+ @value = IPAddr.new( addr, Socket::AF_INET )
40
+ when String
41
+ @value = IPAddr.new( addr )
42
+ else
43
+ raise TypeError, "Invalid IP address: #{ addr.inspect }"
44
+ end
45
+ if prefixlen < 32
46
+ @value = @value.mask( prefixlen )
47
+ end
48
+ end
49
+
50
+
51
+ #
52
+ # @return [String] the IPv4 address in its text representation.
53
+ #
54
+ def to_s
55
+ @value.to_s
56
+ end
57
+
58
+
59
+ #
60
+ # @return [Number] the IPv4 address in its numeric representation.
61
+ #
62
+ def to_i
63
+ @value.to_i
64
+ end
65
+
66
+
67
+ def == other
68
+ to_s == other.to_s
69
+ end
70
+
71
+
72
+ #
73
+ # @return [Array]
74
+ # an array of decimal numbers converted from IP address.
75
+ #
76
+ def to_a
77
+ to_s.split( "." ).collect do | each |
78
+ each.to_i
79
+ end
80
+ end
81
+ alias :to_array :to_a
82
+ end
83
+ end
84
+
85
+
86
+ ### Local variables:
87
+ ### mode: Ruby
88
+ ### coding: utf-8-unix
89
+ ### indent-tabs-mode: nil
90
+ ### End:
@@ -1,6 +1,3 @@
1
- require "rubygems"
2
-
3
- require "bindata"
4
1
  require "forwardable"
5
2
  require "pio/lldp/frame"
6
3
 
@@ -8,27 +5,92 @@ require "pio/lldp/frame"
8
5
  module Pio
9
6
  # LLDP frame parser and generator.
10
7
  class Lldp
8
+ # User options for creating an LLDP frame.
9
+ class Options
10
+ def initialize options
11
+ @options = options
12
+
13
+ unless dpid
14
+ raise TypeError, "Invalid DPID: #{ dpid.inspect }"
15
+ end
16
+ unless port_id
17
+ raise TypeError, "Invalid port number: #{ port_id.inspect }"
18
+ end
19
+ end
20
+
21
+
22
+ def to_hash
23
+ {
24
+ :destination_mac => Mac.new( destination_mac ).to_a,
25
+ :source_mac => Mac.new( source_mac ).to_a,
26
+ :chassis_id => dpid,
27
+ :port_id => port_id
28
+ }
29
+ end
30
+
31
+
32
+ ##########################################################################
33
+ private
34
+ ##########################################################################
35
+
36
+
37
+ def dpid
38
+ @options[ :dpid ]
39
+ end
40
+
41
+
42
+ def port_id
43
+ @options[ :port_number ]
44
+ end
45
+
46
+
47
+ def destination_mac
48
+ @options[ :destination_mac ] || "01:80:c2:00:00:0e"
49
+ end
50
+
51
+
52
+ def source_mac
53
+ @options[ :source_mac ] || "01:02:03:04:05:06"
54
+ end
55
+ end
56
+
57
+
11
58
  extend Forwardable
12
59
 
13
60
 
14
61
  def self.read raw_data
15
- frame = Frame.read( raw_data )
16
- new frame.dpid, frame.port_id
62
+ begin
63
+ frame = Frame.read( raw_data )
64
+ rescue
65
+ raise Pio::ParseError, $!.message
66
+ end
67
+ lldp = allocate
68
+ lldp.instance_variable_set :@frame, frame
69
+ lldp
17
70
  end
18
71
 
19
72
 
20
- def initialize dpid, port_number, destination_mac = "01:80:c2:00:00:0e"
21
- @frame = Frame.new
22
- @frame.destination_mac = destination_mac
23
- @frame.source_mac = "11:22:33:44:55:66" # FIXME
24
- @frame.chassis_id = dpid
25
- @frame.port_id = port_number
73
+ def initialize options
74
+ @frame = Frame.new( Options.new( options ).to_hash )
26
75
  end
27
76
 
28
77
 
78
+ def_delegator :@frame, :destination_mac
79
+ def_delegator :@frame, :source_mac
80
+ def_delegator :@frame, :ether_type
81
+ def_delegator :@frame, :chassis_id
29
82
  def_delegator :@frame, :dpid
30
83
  def_delegator :@frame, :optional_tlv
31
- def_delegator :@frame, :port_id, :port_number
84
+ def_delegator :@frame, :port_id
85
+ alias :port_number :port_id
86
+ def_delegator :@frame, :ttl
87
+ def_delegator :@frame, :port_description
88
+ def_delegator :@frame, :system_name
89
+ def_delegator :@frame, :system_description
90
+ def_delegator :@frame, :system_capabilities
91
+ def_delegator :@frame, :enabled_capabilities
92
+ def_delegator :@frame, :management_address
93
+ def_delegator :@frame, :organizationally_specific
32
94
 
33
95
 
34
96
  def to_binary
@@ -2,21 +2,21 @@ require "rubygems"
2
2
  require "bindata"
3
3
 
4
4
  require "pio/lldp/chassis-id-tlv"
5
- require "pio/lldp/mac-address"
6
5
  require "pio/lldp/optional-tlv"
7
6
  require "pio/lldp/port-id-tlv"
8
7
  require "pio/lldp/ttl-tlv"
8
+ require "pio/type/ethernet-header"
9
9
 
10
10
 
11
11
  module Pio
12
12
  class Lldp
13
13
  # LLDP frame
14
14
  class Frame < BinData::Record
15
+ extend Type::EthernetHeader
16
+
15
17
  endian :big
16
18
 
17
- mac_address :destination_mac
18
- mac_address :source_mac
19
- uint16 :ethertype, :value => 0x88cc
19
+ ethernet_header :ether_type => 0x88cc
20
20
  chassis_id_tlv :chassis_id
21
21
  port_id_tlv :port_id
22
22
  ttl_tlv :ttl, :initial_value => 120
@@ -26,6 +26,55 @@ module Pio
26
26
  def dpid
27
27
  chassis_id
28
28
  end
29
+
30
+
31
+ def port_description
32
+ get_tlv_field 4, "port_description"
33
+ end
34
+
35
+
36
+ def system_name
37
+ get_tlv_field 5, "system_name"
38
+ end
39
+
40
+
41
+ def system_description
42
+ get_tlv_field 6, "system_description"
43
+ end
44
+
45
+
46
+ def system_capabilities
47
+ get_tlv 7
48
+ end
49
+
50
+
51
+ def management_address
52
+ get_tlv_field 8, "management_address"
53
+ end
54
+
55
+
56
+ def organizationally_specific
57
+ get_tlv 127
58
+ end
59
+
60
+
61
+ ##########################################################################
62
+ private
63
+ ##########################################################################
64
+
65
+
66
+ def get_tlv tlv_type
67
+ tlv = optional_tlv.find do | each |
68
+ each[ "tlv_type" ] == tlv_type
69
+ end
70
+ tlv[ "tlv_value" ] if tlv
71
+ end
72
+
73
+
74
+ def get_tlv_field tlv_type, name
75
+ tlv = get_tlv( tlv_type )
76
+ tlv[ name ] if tlv
77
+ end
29
78
  end
30
79
  end
31
80
  end
@@ -8,7 +8,13 @@ module Pio
8
8
  class ManagementAddressValue < BinData::Record
9
9
  endian :big
10
10
 
11
- stringz :management_address
11
+ uint8 :string_length
12
+ uint8 :subtype
13
+ string :management_address, :read_length => lambda { string_length - 1 }
14
+ uint8 :interface_numbering_subtype
15
+ uint32 :interface_number
16
+ uint8 :oid_string_length
17
+ string :object_identifier, :read_length => lambda { oid_string_length }
12
18
  end
13
19
  end
14
20
  end
@@ -3,6 +3,7 @@ require "pio/lldp/system-name-value"
3
3
  require "pio/lldp/system-description-value"
4
4
  require "pio/lldp/system-capabilities-value"
5
5
  require "pio/lldp/management-address-value"
6
+ require "pio/lldp/organizationally-specific-value"
6
7
  require "pio/lldp/end-of-lldpdu-value"
7
8
 
8
9
 
@@ -24,6 +25,7 @@ module Pio
24
25
  system_description_value 6
25
26
  system_capabilities_value 7
26
27
  management_address_value 8
28
+ organizationally_specific_value 127
27
29
  string "unknown"
28
30
  end
29
31
 
@@ -54,7 +56,7 @@ module Pio
54
56
 
55
57
  def optional_tlv?
56
58
  tmp_tlv_type = tlv_type
57
- 4 <= tmp_tlv_type and tmp_tlv_type <= 8
59
+ 4 <= tmp_tlv_type and tmp_tlv_type <= 127
58
60
  end
59
61
 
60
62
 
@@ -0,0 +1,23 @@
1
+ require "rubygems"
2
+ require "bindata"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ # The V of Organizationally specfic TLV.
8
+ class OrganizationallySpecificValue < BinData::Record
9
+ endian :big
10
+
11
+ uint24be :oui
12
+ uint8 :subtype
13
+ stringz :information
14
+ end
15
+ end
16
+ end
17
+
18
+
19
+ ### Local variables:
20
+ ### mode: Ruby
21
+ ### coding: utf-8-unix
22
+ ### indent-tabs-mode: nil
23
+ ### End:
@@ -0,0 +1,156 @@
1
+ require "forwardable"
2
+
3
+
4
+ module Pio
5
+ #
6
+ # Ethernet address class
7
+ #
8
+ class Mac
9
+ extend Forwardable
10
+ def_delegator :@value, :hash
11
+
12
+
13
+ #
14
+ # Returns an Ethernet address in its numeric presentation.
15
+ #
16
+ # @example
17
+ # Mac.new("11:22:33:44:55:66") #=> 18838586676582
18
+ #
19
+ # @return [Number] the Ethernet address in numeric format
20
+ #
21
+ attr_reader :value
22
+
23
+
24
+ #
25
+ # Creates a {Mac} instance that encapsulates Ethernet addresses.
26
+ #
27
+ # @overload initialize(value)
28
+ #
29
+ # @param [String,Integer] value
30
+ # the Ethernet address to set to.
31
+ #
32
+ # @example address as a hexadecimal string
33
+ # Mac.new("11:22:33:44:55:66")
34
+ #
35
+ # @example address as a hexadecimal number
36
+ # Mac.new(0xffffffffffff)
37
+ #
38
+ # @raise [ArgumentError] if invalid format is detected.
39
+ # @raise [TypeError] if supplied argument is not a String or Integer.
40
+ #
41
+ def initialize value
42
+ case value
43
+ when String
44
+ @value = create_from( value )
45
+ when Integer
46
+ @value = value
47
+ validate_value_range
48
+ when Mac
49
+ @value = create_from( value.to_s )
50
+ else
51
+ raise TypeError, "Invalid MAC address: #{ value.inspect }"
52
+ end
53
+ @string = string_format
54
+ end
55
+
56
+
57
+ #
58
+ # Returns the Ethernet address as 6 pairs of hexadecimal digits
59
+ # delimited by colons.
60
+ #
61
+ # @example
62
+ # Mac.new(18838586676582).to_s #=> "11:22:33:44:55:66"
63
+ #
64
+ # @return [String] the Ethernet address in String format
65
+ #
66
+ def to_s
67
+ @string
68
+ end
69
+
70
+
71
+ #
72
+ # Returns an array of decimal numbers converted from Ethernet's
73
+ # address string format.
74
+ #
75
+ # @example
76
+ # Mac.new("11:22:33:44:55:66").to_a #=> [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 ]
77
+ #
78
+ # @return [Array] the Ethernet address in Array format
79
+ #
80
+ def to_a
81
+ @string.split( ":" ).collect do | each |
82
+ each.hex
83
+ end
84
+ end
85
+
86
+
87
+ #
88
+ # @private
89
+ #
90
+ def == other
91
+ to_s == other.to_s
92
+ end
93
+ alias :eql? :==
94
+
95
+
96
+ #
97
+ # Returns true if Ethernet address is a multicast address.
98
+ #
99
+ # @example
100
+ # Mac.new("01:00:00:00:00:00").multicast? #=> true
101
+ # Mac.new("00:00:00:00:00:00").multicast? #=> false
102
+ #
103
+ # @return [Boolean] whether the Ethernet address is multicast
104
+ #
105
+ def multicast?
106
+ to_a[ 0 ] & 1 == 1
107
+ end
108
+
109
+
110
+ #
111
+ # Returns true if Ethernet address is a broadcast address.
112
+ #
113
+ # @example
114
+ # Mac.new("ff:ff:ff:ff:ff:ff").broadcast? #=> true
115
+ #
116
+ # @return [Boolean] whether the Ethernet address is broadcast
117
+ #
118
+ def broadcast?
119
+ to_a.all? { | each | each == 0xff }
120
+ end
121
+
122
+
123
+ ################################################################################
124
+ private
125
+ ################################################################################
126
+
127
+
128
+ def create_from string
129
+ octet_regex = "[0-9a-fA-F][0-9a-fA-F]"
130
+ if /^(#{ octet_regex }:){5}(#{ octet_regex })$/=~ string
131
+ string.gsub( ":", "" ).hex
132
+ else
133
+ raise ArgumentError, %{Invalid MAC address: "#{ string }"}
134
+ end
135
+ end
136
+
137
+
138
+ def validate_value_range
139
+ unless ( @value >= 0 and @value <= 0xffffffffffff )
140
+ raise ArgumentError, "Invalid MAC address: #{ @value }"
141
+ end
142
+ end
143
+
144
+
145
+ def string_format
146
+ sprintf( "%012x", @value ).unpack( "a2" * 6 ).join( ":" )
147
+ end
148
+ end
149
+ end
150
+
151
+
152
+ ### Local variables:
153
+ ### mode: Ruby
154
+ ### coding: utf-8-unix
155
+ ### indent-tabs-mode: nil
156
+ ### End: