Initial communication with OpenNebula, add data model and configuration support for it.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Tue, 7 Jun 2016 16:08:35 +0000 (18:08 +0200)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Tue, 7 Jun 2016 16:09:31 +0000 (18:09 +0200)
Gemfile
config.ru
etc/now.yaml
lib/api.rb
lib/nebula.rb
models/error.rb [new file with mode: 0644]
models/network.rb [new file with mode: 0644]

diff --git a/Gemfile b/Gemfile
index aaefb04..fb9d3a4 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -1,5 +1,6 @@
 source 'https://rubygems.org'
 
+gem "opennebula"
 gem "sinatra"
 gem "sinatra-cross_origin"
 
index 00f9f4b..804c933 100644 (file)
--- a/config.ru
+++ b/config.ru
@@ -1,5 +1,8 @@
 require 'logger'
 
+Dir["./models/*.rb"].each { |file|
+  require file
+}
 require './version.rb'
 require './lib/nebula.rb'
 require './lib/api.rb'
index 3dc3048..0909b6c 100644 (file)
@@ -1,3 +1,4 @@
 opennebula:
   admin_user: oneadmin
   admin_password: 'good-password'
+  endpoint: 'http://localhost:2633/RPC2'
index 0d9df51..3875723 100644 (file)
@@ -1,3 +1,4 @@
+require 'json'
 require 'sinatra'
 require 'sinatra/cross_origin'
 require ::File.expand_path('../../version',  __FILE__)
@@ -13,11 +14,20 @@ module Now
       @nebula = $nebula
     end
 
-    get // do
+    get '/' do
       cross_origin
       API_VERSION
     end
 
+    get '/list' do
+      cross_origin
+      begin
+        networks = @nebula.list_networks
+        JSON.pretty_generate(networks)
+      rescue NowError => e
+        halt e.code, e.message
+      end
+    end
   end
 
 end
index 4026c7f..31be255 100644 (file)
@@ -1,9 +1,16 @@
+require 'opennebula'
 require 'yaml'
 
-module Now
+# http://stackoverflow.com/questions/9381553/ruby-merge-nested-hash
+public def deep_merge(second)
+   merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
+   self.merge(second.to_h, &merger)
+end
 
+
+module Now
   class Nebula
-    attr_accessor :config, :logger
+    attr_accessor :config, :logger, :client
 
     def load_config(file)
       begin
@@ -16,13 +23,45 @@ module Now
       end
     end
 
+    def one_connect(url, credentials)
+      @logger.debug "Connecting to #{url}..."
+      @client = OpenNebula::Client.new(credentials, url)
+    end
+
     def initialize()
       @logger = $logger
       @logger.info "Starting Network Orchestrator Wrapper (NOW #{VERSION})"
       @config = {}
-      @config.merge! load_config(::File.expand_path('../../etc/now.yaml', __FILE__))
-      @config.merge! load_config('/etc/now.yaml')
-      @logger.debug "Configuration: #{@config}"
+
+      c = load_config(::File.expand_path('../../etc/now.yaml', __FILE__))
+      @config = @config.deep_merge(c)
+      #@logger.debug "Configuration: #{@config}"
+
+      c = load_config('/etc/now.yaml')
+      @config = @config.deep_merge(c)
+      #@logger.debug "Configuration: #{@config}"
+
+      url = @config["opennebula"]["endpoint"]
+      credentials = "#{@config["opennebula"]["admin_user"]}:#{@config["opennebula"]["admin_password"]}"
+      one_connect(url, credentials)
+    end
+
+    def list_networks()
+      vn_pool = OpenNebula::VirtualNetworkPool.new(client, -1)
+      req = vn_pool.info
+      if OpenNebula.is_error?(req)
+        raise NowError.new({code: 500, message: req.message})
+      end
+
+      networks = []
+      vn_pool.each do |vn|
+        id = vn.id
+        title = vn.name
+        network = Network.new({id: id, title: title})
+        networks << network.to_hash
+      end
+
+      return networks
     end
 
   end
diff --git a/models/error.rb b/models/error.rb
new file mode 100644 (file)
index 0000000..029c111
--- /dev/null
@@ -0,0 +1,208 @@
+=begin
+Network Orchestrator API
+
+OpenAPI spec version: 0.0.0
+
+Partially generated by: https://github.com/swagger-api/swagger-codegen.git
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+=end
+
+require 'date'
+
+module Now
+
+  class NowError < StandardError
+    attr_accessor :code
+
+    attr_accessor :message
+
+
+    # Attribute mapping from ruby-style variable name to JSON key.
+    def self.attribute_map
+      {
+        :'code' => :'code',
+        :'message' => :'message'
+      }
+    end
+
+    # Attribute type mapping.
+    def self.swagger_types
+      {
+        :'code' => :'Integer',
+        :'message' => :'String'
+      }
+    end
+
+    # Initializes the object
+    # @param [Hash] attributes Model attributes in the form of hash
+    def initialize(attributes = {})
+      return unless attributes.is_a?(Hash)
+
+      # convert string to symbol for hash key
+      attributes = attributes.each_with_object({}){|(k,v), h| h[k.to_sym] = v}
+
+      if attributes.has_key?(:'code')
+        self.code = attributes[:'code']
+      end
+
+      if attributes.has_key?(:'message')
+        self.message = attributes[:'message']
+      end
+
+    end
+
+    # Show invalid properties with the reasons. Usually used together with valid?
+    # @return Array for valid properies with the reasons
+    def list_invalid_properties
+      invalid_properties = Array.new
+      return invalid_properties
+    end
+
+    # Check to see if the all the properties in the model are valid
+    # @return true if the model is valid
+    def valid?
+      return false if @code.nil?
+      return false if @message.nil?
+      return true
+    end
+
+    # Checks equality by comparing each attribute.
+    # @param [Object] Object to be compared
+    def ==(o)
+      return true if self.equal?(o)
+      self.class == o.class &&
+          code == o.code &&
+          message == o.message
+    end
+
+    # @see the `==` method
+    # @param [Object] Object to be compared
+    def eql?(o)
+      self == o
+    end
+
+    # Calculates hash code according to all attributes.
+    # @return [Fixnum] Hash code
+    def hash
+      [code, message].hash
+    end
+
+    # Builds the object from hash
+    # @param [Hash] attributes Model attributes in the form of hash
+    # @return [Object] Returns the model itself
+    def build_from_hash(attributes)
+      return nil unless attributes.is_a?(Hash)
+      self.class.swagger_types.each_pair do |key, type|
+        if type =~ /^Array<(.*)>/i
+          # check to ensure the input is an array given that the the attribute
+          # is documented as an array but the input is not
+          if attributes[self.class.attribute_map[key]].is_a?(Array)
+            self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
+          end
+        elsif !attributes[self.class.attribute_map[key]].nil?
+          self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
+        end # or else data not found in attributes(hash), not an issue as the data can be optional
+      end
+
+      self
+    end
+
+    # Deserializes the data based on type
+    # @param string type Data type
+    # @param string value Value to be deserialized
+    # @return [Object] Deserialized data
+    def _deserialize(type, value)
+      case type.to_sym
+      when :DateTime
+        DateTime.parse(value)
+      when :Date
+        Date.parse(value)
+      when :String
+        value.to_s
+      when :Integer
+        value.to_i
+      when :Float
+        value.to_f
+      when :BOOLEAN
+        if value.to_s =~ /^(true|t|yes|y|1)$/i
+          true
+        else
+          false
+        end
+      when :Object
+        # generic object (usually a Hash), return directly
+        value
+      when /\AArray<(?<inner_type>.+)>\z/
+        inner_type = Regexp.last_match[:inner_type]
+        value.map { |v| _deserialize(inner_type, v) }
+      when /\AHash<(?<k_type>.+), (?<v_type>.+)>\z/
+        k_type = Regexp.last_match[:k_type]
+        v_type = Regexp.last_match[:v_type]
+        {}.tap do |hash|
+          value.each do |k, v|
+            hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
+          end
+        end
+      else # model
+        temp_model = Now.const_get(type).new
+        temp_model.build_from_hash(value)
+      end
+    end
+
+    # Returns the string representation of the object
+    # @return [String] String presentation of the object
+    def to_s
+      to_hash.to_s
+    end
+
+    # to_body is an alias to to_hash (backward compatibility)
+    # @return [Hash] Returns the object in the form of hash
+    def to_body
+      to_hash
+    end
+
+    # Returns the object in the form of hash
+    # @return [Hash] Returns the object in the form of hash
+    def to_hash
+      hash = {}
+      self.class.attribute_map.each_pair do |attr, param|
+        value = self.send(attr)
+        next if value.nil?
+        hash[param] = _to_hash(value)
+      end
+      hash
+    end
+
+    # Outputs non-array value in the form of hash
+    # For object, use to_hash. Otherwise, just return the value
+    # @param [Object] value Any valid value
+    # @return [Hash] Returns the value in the form of hash
+    def _to_hash(value)
+      if value.is_a?(Array)
+        value.compact.map{ |v| _to_hash(v) }
+      elsif value.is_a?(Hash)
+        {}.tap do |hash|
+          value.each { |k, v| hash[k] = _to_hash(v) }
+        end
+      elsif value.respond_to? :to_hash
+        value.to_hash
+      else
+        value
+      end
+    end
+
+  end
+
+end
diff --git a/models/network.rb b/models/network.rb
new file mode 100644 (file)
index 0000000..6d7e166
--- /dev/null
@@ -0,0 +1,216 @@
+=begin
+Network Orchestrator API
+
+OpenAPI spec version: 0.0.0
+
+Partially generated by: https://github.com/swagger-api/swagger-codegen.git
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+=end
+
+require 'date'
+
+module Now
+
+  class Network
+    attr_accessor :id
+
+    attr_accessor :title
+
+    attr_accessor :user
+
+
+    # Attribute mapping from ruby-style variable name to JSON key.
+    def self.attribute_map
+      {
+        :'id' => :'id',
+        :'title' => :'title',
+        :'user' => :'user'
+      }
+    end
+
+    # Attribute type mapping.
+    def self.swagger_types
+      {
+        :'id' => :'Integer',
+        :'title' => :'String',
+        :'user' => :'String'
+      }
+    end
+
+    # Initializes the object
+    # @param [Hash] attributes Model attributes in the form of hash
+    def initialize(attributes = {})
+      return unless attributes.is_a?(Hash)
+
+      # convert string to symbol for hash key
+      attributes = attributes.each_with_object({}){|(k,v), h| h[k.to_sym] = v}
+
+      if attributes.has_key?(:'id')
+        self.id = attributes[:'id']
+      end
+
+      if attributes.has_key?(:'title')
+        self.title = attributes[:'title']
+      end
+
+      if attributes.has_key?(:'user')
+        self.user = attributes[:'user']
+      end
+
+    end
+
+    # Show invalid properties with the reasons. Usually used together with valid?
+    # @return Array for valid properies with the reasons
+    def list_invalid_properties
+      invalid_properties = Array.new
+      return invalid_properties
+    end
+
+    # Check to see if the all the properties in the model are valid
+    # @return true if the model is valid
+    def valid?
+      return false if @id.nil?
+      return true
+    end
+
+    # Checks equality by comparing each attribute.
+    # @param [Object] Object to be compared
+    def ==(o)
+      return true if self.equal?(o)
+      self.class == o.class &&
+          id == o.id &&
+          title == o.title &&
+          user == o.user
+    end
+
+    # @see the `==` method
+    # @param [Object] Object to be compared
+    def eql?(o)
+      self == o
+    end
+
+    # Calculates hash code according to all attributes.
+    # @return [Fixnum] Hash code
+    def hash
+      [id, title, user].hash
+    end
+
+    # Builds the object from hash
+    # @param [Hash] attributes Model attributes in the form of hash
+    # @return [Object] Returns the model itself
+    def build_from_hash(attributes)
+      return nil unless attributes.is_a?(Hash)
+      self.class.swagger_types.each_pair do |key, type|
+        if type =~ /^Array<(.*)>/i
+          # check to ensure the input is an array given that the the attribute
+          # is documented as an array but the input is not
+          if attributes[self.class.attribute_map[key]].is_a?(Array)
+            self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
+          end
+        elsif !attributes[self.class.attribute_map[key]].nil?
+          self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
+        end # or else data not found in attributes(hash), not an issue as the data can be optional
+      end
+
+      self
+    end
+
+    # Deserializes the data based on type
+    # @param string type Data type
+    # @param string value Value to be deserialized
+    # @return [Object] Deserialized data
+    def _deserialize(type, value)
+      case type.to_sym
+      when :DateTime
+        DateTime.parse(value)
+      when :Date
+        Date.parse(value)
+      when :String
+        value.to_s
+      when :Integer
+        value.to_i
+      when :Float
+        value.to_f
+      when :BOOLEAN
+        if value.to_s =~ /^(true|t|yes|y|1)$/i
+          true
+        else
+          false
+        end
+      when :Object
+        # generic object (usually a Hash), return directly
+        value
+      when /\AArray<(?<inner_type>.+)>\z/
+        inner_type = Regexp.last_match[:inner_type]
+        value.map { |v| _deserialize(inner_type, v) }
+      when /\AHash<(?<k_type>.+), (?<v_type>.+)>\z/
+        k_type = Regexp.last_match[:k_type]
+        v_type = Regexp.last_match[:v_type]
+        {}.tap do |hash|
+          value.each do |k, v|
+            hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
+          end
+        end
+      else # model
+        temp_model = Now.const_get(type).new
+        temp_model.build_from_hash(value)
+      end
+    end
+
+    # Returns the string representation of the object
+    # @return [String] String presentation of the object
+    def to_s
+      to_hash.to_s
+    end
+
+    # to_body is an alias to to_hash (backward compatibility)
+    # @return [Hash] Returns the object in the form of hash
+    def to_body
+      to_hash
+    end
+
+    # Returns the object in the form of hash
+    # @return [Hash] Returns the object in the form of hash
+    def to_hash
+      hash = {}
+      self.class.attribute_map.each_pair do |attr, param|
+        value = self.send(attr)
+        next if value.nil?
+        hash[param] = _to_hash(value)
+      end
+      hash
+    end
+
+    # Outputs non-array value in the form of hash
+    # For object, use to_hash. Otherwise, just return the value
+    # @param [Object] value Any valid value
+    # @return [Hash] Returns the value in the form of hash
+    def _to_hash(value)
+      if value.is_a?(Array)
+        value.compact.map{ |v| _to_hash(v) }
+      elsif value.is_a?(Hash)
+        {}.tap do |hash|
+          value.each { |k, v| hash[k] = _to_hash(v) }
+        end
+      elsif value.respond_to? :to_hash
+        value.to_hash
+      else
+        value
+      end
+    end
+
+  end
+
+end