rubyang 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +3 -0
- data/.gitignore +1 -0
- data/README.md +106 -2
- data/lib/rubyang/cli.rb +112 -101
- data/lib/rubyang/cli/parser.rb +1 -1
- data/lib/rubyang/component/base.rb +35 -0
- data/lib/rubyang/component/example.rb +15 -0
- data/lib/rubyang/database/component_manager.rb +77 -0
- data/lib/rubyang/database/data_tree.rb +22 -2
- data/lib/rubyang/database/schema_tree.rb +34 -3
- data/lib/rubyang/model/parser/parser.tab.rb +248 -248
- data/lib/rubyang/model/parser/parser.y +1 -1
- data/lib/rubyang/restapi.rb +8 -1
- data/lib/rubyang/restapi/app.rb +72 -0
- data/lib/rubyang/server.rb +8 -0
- data/lib/rubyang/server/base.rb +30 -0
- data/lib/rubyang/server/example.rb +9 -0
- data/lib/rubyang/version.rb +1 -1
- data/lib/rubyang/webui/public/js/webui.js +95 -3
- data/lib/rubyang/yang/rubyang.yang +33 -0
- data/rubyang.gemspec +5 -5
- metadata +11 -4
- data/lib/rubyang/restapi/httpd.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 000a93a9ac2a85ec169ecbe1c54112b330e026b4
|
4
|
+
data.tar.gz: 3e81e19d9247fe5459de45aa57120508371d12fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d5ab6836505d061b019f844893a17fcfc4c4761eb2bf7e1d140670d8aa97970faacb952c8bffd10a337a80efe07d7be26c15ad3b50048f7c6697432d60e8668
|
7
|
+
data.tar.gz: ad6e739291421f469746f786755ef8288821af5a30cbda43b3c0fb757434019eddda5668422ff315b587002e23e82324c7251374d3674a2b04ee46450738e2bd
|
data/.codeclimate.yml
CHANGED
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# rubyang
|
2
2
|
|
3
3
|
[](https://travis-ci.org/hirura/rubyang)
|
4
|
-
[](https://coveralls.io/github/hirura/rubyang?branch=master)
|
5
4
|
[](https://codeclimate.com/github/hirura/rubyang)
|
5
|
+
[](https://codeclimate.com/github/hirura/rubyang/coverage)
|
6
6
|
[](https://codeclimate.com/github/hirura/rubyang)
|
7
|
+
[](https://badge.fury.io/rb/rubyang)
|
7
8
|
|
8
9
|
YANG parser and tree structure data store
|
9
10
|
|
@@ -25,7 +26,110 @@ Or install it yourself as:
|
|
25
26
|
|
26
27
|
## Usage
|
27
28
|
|
28
|
-
|
29
|
+
### Core
|
30
|
+
|
31
|
+
You can use Rubyang as yang database or as server
|
32
|
+
|
33
|
+
To use rubyang as database, add this line to head of your code:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require 'rubyang'
|
37
|
+
```
|
38
|
+
|
39
|
+
You can specify YANG model as String:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
yang = <<EOB
|
43
|
+
module rubyang-example {
|
44
|
+
namespace 'http://rubyang/example';
|
45
|
+
prefix 'rubyang-example';
|
46
|
+
container container1 {
|
47
|
+
leaf leaf1 {
|
48
|
+
type string;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
EOB
|
53
|
+
```
|
54
|
+
|
55
|
+
And prepare DB for configurations:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
db = Rubyang::Database.new
|
59
|
+
```
|
60
|
+
|
61
|
+
You can load YANG model to DB:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
db.load_model Rubyang::Model::Parser.parse( yang )
|
65
|
+
```
|
66
|
+
|
67
|
+
Then configurations can be set with YANG model:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
db.configure.edit( "container1" ).edit( "leaf1" ).set( "hoge" )
|
71
|
+
```
|
72
|
+
|
73
|
+
And you can see configured data in XML format:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
puts db.configure.to_xml( pretty: true )
|
77
|
+
# => <config xmlns='http://rubyang/config/0.1'>
|
78
|
+
# <container1 xmlns='http://rubyang/example'>
|
79
|
+
# <leaf1>hoge</leaf1>
|
80
|
+
# </container1>
|
81
|
+
# </config>
|
82
|
+
```
|
83
|
+
|
84
|
+
And also JSON format:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
puts db.configure.to_json( pretty: true )
|
88
|
+
# => {
|
89
|
+
# "config": {
|
90
|
+
# "container1": {
|
91
|
+
# "leaf1": "hoge"
|
92
|
+
# }
|
93
|
+
# }
|
94
|
+
# }
|
95
|
+
```
|
96
|
+
|
97
|
+
And to use rubyang as server
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
require 'rubyang/server/base'
|
101
|
+
```
|
102
|
+
|
103
|
+
and create some class inheriting Rubyang::Server::Base class
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
class Example < Rubyang::Server::Base
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
now you can run server
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
example = Example.new
|
114
|
+
example.run
|
115
|
+
```
|
116
|
+
|
117
|
+
you can connect to this server with cli.rb
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
require 'rubyang/cli'
|
121
|
+
|
122
|
+
cli = Rubyang::Cli.new
|
123
|
+
cli.run
|
124
|
+
```
|
125
|
+
|
126
|
+
### Additional Component
|
127
|
+
|
128
|
+
Rubyang provides component mechanism to develop some component for users.
|
129
|
+
Users can develop some components and load those components.
|
130
|
+
The components which is defined as "hook commit" in configuraion are called when commit is executed.
|
131
|
+
|
132
|
+
Example component is stored in "/path/to/rubyang/component/example.rb"
|
29
133
|
|
30
134
|
## Development
|
31
135
|
|
data/lib/rubyang/cli.rb
CHANGED
@@ -1,120 +1,131 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
require 'drb/drb'
|
3
4
|
require 'readline'
|
4
5
|
|
5
|
-
require_relative 'cli/parser'
|
6
|
-
|
7
6
|
require_relative '../rubyang'
|
7
|
+
require_relative 'cli/parser'
|
8
8
|
|
9
9
|
module Rubyang
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
class Cli
|
11
|
+
def initialize
|
12
|
+
@sock_file = "/tmp/rubyang/server/Example.sock"
|
13
|
+
@db = DRbObject.new_with_uri( "drbunix:#{@sock_file}" )
|
13
14
|
|
14
|
-
|
15
|
-
model = Rubyang::Model::Parser.parse( File.open( target_yang, 'r' ).read )
|
16
|
-
db = Rubyang::Database.new
|
17
|
-
db.load_model model
|
18
|
-
$config_tree = db.configure
|
15
|
+
@config_tree = @db.configure
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def set config_tree, tokens
|
24
|
-
set_recursive config_tree, tokens
|
25
|
-
end
|
26
|
-
|
27
|
-
def set_recursive config_tree, tokens
|
28
|
-
return config_tree if tokens.size == 0
|
29
|
-
token = tokens[0]
|
30
|
-
case tokens[1..-1].size
|
31
|
-
when 0
|
32
|
-
config_tree.set token
|
33
|
-
else
|
34
|
-
child_tree = config_tree.edit token
|
35
|
-
set_recursive child_tree, tokens[1..-1]
|
36
|
-
end
|
37
|
-
end
|
17
|
+
@fo = File.open("#{File.dirname(__FILE__)}/cli/log.txt", 'w')
|
18
|
+
@fo.sync = true
|
38
19
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
20
|
+
#Readline.basic_word_break_characters = ""
|
21
|
+
#Readline.completer_word_break_characters = ""
|
22
|
+
Readline.completion_append_character = " "
|
23
|
+
Readline.completion_proc = proc { |buf|
|
24
|
+
@fo.puts "line_buffer: #{Readline.line_buffer}"
|
25
|
+
begin
|
26
|
+
tokens = Rubyang::Cli::Parser.parse( Readline.line_buffer )
|
27
|
+
rescue
|
28
|
+
next
|
29
|
+
end
|
30
|
+
command_type = tokens.shift
|
31
|
+
case command_type
|
32
|
+
when /^set$/
|
33
|
+
if tokens.size == 0
|
34
|
+
all_candidates = ['set']
|
35
|
+
candidates = all_candidates
|
36
|
+
else
|
37
|
+
value = tokens.last.to_s
|
38
|
+
all_candidates = get_candidates( @config_tree, tokens )
|
39
|
+
candidates = all_candidates.grep(/^#{Regexp.escape(value.to_s)}/)
|
40
|
+
end
|
41
|
+
when /^show$/
|
42
|
+
format = tokens[0].to_s
|
43
|
+
all_candidates = ['xml', 'json']
|
44
|
+
candidates = all_candidates.grep(/^#{Regexp.escape(format.to_s)}/)
|
45
|
+
candidates
|
46
|
+
else
|
47
|
+
all_candidates = ['set', 'show']
|
48
|
+
candidates = all_candidates.grep(/^#{Regexp.escape(command_type.to_s)}/)
|
49
|
+
candidates
|
50
|
+
end
|
51
|
+
@fo.puts "tokens = #{tokens.inspect}"
|
52
|
+
@fo.puts "all_candidates = #{all_candidates.inspect}"
|
53
|
+
@fo.puts "candidates = #{candidates.inspect}"
|
54
|
+
@fo.puts
|
55
|
+
@fo.puts
|
56
|
+
candidates
|
57
|
+
}
|
49
58
|
end
|
50
|
-
else
|
51
|
-
if tokens.size > 1 and config_tree.schema.children.map{ |c| c.arg }.include? token
|
52
|
-
child_tree = config_tree.edit token
|
53
|
-
get_candidates child_tree, tokens[1..-1]
|
54
|
-
else
|
55
|
-
config_tree.schema.children.map{ |c| c.arg }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
59
|
|
60
|
+
def set config_tree, tokens
|
61
|
+
set_recursive config_tree, tokens
|
62
|
+
end
|
60
63
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
command_type = tokens.shift
|
72
|
-
case command_type
|
73
|
-
when /^set$/
|
74
|
-
if tokens.size == 0
|
75
|
-
all_candidates = ['set']
|
76
|
-
candidates = all_candidates
|
77
|
-
else
|
78
|
-
value = tokens.last.to_s
|
79
|
-
all_candidates = get_candidates( $config_tree, tokens )
|
80
|
-
candidates = all_candidates.grep(/^#{Regexp.escape(value.to_s)}/)
|
64
|
+
def set_recursive config_tree, tokens
|
65
|
+
return config_tree if tokens.size == 0
|
66
|
+
token = tokens[0]
|
67
|
+
case tokens[1..-1].size
|
68
|
+
when 0
|
69
|
+
config_tree.set token
|
70
|
+
else
|
71
|
+
child_tree = config_tree.edit token
|
72
|
+
set_recursive child_tree, tokens[1..-1]
|
73
|
+
end
|
81
74
|
end
|
82
|
-
when /^show$/
|
83
|
-
format = tokens[0].to_s
|
84
|
-
all_candidates = ['xml', 'json']
|
85
|
-
candidates = all_candidates.grep(/^#{Regexp.escape(format.to_s)}/)
|
86
|
-
candidates
|
87
|
-
else
|
88
|
-
all_candidates = ['set', 'show']
|
89
|
-
candidates = all_candidates.grep(/^#{Regexp.escape(command_type.to_s)}/)
|
90
|
-
candidates
|
91
|
-
end
|
92
|
-
$fo.puts "tokens = #{tokens.inspect}"
|
93
|
-
$fo.puts "all_candidates = #{all_candidates.inspect}"
|
94
|
-
$fo.puts "candidates = #{candidates.inspect}"
|
95
|
-
$fo.puts
|
96
|
-
$fo.puts
|
97
|
-
candidates
|
98
|
-
}
|
99
75
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
76
|
+
def get_candidates config_tree, tokens
|
77
|
+
@fo.puts config_tree.class
|
78
|
+
@fo.puts tokens.inspect
|
79
|
+
token = tokens[0].to_s
|
80
|
+
case config_tree
|
81
|
+
when Rubyang::Database::DataTree::Leaf
|
82
|
+
if tokens.size == 1
|
83
|
+
[config_tree.value]
|
84
|
+
else
|
85
|
+
[]
|
86
|
+
end
|
87
|
+
else
|
88
|
+
if tokens.size > 1 and config_tree.schema.children.map{ |c| c.arg }.include? token
|
89
|
+
child_tree = config_tree.edit token
|
90
|
+
get_candidates child_tree, tokens[1..-1]
|
91
|
+
else
|
92
|
+
config_tree.schema.children.map{ |c| c.arg }
|
93
|
+
end
|
94
|
+
end
|
110
95
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
96
|
+
|
97
|
+
def run
|
98
|
+
while buf = Readline.readline("> ", true)
|
99
|
+
tokens = Rubyang::Cli::Parser.parse( buf )
|
100
|
+
command_type = tokens.shift
|
101
|
+
case command_type
|
102
|
+
when /^set$/
|
103
|
+
begin
|
104
|
+
set @config_tree, tokens
|
105
|
+
rescue => e
|
106
|
+
puts "Error: #{e}"
|
107
|
+
end
|
108
|
+
when /^show$/
|
109
|
+
show_type = tokens.shift
|
110
|
+
case show_type
|
111
|
+
when /^xml$/
|
112
|
+
puts @config_tree.to_xml( pretty: true )
|
113
|
+
when /^json$/
|
114
|
+
puts @config_tree.to_json( pretty: true )
|
115
|
+
end
|
116
|
+
when /^commit$/
|
117
|
+
begin
|
118
|
+
@config_tree.commit
|
119
|
+
rescue => e
|
120
|
+
puts "Error: #{e}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
118
124
|
end
|
119
125
|
end
|
120
126
|
end
|
127
|
+
|
128
|
+
if __FILE__ == $0
|
129
|
+
cli = Rubyang::Cli.new
|
130
|
+
cli.run
|
131
|
+
end
|
data/lib/rubyang/cli/parser.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
require 'drb/drb'
|
7
|
+
|
8
|
+
module Rubyang
|
9
|
+
module Component
|
10
|
+
class Base
|
11
|
+
include DRb::DRbUndumped
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@rubyang_sock_file = "/tmp/rubyang/server/Example.sock"
|
15
|
+
@db = DRbObject.new_with_uri( "drbunix:#{@rubyang_sock_file}" )
|
16
|
+
|
17
|
+
#@pid = Process.pid
|
18
|
+
@sock_dir = "/tmp/rubyang/component"
|
19
|
+
#@sock_file = "#{@sock_dir}/#{self.class}.#{pid}.sock"
|
20
|
+
@sock_file = "#{@sock_dir}/#{self.class}.sock"
|
21
|
+
|
22
|
+
FileUtils.mkdir_p @sock_dir
|
23
|
+
|
24
|
+
DRb.start_service( "drbunix:#{@sock_file}", self )
|
25
|
+
DRb.thread.join
|
26
|
+
end
|
27
|
+
|
28
|
+
def run
|
29
|
+
end
|
30
|
+
|
31
|
+
def finish
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
class Example < Rubyang::Component::Base
|
6
|
+
def run
|
7
|
+
config = @db.configure
|
8
|
+
File.open( '/tmp/rubyang_component_example.txt', 'w' ){ |fo|
|
9
|
+
fo.puts config.to_xml( pretty: true )
|
10
|
+
}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
example = Example.new
|
15
|
+
example.run
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'drb/drb'
|
5
|
+
|
6
|
+
require_relative '../../rubyang'
|
7
|
+
|
8
|
+
module Rubyang
|
9
|
+
class Database
|
10
|
+
class ComponentManager
|
11
|
+
class Component
|
12
|
+
attr_reader :name, :hook, :path, :sock, :thread, :instance
|
13
|
+
|
14
|
+
def initialize name, hook, path
|
15
|
+
@name = name
|
16
|
+
@hook = hook
|
17
|
+
@path = path
|
18
|
+
@sock = "/tmp/rubyang/component/#{@name}.sock"
|
19
|
+
end
|
20
|
+
|
21
|
+
def start
|
22
|
+
begin
|
23
|
+
@thread = Thread.new( @path ) do |path|
|
24
|
+
stdout, stderr, status = Open3.capture3( "#{RUBY_ENGINE} #{path}" )
|
25
|
+
end
|
26
|
+
10.times{ |i|
|
27
|
+
break if File.socket? @sock
|
28
|
+
sleep 1
|
29
|
+
raise "Load failed: #{@name} : #{@path}" if i == 9
|
30
|
+
}
|
31
|
+
@instance = DRbObject.new_with_uri( "drbunix:#{@sock}" )
|
32
|
+
rescue => e
|
33
|
+
puts "Error: #{e}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def run
|
38
|
+
begin
|
39
|
+
@instance.run
|
40
|
+
rescue => e
|
41
|
+
puts "Error: #{e}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@components = Array.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def update components
|
51
|
+
current_component_names = @components.map{ |c| c.name }
|
52
|
+
new_component_names = components.map{ |c| c[0] }
|
53
|
+
unloading_component_names = current_component_names - new_component_names
|
54
|
+
loading_component_names = new_component_names - current_component_names
|
55
|
+
puts "Load: #{loading_component_names}"
|
56
|
+
puts "Unload: #{unloading_component_names}"
|
57
|
+
unloading_component_names.each{ |n|
|
58
|
+
component = @components.find{ |c| c.name == n }
|
59
|
+
component.thread.kill
|
60
|
+
@components.delete_if{ |c| c.name == n }
|
61
|
+
}
|
62
|
+
loading_component_names.each{ |n|
|
63
|
+
name, hook, path = components.find{ |c| c[0] == n }
|
64
|
+
component = Component.new name, hook, path
|
65
|
+
component.start
|
66
|
+
@components.push component
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def run hook
|
71
|
+
@components.select{ |c| c.hook == hook }.each{ |c|
|
72
|
+
c.run
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|