rubyang 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/hirura/rubyang.svg?branch=master)](https://travis-ci.org/hirura/rubyang)
|
4
|
-
[![Coverage Status](https://coveralls.io/repos/github/hirura/rubyang/badge.svg?branch=master)](https://coveralls.io/github/hirura/rubyang?branch=master)
|
5
4
|
[![Code Climate](https://codeclimate.com/github/hirura/rubyang/badges/gpa.svg)](https://codeclimate.com/github/hirura/rubyang)
|
5
|
+
[![Test Coverage](https://codeclimate.com/github/hirura/rubyang/badges/coverage.svg)](https://codeclimate.com/github/hirura/rubyang/coverage)
|
6
6
|
[![Issue Count](https://codeclimate.com/github/hirura/rubyang/badges/issue_count.svg)](https://codeclimate.com/github/hirura/rubyang)
|
7
|
+
[![Gem Version](https://badge.fury.io/rb/rubyang.svg)](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
|