junoser 0.3.9 → 0.4.0

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.
data/lib/junoser/ruler.rb CHANGED
@@ -46,64 +46,96 @@ module Junoser
46
46
 
47
47
  str.gsub! /"groups" \(\s*s\(\s*any\s*\)\s*\)/, 'a("groups", arg, configuration)'
48
48
 
49
- %w[as-number confederation-as metric-value limit-threshold filename filter-name class-name classifier-name link-subscription per-traffic-class-bandwidth template-name].each do |key|
50
- str.gsub! %["#{key}" arg], 'arg'
51
- end
52
-
53
49
  str.gsub! '"equal-literal"', '"="'
54
50
  str.gsub! '"plus-literal"', '"+"'
55
51
  str.gsub! '"minus-literal"', '"-"'
56
52
 
57
- str.gsub!(/\((.*) \| "name"\)/) { "(#$1 | arg)" }
58
- str.gsub! '"vlan" ("all" | "vlan-name")', '"vlan" ("all" | arg)'
59
- str.gsub!(/\((.*) \| "vlan-id"\)/) { "(#$1 | arg)" }
53
+ #
54
+ # Statements can be quoted
55
+ #
60
56
  str.gsub!(/("ssh-\S+") arg/) { "#$1 (quote | arg)" }
57
+ str.gsub! '"message" arg', '"message" (quote | arg)'
61
58
  str.gsub! '"description" arg', '"description" (quote | arg)'
62
59
  str.gsub! '"as-path-prepend" arg', '"as-path-prepend" (quote | arg)'
63
- str.gsub!(/arg \| (".*")/) { "#$1 | arg" }
64
- str.gsub! '"dhcp-service" (', '("dhcp-service" | "dhcp") ('
65
60
 
66
- str.gsub!(/"inet"(.*)"inet6"/) { %["inet6"#$1"inet"] }
67
- str.gsub!(/"icmp"(.*)"icmp6"/) { %["icmp6"#$1"icmp"] }
68
- str.gsub!(/"icmp"(.*)"icmpv6"/) { %["icmpv6"#$1"icmp"] }
69
- str.gsub!(/"http"(.*)"https"/) { %["https"#$1"http"] }
70
- str.gsub!(/"snmp"(.*)"snmptrap"/) { %["snmptrap"#$1"snmp"] }
71
- %w[ccc ethernet-over-atm tcc vpls bridge].each do |encap|
72
- str.gsub!(/"ethernet"(.*)"ethernet-#{encap}"/) { %["ethernet-#{encap}"#$1"ethernet"] }
61
+ str.gsub!(/^(\s*)"as-path" arg \(\s*c\(\s*arg/) do
62
+ format(['"as-path" arg (',
63
+ ' c(',
64
+ ' quote | arg'], $1)
73
65
  end
74
- str.gsub! '"icmp6" |', '"icmp6" | "icmpv6" |'
75
- str.gsub!(/"cspf"(.*)"cspf-link"/) { %["cspf-link"#$1"cspf"] }
76
- str.gsub!(/"route-filter" (\(\s*control_route_filter_type\s*\))/) { %["route-filter" arg #{$1}.as(:oneline)] }
77
- str.gsub!(/"source-address-filter" (\(\s*control_source_address_filter_type\s*\))/) { %["source-adress-filter" arg #{$1}.as(:oneline)] }
78
66
 
79
- %w[teardown hold-time stub].each do |key|
80
- str.gsub!(/^(\s*"#{key}" \(\s*)c\(/) { "#{$1}sc(" }
67
+ str.gsub!(/^rule\(:regular_expression\) do\s*((?!end).)*\s*end/) do
68
+ <<~EOS
69
+ rule(:regular_expression) do
70
+ (quote | arg).as(:arg)
71
+ end
72
+ EOS
81
73
  end
82
- %w[file confederation].each do |key|
83
- str.gsub!(/^(\s*"#{key}" \(\s*)c\(\s*arg,/) { "#{$1}sca(" }
74
+
75
+ str.gsub!(/^rule\(:login_user_object\) do\s*arg\.as\(:arg\) \(\s*c\(\s*"full-name" arg,/) do
76
+ <<~EOS
77
+ rule(:login_user_object) do
78
+ arg.as(:arg) (
79
+ sc(
80
+ "full-name" (quote | arg),
81
+ EOS
84
82
  end
85
- %w[exact longer orlonger].each do |key|
86
- str.gsub!(/^(\s*"#{key}") arg/) { "#{$1}" }
83
+
84
+ str.gsub!(/^(\s*)"location" arg,\s*"contact" arg,/) do
85
+ format(['"location" (quote | arg),',
86
+ '"contact" (quote | arg),'], $1)
87
87
  end
88
88
 
89
+ str.gsub!(/^(\s*)"as-path" \(\s*c\(\s*"path" arg/) do
90
+ format(['"as-path" (',
91
+ ' c(',
92
+ ' "path" (quote | arg)'], $1)
93
+ end
89
94
 
90
- str.gsub!(/^(\s*)"ieee-802.3ad" \(\s*c\(\s*"lacp" \(\s*c\(/) do
91
- format(['"802.3ad" (',
92
- ' ca(',
93
- ' "lacp" (',
94
- ' c(',
95
- ' "force-up",'], $1)
95
+ str.gsub!(/^(\s*)prefix_list_items,\s*"apply-path" arg/) do
96
+ format(['"apply-path" (quote | arg),',
97
+ 'prefix_list_items'], $1)
96
98
  end
97
- str.gsub!(/^(\s*)"as-path" arg \(\s*c\(\s*arg/) do
98
- format(['"as-path" arg (',
99
- ' c(',
100
- ' quote | arg'], $1)
99
+
100
+ #
101
+ # "arg" matches anything so move to the end
102
+ #
103
+ str.gsub!(/arg \| (".*")/) { "#$1 | arg" }
104
+ str.gsub!(/^(\s*)c\(\s*arg,$/) { "#{$1}ca(" }
105
+ str.gsub!(/(rule\(:control_route_filter_type\) do\s*)s\(\s*arg,/) { "#{$1}b(" }
106
+ str.gsub!(/(rule\(:control_source_address_filter_type\) do\s*)s\(\s*arg,/) { "#{$1}b(" }
107
+ str.gsub!(/^(rule\(:trace_file_type\) do\s*)ca\(/) { "#{$1}sca(" }
108
+
109
+ str.gsub!(/^(rule\(:archive_object\) do\s*)c\(/) { "#{$1}sc(" }
110
+ str.gsub!(/^(rule\(:server_group_type\) do\s*)c\(\s*c\(\s*arg\s*\)\s*\)/) { "#{$1}s(arg, arg)" }
111
+
112
+ str.gsub!(/^(rule\(:rib_group_inet_type\) do)\s*c\(\s*arg/) do
113
+ format([$1,
114
+ ' ca(',
115
+ ' a(arg, arg)'], '')
101
116
  end
102
- str.gsub!(/^(\s*)"priority" \(\s*c\(\s*arg,\s*arg\s*\)/) do
117
+
118
+ # Fix overkill
119
+ str.gsub!(/^(\s*)"priority" \(\s*ca\(\s*arg\s*\)/) do
103
120
  format(['"priority" (',
104
121
  ' a(arg, arg)', $1])
105
122
  end
106
- str.gsub!(/^(\s*)"path" arg \(\s*c\(\s*c\(\s*"abstract",\s*c\(\s*"loose",\s*"loose-link",\s*"strict"\s*\)\s*\)\.as\(:oneline\)/) do
123
+
124
+ #
125
+ # Longer pattern first
126
+ #
127
+ str.gsub!(/"cspf"(.*)"cspf-link"/) { %["cspf-link"#$1"cspf"] }
128
+ str.gsub!(/"http"(.*)"https"/) { %["https"#$1"http"] }
129
+ str.gsub!(/"inet"(.*)"inet6"/) { %["inet6"#$1"inet"] }
130
+ str.gsub!(/"icmp"(.*)"icmp6"/) { %["icmp6"#$1"icmp"] }
131
+ str.gsub!(/"icmp"(.*)"icmpv6"/) { %["icmpv6"#$1"icmp"] }
132
+ str.gsub!(/"snmp"(.*)"snmptrap"/) { %["snmptrap"#$1"snmp"] }
133
+
134
+ %w[ccc ethernet-over-atm tcc vpls bridge].each do |encap|
135
+ str.gsub!(/"ethernet"(.*)"ethernet-#{encap}"/) { %["ethernet-#{encap}"#$1"ethernet"] }
136
+ end
137
+
138
+ str.gsub!(/^(\s*)"path" arg \(\s*c\(\s*sc\(\s*"abstract",\s*c\(\s*"loose",\s*"loose-link",\s*"strict"\s*\)\s*\)\.as\(:oneline\)/) do
107
139
  format(['"path" arg (',
108
140
  ' c(',
109
141
  ' b(',
@@ -119,48 +151,51 @@ module Junoser
119
151
  ' )', $1])
120
152
  end
121
153
 
122
- str.gsub!(/^(\s*)c\(\s*\("default"\)\s*\)/) do
123
- format(['c(',
124
- ' ("default" | arg)',
125
- ')'], $1)
126
- end
154
+ #
155
+ # Fix .xsd: Elements without "nokeyword" flag
156
+ #
157
+ str.gsub!(/\((.*) \| "name"\)/) { "(#$1 | arg)" }
158
+ str.gsub! '"vlan" ("all" | "vlan-name")', '"vlan" ("all" | arg)'
159
+ str.gsub!(/\((.*) \| "vlan-id"\)/) { "(#$1 | arg)" }
127
160
 
128
- str.gsub!(/^rule\(:regular_expression\) do\s*((?!end).)*\s*end/) do
129
- format(['rule(:regular_expression) do',
130
- ' (quote | arg).as(:arg)',
131
- 'end'])
132
- end
133
- str.gsub!(/^rule\(:login_user_object\) do\s*arg\.as\(:arg\) \(\s*c\(\s*"full-name" arg,/) do
134
- format(['rule(:login_user_object) do',
135
- ' arg.as(:arg) (',
136
- ' sc(',
137
- ' "full-name" (quote | arg),'])
161
+ %w[filename].each do |key|
162
+ str.gsub! %["#{key}" arg], 'arg'
138
163
  end
139
164
 
140
- str.gsub!(/(rule\(:control_route_filter_type\) do\s*)s\(\s*arg,/) { "#{$1}b(" }
141
- str.gsub!(/(rule\(:control_source_address_filter_type\) do\s*)s\(\s*arg,/) { "#{$1}b(" }
142
- str.gsub!(/^(rule\(:trace_file_type\) do\s*)c\(\s*arg,/) { "#{$1}sca(" }
143
- str.gsub!(/^(rule\(:archive_object\) do\s*)c\(/) { "#{$1}sc(" }
144
- str.gsub!(/^(rule\(:server_group_type\) do\s*)c\(\s*c\(\s*arg\s*\)\s*\)/) { "#{$1}s(arg, arg)" }
145
- str.gsub!(/^(rule\(:rib_group_inet_type\) do)\s*c\(\s*arg/) do
146
- format([$1,
147
- ' ca(',
148
- ' a(arg, arg)'], '')
149
- end
165
+ # "filename" fix above leaves "arg". Move to the end
166
+ str.gsub!(/^(rule\(:esp_trace_file_type\) do\s*)c\(\s*arg,/) { "#{$1}ca(" }
150
167
 
151
- str.gsub!(/^(\s*)c\(\s*arg,$/) { "#{$1}ca(" }
168
+ # Fix .xsd: system processes dhcp is valid on some platforms
169
+ str.gsub! '"dhcp-service" (', '("dhcp-service" | "dhcp") ('
152
170
 
153
- str.gsub!(/^(\s*)"location" arg,\s*"contact" arg,/) do
154
- format(['"location" (quote | arg),',
155
- '"contact" (quote | arg),'], $1)
171
+ # Fix .xsd: "icmpv6" is also acceptable
172
+ str.gsub! '"icmp6" |', '"icmp6" | "icmpv6" |'
173
+
174
+ #
175
+ # Fix .xsd: "arg" is missing
176
+ #
177
+ str.gsub!(/"route-filter" (\(\s*control_route_filter_type\s*\))/) { %["route-filter" arg #{$1}.as(:oneline)] }
178
+ str.gsub!(/"source-address-filter" (\(\s*control_source_address_filter_type\s*\))/) { %["source-adress-filter" arg #{$1}.as(:oneline)] }
179
+ %w[file].each do |key|
180
+ str.gsub!(/^(\s*"#{key}" \(\s*)c\(\s*arg,/) { "#{$1}sca(" }
156
181
  end
157
182
 
158
- str.gsub!(/^(\s*)"as-path" \(\s*c\(\s*"path" arg/) do
159
- format(['"as-path" (',
160
- ' c(',
161
- ' "path" (quote | arg)'], $1)
183
+ # Fix .xsd: Unnecessary "arg" is added
184
+ %w[exact longer orlonger].each do |key|
185
+ str.gsub!(/^(\s*"#{key}") arg/) { "#{$1}" }
186
+ end
187
+
188
+ # Fix .xsd: "ieee-802.3ad" is invalid
189
+ str.gsub! '"ieee-802.3ad"', '"802.3ad"'
190
+
191
+ # Fix .xsd: "class-of-service interfaces all unit * classifiers exp foo"
192
+ str.gsub!(/^(\s*)sc\(\s*\("default"\)\s*\)/) do
193
+ format(['c(',
194
+ ' ("default" | arg)',
195
+ ')'], $1)
162
196
  end
163
197
 
198
+ # Fix .xsd: "from-zone" arg is also required
164
199
  str.gsub!(/^(\s*)"policy" \(\s*s\(\s*arg,\s*"to-zone-name" arg,\s*c\(\s*"policy" \(\s*policy_type\s*\)\s*\)/) do
165
200
  format(['b(s("from-zone", arg, "to-zone", arg),',
166
201
  ' b("policy", policy_type',
@@ -2,62 +2,12 @@ require 'junoser'
2
2
  require 'parslet'
3
3
 
4
4
  module Junoser
5
- class DeleteTransformer < Parslet::Transform
6
- rule(config: simple(:config)) do
7
- "(#{config.to_s} .*"
8
- end
9
-
10
- rule(config: sequence(:configs)) do
11
- configs.join("\n")
12
- end
13
-
14
- rule(arg: simple(:arg)) do
15
- arg
16
- end
17
-
18
- rule(label: simple(:label)) do
19
- ")#{Regexp.escape(label.to_s)}"
20
- end
21
-
22
- rule(label: simple(:label), child: simple(:child)) do
23
- "#{Regexp.escape(label.to_s)} #{child}"
24
- end
25
-
26
- rule(label: simple(:label), child: sequence(:children)) do
27
- %[#{Regexp.escape(label.to_s)} #{children.join(' ')}]
28
- end
29
-
30
- rule(statement: simple(:statement), argument: simple(:argument)) do
31
- "#{statement} #{argument}"
32
- end
33
-
34
- rule(statement: simple(:statement), argument: sequence(:arguments)) do
35
- %[#{statement} #{arguments.join(' ')}]
36
- end
37
-
38
- rule(oneline: simple(:str)) do
39
- str
40
- end
41
-
42
- rule(oneline: sequence(:strs)) do
43
- strs.join(' ')
44
- end
45
-
46
- rule(enum: simple(:str)) do
47
- str
48
- end
49
-
50
- rule(enum: sequence(:strs)) do
51
- strs.join(' ')
52
- end
53
- end
54
-
55
5
  class Squash
56
6
  def initialize(io_or_string)
57
7
  @input = io_or_string
58
8
  @lines = []
59
9
  @parser = Junoser::Parser.new
60
- @transformer = DeleteTransformer.new
10
+ @transformer = Junoser::Transformer.new
61
11
  end
62
12
 
63
13
  def transform
@@ -65,11 +15,16 @@ module Junoser
65
15
  config.each do |l|
66
16
  l.strip!
67
17
  case l
68
- when /^set /
69
- @lines << l
70
- when /^delete /
71
- to_delete = @parser.parse(l.gsub(/^delete /, 'set '))
72
- delete_lines @transformer.apply(to_delete)
18
+ when /^(set|deactivate) /
19
+ @lines << l
20
+ when /^delete /
21
+ delete_lines delete_pattern(l.gsub(/^delete /, 'set '))
22
+ when /^activate /
23
+ delete_lines l.gsub(/^activate /, 'deactivate ')
24
+ when /^insert (.*) before (.*)/
25
+ insert_before "set #{$1}", $2
26
+ when /^insert (.*) after (.*)/
27
+ insert_after "set #{$1}", $2
73
28
  end
74
29
  end
75
30
 
@@ -80,7 +35,7 @@ module Junoser
80
35
  private
81
36
 
82
37
  def remove_subcommand(lines)
83
- lines.each_with_index do |l,i|
38
+ lines.each_with_index do |l, i|
84
39
  lines[i..-1].each do |l2|
85
40
  if l.include?(l2) and l != l2
86
41
  lines.delete(l2)
@@ -94,5 +49,44 @@ module Junoser
94
49
  l.sub!(/#{pattern}/) { $1 }
95
50
  end
96
51
  end
52
+
53
+ def split_last_token(line)
54
+ tokens = join_arg(@transformer.apply(@parser.parse(line))).split("\n")
55
+ tokens.map! { |t|
56
+ t.gsub!(/arg\((.*)\)/) { "#$1" } # Strip arg
57
+ Regexp.escape(t.strip)
58
+ }
59
+
60
+ [tokens[0..-2].join(' '), tokens.last]
61
+ end
62
+
63
+ # Ported from lib/junoser/display/config_store.rb
64
+ def join_arg(str)
65
+ str.gsub!(/\narg\((.*)\)$/) { " #$1" }
66
+ str.gsub!(/arg\((.*)\)/) { "#$1" }
67
+ str
68
+ end
69
+
70
+ def delete_pattern(line)
71
+ line, last_token = split_last_token(line)
72
+ "(#{line}\s+)#{last_token}.*"
73
+ end
74
+
75
+ def insert_before(statement_to_insert, key_statement)
76
+ key_tokens = key_statement.strip.split
77
+ key_statement = (statement_to_insert.strip.split[0..-(key_tokens.size+1)] + key_tokens).join(' ')
78
+
79
+ lines_to_insert = @lines.select { |l| l.include?(statement_to_insert) }
80
+ @lines.reject! { |l| l.include?(statement_to_insert) }
81
+
82
+ key_index = @lines.index { |l| l.include?(key_statement) }
83
+ @lines.insert(key_index, lines_to_insert).flatten!
84
+ end
85
+
86
+ def insert_after(pattern_to_insert, key_token)
87
+ @lines.reverse!
88
+ insert_before pattern_to_insert, key_token
89
+ @lines.reverse!
90
+ end
97
91
  end
98
92
  end
@@ -1,3 +1,3 @@
1
1
  module Junoser
2
- VERSION = "0.3.9"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,13 +1,14 @@
1
1
  module Junoser
2
2
  module Xsd
3
3
  module Base
4
- attr_reader :xml
4
+ attr_reader :xml, :parent
5
5
 
6
6
  OFFSET = ' '
7
7
 
8
8
  def initialize(xml, options={})
9
9
  @xml = xml
10
10
  @depth = options[:depth] || 0
11
+ @parent = options[:parent]
11
12
  end
12
13
 
13
14
  def config
@@ -32,13 +33,13 @@ module Junoser
32
33
  ']>'].join("\n#{OFFSET*(@depth+1)}")
33
34
  end
34
35
 
35
-
36
- private
37
-
38
36
  def oneliner?
39
37
  @oneliner ||= !xml.xpath('./xsd:annotation/xsd:appinfo/flag[text()="oneliner"]').empty?
40
38
  end
41
39
 
40
+
41
+ private
42
+
42
43
  def nokeyword?
43
44
  @nokeyword ||= !xml.xpath('./xsd:annotation/xsd:appinfo/flag[text()="nokeyword"]').empty?
44
45
  end
@@ -10,9 +10,9 @@ module Junoser
10
10
  @config ||= children.map {|child|
11
11
  case child.name
12
12
  when 'element'
13
- Junoser::Xsd::Element.new(child, depth: @depth+1)
13
+ Junoser::Xsd::Element.new(child, depth: @depth+1, parent: self)
14
14
  when 'choice'
15
- Junoser::Xsd::Choice.new(child, depth: @depth+1)
15
+ Junoser::Xsd::Choice.new(child, depth: @depth+1, parent: self)
16
16
  else
17
17
  raise "ERROR: unknown element: #{child.name}"
18
18
  end
@@ -29,6 +29,10 @@ module Junoser
29
29
  format('c(', config.map(&:to_s).join(",\n"), ')')
30
30
  end
31
31
  end
32
+
33
+ def unbounded?
34
+ xml['maxOccurs'] == 'unbounded'
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -18,9 +18,9 @@ module Junoser
18
18
  @config ||= children.map {|child|
19
19
  case child.name
20
20
  when 'sequence'
21
- Junoser::Xsd::Sequence.new(child, depth: @depth+1)
21
+ Junoser::Xsd::Sequence.new(child, depth: @depth+1, parent: self)
22
22
  when 'simpleContent'
23
- Junoser::Xsd::SimpleContent.new(child, depth: @depth+1)
23
+ Junoser::Xsd::SimpleContent.new(child, depth: @depth+1, parent: self)
24
24
  when 'attribute'
25
25
  'arg'
26
26
  else
@@ -52,7 +52,7 @@ module Junoser
52
52
  def argument
53
53
  return unless @argument
54
54
 
55
- arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1).config
55
+ arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1, parent: self).config
56
56
  raise "ERROR: argument shouldn't consist of multiple elements" if arg.size > 1
57
57
 
58
58
  if root?
@@ -16,9 +16,9 @@ module Junoser
16
16
  @config ||= children.map {|child|
17
17
  case child.name
18
18
  when 'complexType'
19
- Junoser::Xsd::ComplexType.new(child, depth: @depth+1)
19
+ Junoser::Xsd::ComplexType.new(child, depth: @depth+1, parent: self)
20
20
  when 'simpleType'
21
- Junoser::Xsd::SimpleType.new(child, depth: @depth+1)
21
+ Junoser::Xsd::SimpleType.new(child, depth: @depth+1, parent: self)
22
22
  else
23
23
  raise "ERROR: unknown element: #{child.name}"
24
24
  end
@@ -70,7 +70,7 @@ module Junoser
70
70
  when @argument.name == 'simpleType'
71
71
  'arg'
72
72
  else
73
- arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1).config
73
+ arg = Junoser::Xsd::Element.new(@argument, depth: @depth+1, parent: self).config
74
74
  raise "ERROR: argument shouldn't consist of multiple elements" if arg.size > 1
75
75
  arg.first.to_s.strip
76
76
  end
@@ -97,6 +97,10 @@ module Junoser
97
97
 
98
98
  def documentation
99
99
  @documentation ||= xml.xpath('./xsd:annotation/xsd:documentation').text
100
+
101
+ # Translate multiline documentation into a single line to make it parsable in further processes
102
+ @documentation.gsub! /\n\s*/, ' '
103
+
100
104
  @documentation.empty? ? nil : @documentation
101
105
  end
102
106
  end