junoser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,173 @@
1
+ module Junoser
2
+ class Ruler
3
+ OFFSET = ' '
4
+
5
+ def initialize(input)
6
+ @rule = input
7
+ end
8
+
9
+ def to_rule
10
+ rule_header << rule << rule_footer
11
+ end
12
+
13
+ def rule
14
+ str = @rule.read
15
+ str = process_reserved_element(str)
16
+ str = str.split(/\n/).map {|l| format(process_line(l)) }.join("\n")
17
+ end
18
+
19
+
20
+ private
21
+
22
+ def process_line(str)
23
+ return str if str =~ /^(.* do|end)$/
24
+
25
+ str.gsub!(/("[^"]+")/) { "str(#$1)" } # "foo" -> str("foo")
26
+
27
+ str.gsub!(/^(\s*)arg(\.as\(:\S+\))? \($/) { "#{$1}b(arg#$2," } # arg ( -> b(arg,
28
+ str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f\(|,]+)(\.as\(:\S+\))?(,?)$/) { "#{$1}a(#$2, #$3)#$4#$5" } # str("foo") bar -> a(str("foo"), bar)
29
+ str.gsub!(/^(\s*)(str\(\S+\)) \((.*)\)(,?)$/) { "#{$1}a(#$2, #$3)#$4" } # str("foo") (a | b) -> a(str("foo"), a | b)
30
+
31
+ str.gsub!(/^(\s*)(str\(\S+\)) \($/) { "#{$1}b(#$2," } # str("foo") ( -> b(str("foo"),
32
+ str.gsub!(/^(\s*)(\(.*\))(\.as\(:\S\))? \($/) { "#{$1}b(#$2#$3," } # (a | b) ( -> b((a | b),
33
+ str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f\(|,]+) \($/) { "#{$1}b(a(#$2, #$3)," } # str("foo") bar ( -> b(a(str("foo"), bar),
34
+ str.gsub!(/^(\s*)(str\(\S+\)) \((.*)\) \($/) { "#{$1}a(#$2, #$3," } # str("foo") (a | b) ( -> a(str("foo"), a | b,
35
+
36
+ str
37
+ end
38
+
39
+ def process_reserved_element(str)
40
+ str.gsub! /"\$\S+"/, 'arg'
41
+ str.gsub! '"as-number" arg', 'arg'
42
+ str.gsub! '"equal-literal"', '"="'
43
+ str.gsub! '"plus-literal"', '"+"'
44
+ str.gsub! '"minus-literal"', '"-"'
45
+
46
+ str.gsub!(/\((.*) \| "name"\)/) { "(#$1 | arg)" }
47
+ str.gsub! '"vlan" ("id-name" | "all")', '"vlan" ("all" | arg)'
48
+ str.gsub!(/("ssh-\S+") arg/) { "#$1 (quote | arg)" }
49
+ str.gsub! '"metric-value" arg', 'arg'
50
+ str.gsub! '"limit-threshold" arg', 'arg'
51
+ str.gsub! '"filename" arg', 'arg'
52
+ str.gsub! '"description" arg', '"description" (quote | arg)'
53
+ str.gsub! '"as-path-prepend" arg', '"as-path-prepend" (quote | arg)'
54
+
55
+
56
+ str.gsub!(/^(\s*)"inline-services"/) do
57
+ format(['"inline-services" (',
58
+ ' "bandwidth" ("1g" | "10g")',
59
+ ')'], $1)
60
+ end
61
+ str.gsub!(/^(\s*)"ieee-802.3ad" \(\s*sc\(\s*"lacp" \(\s*sc\(/) do
62
+ format(['"802.3ad" (',
63
+ ' sc(',
64
+ ' arg,',
65
+ ' "lacp" (',
66
+ ' sc(',
67
+ ' "force-up",'], $1)
68
+ end
69
+ str.gsub!(/^(\s*)"as-path" \(\s*sc\(\s*"path" arg/) do
70
+ format(['"as-path" (',
71
+ ' sc(',
72
+ ' arg'], $1)
73
+ end
74
+ str.gsub!(/^(\s*)"as-path" arg \(\s*sc\(\s*"path" arg\s*\)/) do
75
+ format(['"as-path" arg (',
76
+ ' sc(',
77
+ ' quote,',
78
+ ' arg',
79
+ ' )'], $1)
80
+ end
81
+
82
+ %w[metric metric2 metric3 metric4 tag tag2 preference preference2 color color2 local-preference].each do |key|
83
+ str.gsub!(/^(\s*"#{key}" \(\s*sc\(\s*c\(\s*)"#{key}" arg/) { "#{$1}arg" }
84
+ end
85
+
86
+ str.gsub!(/(s\(\s*)"address" \(\s*arg\s*\)/) { "#{$1}arg" }
87
+ str.gsub!(/^(\s*"idle-timeout" \(\s*sc\(\s*c\(\s*"forever",\s*)"timeout" arg/) { "#{$1}arg" }
88
+
89
+ str = omit_label(str, 'contents', 'syslog_object')
90
+ str = omit_label(str, 'interface', 'cos_interfaces_type')
91
+ str = omit_label(str, 'interface', 'ir_interfaces_type')
92
+ str = omit_label(str, 'interface', 'interfaces_type')
93
+ str = omit_label(str, 'client-address-list', 'client_address_object')
94
+ str = omit_label(str, 'prefix-list-item', 'prefix_list_items')
95
+ str = omit_label(str, 'instance', 'juniper_routing_instance')
96
+ str = omit_label(str, 'vlan', 'vlan_type')
97
+
98
+ str.gsub!(/"icmp"(.*)"icmp6"/) { %["icmpv6"#$1"icmp"] }
99
+ str.gsub!(/"http"(.*)"https"/) { %["https"#$1"http"] }
100
+ str.gsub!(/"snmp"(.*)"snmptrap"/) { %["snmptrap"#$1"snmp"] }
101
+
102
+ str
103
+ end
104
+
105
+ def omit_label(str, label, content)
106
+ str.gsub(/(\s*)"#{label}" \(\s*#{content}\s*\)/) { "#{$1}#{content}" }
107
+ end
108
+
109
+ def format(str, offset=OFFSET)
110
+ case str
111
+ when String
112
+ str.empty? ? '' : offset + str
113
+ when Array
114
+ str.map {|s| s.empty? ? '' : offset + s.to_s }.join("\n")
115
+ end
116
+ end
117
+
118
+ def rule_header
119
+ <<-EOS
120
+ require 'parslet'
121
+
122
+ module Junoser
123
+ class Parser < Parslet::Parser
124
+ # block with children maybe
125
+ def b(object, *children)
126
+ children.inject(object) {|rule, child| rule.as(:label) >> (space >> child.as(:child) | eos) }
127
+ end
128
+
129
+ # with an argument, and children maybe
130
+ def a(object, arg, *children)
131
+ b(object.as(:statement) >> space >> arg.as(:argument), *children)
132
+ end
133
+
134
+ # choice
135
+ def c(*objects)
136
+ objects.inject {|rule, object| rule | object }
137
+ end
138
+
139
+ # sequence
140
+ def s(*objects)
141
+ # TODO: eval "minOccurs" attribute of choice element
142
+ objects.inject {|rule, object| rule >> (space >> object).maybe }
143
+ end
144
+
145
+ # sequential choice
146
+ def sc(*objects)
147
+ (c(*objects) >> space.maybe).repeat(0)
148
+ end
149
+
150
+ rule(:arg) { match('\\S').repeat(1) }
151
+ rule(:space) { match('\\s').repeat(1) }
152
+ rule(:any) { match('.').repeat(1) }
153
+ rule(:eos) { match('$') }
154
+ rule(:dotted) { match('[^. \\t\\n\\r\\f]').repeat(1) >> match('\.') >> match('[^. \\t\\n\\r\\f]').repeat(1) }
155
+ rule(:quote) { match('"') >> match('[^"]').repeat(1) >> match('"') }
156
+ rule(:address) { match('[0-9a-fA-F:\.]').repeat(1) }
157
+ rule(:prefix ) { address >> (match('/') >> match('[0-9]').repeat(1)).maybe }
158
+
159
+ root(:set)
160
+ rule(:set) { (str('set') | str('deactivate')) >> space >> configuration.as(:config) }
161
+
162
+ EOS
163
+ end
164
+
165
+ def rule_footer
166
+ <<-EOS
167
+
168
+ end
169
+ end
170
+ EOS
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,41 @@
1
+ require 'parslet'
2
+
3
+ module Junoser
4
+ class Transformer < Parslet::Transform
5
+ rule(config: simple(:config)) do
6
+ config.to_s
7
+ end
8
+
9
+ rule(config: sequence(:configs)) do
10
+ configs.join("\n")
11
+ end
12
+
13
+ rule(arg: simple(:arg)) do
14
+ "arg(#{arg})"
15
+ end
16
+
17
+ rule(label: simple(:label)) do
18
+ label.to_s
19
+ end
20
+
21
+ rule(label: simple(:label), child: simple(:child)) do
22
+ "#{label}\n#{child}"
23
+ end
24
+
25
+ rule(label: simple(:label), child: sequence(:children)) do
26
+ %[#{label}\n#{children.join("\n")}]
27
+ end
28
+
29
+ rule(statement: simple(:statement), argument: simple(:argument)) do
30
+ "#{statement} #{argument}"
31
+ end
32
+
33
+ rule(statement: simple(:statement), argument: sequence(:arguments)) do
34
+ %[#{statement}\n#{arguments.join("\n")}]
35
+ end
36
+
37
+ rule(oneline: simple(:str)) do
38
+ str.gsub "\n", ' '
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Junoser
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,54 @@
1
+ module Junoser
2
+ module Xsd
3
+ module Base
4
+ attr_reader :xml
5
+
6
+ OFFSET = ' '
7
+
8
+ def initialize(xml, options={})
9
+ @xml = xml
10
+ @depth = options[:depth] || 0
11
+ end
12
+
13
+ def config
14
+ raise "ERROR: no implementation"
15
+ end
16
+
17
+ def children
18
+ @children||= xml.xpath('./*[not(self::xsd:annotation)]')
19
+ end
20
+
21
+ def root?
22
+ @depth == 1
23
+ end
24
+
25
+ def inspect
26
+ ["#<#{self.class}:0x#{object_id}",
27
+ "xml=#{xml.namespace.prefix}:#{xml.name}" <<
28
+ " attributes=" << Hash[xml.attributes.map {|k, v| [k, v.value] }].to_s <<
29
+ (respond_to?(:label) ? " label=#{label}" : ''),
30
+ "config=[",
31
+ *config.map {|c| c.inspect },
32
+ ']>'].join("\n#{OFFSET*(@depth+1)}")
33
+ end
34
+
35
+
36
+ private
37
+
38
+ def oneliner?
39
+ @oneliner ||= !xml.xpath('./xsd:annotation/xsd:appinfo/flag[contains(text(), "oneliner")]').empty?
40
+ end
41
+
42
+ def has_single_child_of?(klass)
43
+ config.size == 1 && config.first.is_a?(klass)
44
+ end
45
+
46
+ def format(header, content=nil, footer=nil)
47
+ str = OFFSET*@depth << header.to_s
48
+ str << "\n" << content if content
49
+ str << "\n" << OFFSET*@depth << footer if footer
50
+ str
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,33 @@
1
+ require 'junoser/xsd/base'
2
+ require 'junoser/xsd/element'
3
+
4
+ module Junoser
5
+ module Xsd
6
+ class Choice
7
+ include Base
8
+
9
+ def config
10
+ @config ||= children.map {|child|
11
+ case child.name
12
+ when 'element'
13
+ Junoser::Xsd::Element.new(child, depth: @depth+1)
14
+ when 'choice'
15
+ Junoser::Xsd::Choice.new(child, depth: @depth+1)
16
+ else
17
+ raise "ERROR: unknown element: #{child.name}"
18
+ end
19
+ }.compact
20
+ end
21
+
22
+ def to_s
23
+ case
24
+ when config.empty?
25
+ when has_single_child_of?(Junoser::Xsd::Choice)
26
+ format('c(', config.first.config.map(&:to_s).compact.join(",\n"), ')')
27
+ else
28
+ format('c(', config.map(&:to_s).compact.join(",\n"), ')')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,74 @@
1
+ require 'junoser/xsd/base'
2
+ require 'junoser/xsd/element'
3
+ require 'junoser/xsd/sequence'
4
+ require 'junoser/xsd/simple_content'
5
+
6
+ module Junoser
7
+ module Xsd
8
+ class ComplexType
9
+ include Base
10
+
11
+ def initialize(xml, options={})
12
+ super
13
+ raise "ERROR: #{xml.name} shouldn't have name '#{xml['name']}'" if xml['name'] && !root?
14
+ @argument = find_name_element
15
+ end
16
+
17
+ def config
18
+ @config ||= children.map {|child|
19
+ case child.name
20
+ when 'sequence'
21
+ Junoser::Xsd::Sequence.new(child, depth: @depth+1)
22
+ when 'simpleContent'
23
+ Junoser::Xsd::SimpleContent.new(child, depth: @depth+1)
24
+ when 'attribute'
25
+ 'arg'
26
+ else
27
+ raise "ERROR: unknown element: #{child.name}"
28
+ end
29
+ }.compact
30
+ end
31
+
32
+ def to_s
33
+ str = config.map {|c| c.is_a?(String) ? format(OFFSET + c) : c.to_s }.compact.join("\n")
34
+
35
+ str = case [!argument.nil?, !str.empty?]
36
+ when [true, true]
37
+ format("#{argument} (", str, ')')
38
+ when [true, false]
39
+ format(argument)
40
+ when [false, true]
41
+ simple_argument?(str) ? "#{str}.as(:arg)" : str
42
+ else
43
+ ''
44
+ end
45
+
46
+ oneliner? ? "#{str}.as(:oneline)" : str
47
+ end
48
+
49
+
50
+ private
51
+
52
+ def argument
53
+ return unless @argument
54
+
55
+ arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1).config
56
+ raise "ERROR: argument shouldn't consist of multiple elements" if arg.size > 1
57
+
58
+ if root?
59
+ arg.first.to_s.strip + ".as(:arg)"
60
+ else
61
+ arg.first.to_s.strip
62
+ end
63
+ end
64
+
65
+ def find_name_element
66
+ xml.xpath('./xsd:sequence/xsd:element[@name="name"]').remove.first
67
+ end
68
+
69
+ def simple_argument?(str)
70
+ root? && str =~ /\A\s*arg\z/
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,100 @@
1
+ require 'active_support/inflector'
2
+ require 'junoser/xsd/base'
3
+ require 'junoser/xsd/complex_type'
4
+
5
+ module Junoser
6
+ module Xsd
7
+ class Element
8
+ include Base
9
+
10
+ def initialize(xml, options={})
11
+ super
12
+ @argument = find_name_element || find_single_simple_type || find_simple_type_attribute
13
+ end
14
+
15
+ def config
16
+ @config ||= children.map {|child|
17
+ case child.name
18
+ when 'complexType'
19
+ Junoser::Xsd::ComplexType.new(child, depth: @depth+1)
20
+ when 'simpleType'
21
+ 'arg'
22
+ else
23
+ raise "ERROR: unknown element: #{child.name}"
24
+ end
25
+ }.compact
26
+ end
27
+
28
+ def to_s
29
+ str = config.map {|c| c.is_a?(String) ? format(OFFSET + c) : c.to_s }.compact.join("\n")
30
+
31
+ str = case
32
+ when str.empty? && xml['type']
33
+ l = label ? "#{label} (" : '('
34
+ format(l, format(OFFSET + xml['type'].underscore), ')')
35
+ when str.empty?
36
+ format(label)
37
+ else
38
+ l = label ? "#{label} (" : '('
39
+ format(l, str, ')')
40
+ end
41
+
42
+ oneliner? ? "#{str}.as(:oneline)" : str
43
+ end
44
+
45
+ def content
46
+ str = config.map {|c| c.is_a?(String) ? format(OFFSET + c) : c.to_s }.compact.join("\n")
47
+
48
+ case
49
+ when str.empty? && xml['type']
50
+ format(OFFSET + xml['type'].underscore)
51
+ when str.empty?
52
+ ''
53
+ else
54
+ str
55
+ end
56
+ end
57
+
58
+
59
+ private
60
+
61
+ def label
62
+ return unless xml['name']
63
+
64
+ %["#{xml['name']}" #{argument}].strip
65
+ end
66
+
67
+ def argument
68
+ return unless @argument
69
+
70
+ case
71
+ when @argument.is_a?(String)
72
+ @argument
73
+ when @argument.name == 'simpleType'
74
+ 'arg'
75
+ else
76
+ arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1).config
77
+ raise "ERROR: argument shouldn't consist of multiple elements" if arg.size > 1
78
+ arg.first.to_s.strip
79
+ end
80
+ end
81
+
82
+ def find_name_element
83
+ xml.xpath('./xsd:complexType/xsd:sequence/xsd:element[@name="name"]').remove.first
84
+ end
85
+
86
+ def find_single_simple_type
87
+ simples = xml.xpath('./xsd:simpleType').remove
88
+ raise "ERROR: Element shouldn't have simpleType child and another" if simples.size > 1
89
+ simples.first
90
+ end
91
+
92
+ def find_simple_type_attribute
93
+ if xml['type'] =~ /^xsd:.*/
94
+ xml.remove_attribute 'type'
95
+ 'arg'
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,30 @@
1
+ require 'junoser/xsd/base'
2
+
3
+ module Junoser
4
+ module Xsd
5
+ class Enumeration
6
+ include Base
7
+
8
+ def initialize(xml, options={})
9
+ super
10
+ end
11
+
12
+ def config
13
+ raise "ERROR: unknown Enumeration format" if children.size > 1
14
+
15
+ has_match? ? 'arg' : %["#{xml['value']}"]
16
+ end
17
+
18
+ def to_s
19
+ format(OFFSET + config)
20
+ end
21
+
22
+
23
+ private
24
+
25
+ def has_match?
26
+ return true unless xml.xpath('.//match').empty?
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ require 'active_support/inflector'
2
+ require 'junoser/xsd/complex_type'
3
+ require 'junoser/xsd/element'
4
+ require 'nokogiri'
5
+
6
+ module Junoser
7
+ module Xsd
8
+ module Parsable
9
+ def to_config
10
+ rule = "rule(:#{self['name'].underscore}) do\n"
11
+
12
+ case name
13
+ when 'complexType'
14
+ rule << Junoser::Xsd::ComplexType.new(self, depth: 1).to_s
15
+ when 'element'
16
+ rule << Junoser::Xsd::Element.new(self, depth: 1).content
17
+ else
18
+ raise "ERROR: unknown element: #{name}"
19
+ end
20
+
21
+ rule << "\nend\n\n"
22
+ end
23
+
24
+ def remove_unused
25
+ xpath('/xsd:schema/*[self::xsd:import]').remove
26
+ xpath('//xsd:element[@ref="undocumented" or @ref="junos:comment"]').remove
27
+ self
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Nokogiri::XML::Element.include Junoser::Xsd::Parsable
@@ -0,0 +1,34 @@
1
+ require 'junoser/xsd/base'
2
+ require 'junoser/xsd/enumeration'
3
+
4
+ module Junoser
5
+ module Xsd
6
+ class Restriction
7
+ include Base
8
+
9
+ def children
10
+ @children||= xml.xpath('./*[not(self::xsd:annotation or ' +
11
+ 'self::xsd:minInclusive or self::xsd:maxInclusive or ' +
12
+ 'self::xsd:minLength or self::xsd:maxLength)]')
13
+ end
14
+
15
+ def config
16
+ @config ||= children.map {|child|
17
+ case child.name
18
+ when 'enumeration'
19
+ Junoser::Xsd::Enumeration.new(child, depth: @depth+1)
20
+ else
21
+ raise "ERROR: unknown element: #{child.name}"
22
+ end
23
+ }.compact
24
+ end
25
+
26
+ def to_s
27
+ return format('arg') if config.empty?
28
+
29
+ str = '(' + config.map {|c| c.to_s.strip }.compact.join(' | ') + ')'
30
+ format(str)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ require 'junoser/xsd/base'
2
+ require 'junoser/xsd/choice'
3
+ require 'junoser/xsd/element'
4
+
5
+ module Junoser
6
+ module Xsd
7
+ class Sequence
8
+ include Base
9
+
10
+ def config
11
+ @config ||= children.map {|child|
12
+ case child.name
13
+ when 'choice'
14
+ Junoser::Xsd::Choice.new(child, depth: @depth+1)
15
+ when 'element'
16
+ Junoser::Xsd::Element.new(child, depth: @depth+1)
17
+ when 'any'
18
+ 'any'
19
+ else
20
+ raise "ERROR: unknown element: #{child.name}"
21
+ end
22
+ }.compact
23
+ end
24
+
25
+ def to_s
26
+ case
27
+ when config.empty?
28
+ when has_single_child_of?(Junoser::Xsd::Choice)
29
+ child = config.first
30
+ str = child.config.map(&:to_s).compact.join(",\n")
31
+ format('sc(', str, ')') unless str.empty?
32
+ else
33
+ str = config.map {|c| c.is_a?(String) ? format(OFFSET + c) : c.to_s }.compact.join(",\n")
34
+ format('s(', str, ')')
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,31 @@
1
+ require 'junoser/xsd/base'
2
+ require 'junoser/xsd/restriction'
3
+
4
+ module Junoser
5
+ module Xsd
6
+ class SimpleContent
7
+ include Base
8
+
9
+ def config
10
+ @config ||= children.map {|child|
11
+ case child.name
12
+ when 'restriction'
13
+ Junoser::Xsd::Restriction.new(child, depth: @depth+1)
14
+ when 'extension'
15
+ 'arg'
16
+ else
17
+ raise "ERROR: unknown element: #{child.name}"
18
+ end
19
+ }.compact
20
+ end
21
+
22
+ def to_s
23
+ return if config.empty?
24
+ raise "ERROR: simpleContent shouldn't have multiple children" if config.size > 1
25
+
26
+ child = config.first
27
+ child.is_a?(String) ? format(OFFSET + child) : child.to_s
28
+ end
29
+ end
30
+ end
31
+ end
data/lib/junoser.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'junoser/display'
2
+ require 'junoser/parser'
3
+ require 'junoser/version'