From: František Dvořák Date: Fri, 17 Jun 2016 15:32:04 +0000 (+0200) Subject: More detailed information about networks. X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=a889efdf7a5aaeed1e6ccc6e9cd12acf0b2faf5f;p=now.git More detailed information about networks. --- diff --git a/.rubocop.yml b/.rubocop.yml index af1f2e2..f297369 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,6 +4,7 @@ AllCops: Exclude: # exclude generated code (maybe we'll rewrite it anyway one day) - models/network.rb + - models/range.rb # Assignment Branch Condition size for initialize is too high # Perceived complexity for deep_merge is too high @@ -71,6 +72,11 @@ Style/NegatedIf: Style/RedundantReturn: Enabled: false +# Avoid comma after the last parameter of a method call. +# (do want) +Style/TrailingCommaInArguments: + Enabled: false + # Avoid comma after the last item of an array # (do want) Style/TrailingCommaInLiteral: diff --git a/README.md b/README.md index 9365503..c13ef84 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Launch NOW: List networks example: - curl http://now.example.com:9292/list?user=myuser + curl http://now.example.com:9292/network?user=myuser ## Development diff --git a/lib/api.rb b/lib/api.rb index 05735a4..f3c4b2d 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -39,7 +39,7 @@ module Now API_VERSION end - get '/list' do + get '/network' do cross_origin begin switch_user(params['user']) diff --git a/lib/nebula.rb b/lib/nebula.rb index af4ea3c..a5c6ca0 100644 --- a/lib/nebula.rb +++ b/lib/nebula.rb @@ -56,10 +56,12 @@ module Now networks = [] vn_pool.each do |vn| - id = vn.id - title = vn.name - network = Network.new(id: id, title: title) - networks << network.to_hash + begin + network = parse_network(vn) + networks << network.to_hash + rescue NowError => e + logger.warn "[code #{e.code}] #{e.message}, skipping" + end end return networks @@ -70,10 +72,7 @@ module Now vn = OpenNebula::VirtualNetwork.new(vn_generic, @ctx) check(vn.info) - id = vn.id - title = vn.name - logger.debug "OpenNebula get(#{network_id}) ==> #{id}, #{title}" - network = Network.new(id: id, title: title) + network = parse_network(vn) return network.to_hash end @@ -112,5 +111,89 @@ module Now raise NowError.new(code), return_code.message end + def parse_range(vn_id, ar) + id = ar['AR_ID'] + type = ar['TYPE'] + size = ar['SIZE'] + case type + when 'IP4' + ip = ar['IP'] + addr_size = 32 + if ip.nil? || ip.empty? + raise NowError.new(422), "Missing 'IP' in the address range #{id} of network #{vn_id}" + end + when 'IP6', 'IP4_6' + ip = ar['GLOBAL_PREFIX'] || ar['ULA_PREFIX'] + addr_size = 128 + if ip.nil? || ip.empty? + raise NowError.new(422), "Missing 'GLOBAL_PREFIX' in the address range #{id} of network #{vn_id}" + end + else + raise NowError.new(501), "Unknown type '#{type}' in the address range #{id} of network #{vn_id}" + end + if size.nil? || size.empty? + raise NowError.new(422), "Missing 'SIZE' in the address range #{id} of network #{vn_id}" + end + size = size.to_i + mask = addr_size - Math.log(size, 2).ceil + logger.debug "[parse_range] id=#{id}, address=#{ip}/#{mask} (size #{size})" + + return Now::Range.new(address: "#{ip}/#{mask}", allocation: 'dynamic') + end + + def parse_ranges(vn_id, vn) + range = nil + vn.each('AR_POOL/AR') do |ar| + if !range.nil? + raise NowError.new(501), "Multiple address ranges found in network #{vn_id}" + end + range = parse_range(vn_id, ar) + end + return range + end + + def parse_cluster(vn_id, vn) + cluster = nil + vn.each('CLUSTERS/ID') do |cluster_xml| + id = cluster_xml.text + logger.debug "[parse_cluster] cluster: #{id}" + if !cluster.nil? + raise NowError.new(501), "Multiple clusters assigned to network #{vn_id}" + end + cluster = id + end + return cluster + end + + def parse_network(vn) + logger.debug "[parse_network] #{vn.to_hash}" + + id = vn.id + title = vn.name + desc = vn['SUMMARY'] + if desc.nil? || desc.empty? + desc = nil + end + vlan = vn['VLAN_ID'] + if vlan.nil? || vlan.empty? + vlan = nil + end + + range = parse_ranges(id, vn) + zone = parse_cluster(id, vn) + network = Network.new( + id: id, + title: title, + description: desc, + user: vn['UNAME'], + bridge: vn['BRIDGE'], + vlan: vlan, + range: range, + zone: zone, + ) + + return network + end + end end diff --git a/models/network.rb b/models/network.rb index 6d7e166..ba966db 100644 --- a/models/network.rb +++ b/models/network.rb @@ -22,21 +22,43 @@ limitations under the License. require 'date' module Now - + # Network object class Network + # OpenNebula ID attr_accessor :id + # Network title attr_accessor :title + # Network summary + attr_accessor :description + + # Owner attr_accessor :user + # VLAN ID + attr_accessor :vlan + + attr_accessor :range + + # Network state (active, inactive, error) + attr_accessor :state + + # Availability zone (cluster) + attr_accessor :zone + # Attribute mapping from ruby-style variable name to JSON key. def self.attribute_map { :'id' => :'id', :'title' => :'title', - :'user' => :'user' + :'description' => :'description', + :'user' => :'user', + :'vlan' => :'vlan', + :'range' => :'range', + :'state' => :'state', + :'zone' => :'zone' } end @@ -45,7 +67,12 @@ module Now { :'id' => :'Integer', :'title' => :'String', - :'user' => :'String' + :'description' => :'String', + :'user' => :'String', + :'vlan' => :'Integer', + :'range' => :'Range', + :'state' => :'String', + :'zone' => :'String' } end @@ -65,10 +92,30 @@ module Now self.title = attributes[:'title'] end + if attributes.has_key?(:'description') + self.description = attributes[:'description'] + end + if attributes.has_key?(:'user') self.user = attributes[:'user'] end + if attributes.has_key?(:'vlan') + self.vlan = attributes[:'vlan'] + end + + if attributes.has_key?(:'range') + self.range = attributes[:'range'] + end + + if attributes.has_key?(:'state') + self.state = attributes[:'state'] + end + + if attributes.has_key?(:'zone') + self.zone = attributes[:'zone'] + end + end # Show invalid properties with the reasons. Usually used together with valid? @@ -92,7 +139,12 @@ module Now self.class == o.class && id == o.id && title == o.title && - user == o.user + description == o.description && + user == o.user && + vlan == o.vlan && + range == o.range && + state == o.state && + zone == o.zone end # @see the `==` method @@ -104,7 +156,7 @@ module Now # Calculates hash code according to all attributes. # @return [Fixnum] Hash code def hash - [id, title, user].hash + [id, title, description, user, vlan, range, state, zone].hash end # Builds the object from hash diff --git a/models/range.rb b/models/range.rb new file mode 100644 index 0000000..0b4f556 --- /dev/null +++ b/models/range.rb @@ -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 + # Address range + class Range + # Address range (CIDR notation) + attr_accessor :address + + # Address allocation type (static, dynamic) + attr_accessor :allocation + + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'address' => :'address', + :'allocation' => :'allocation' + } + end + + # Attribute type mapping. + def self.swagger_types + { + :'address' => :'String', + :'allocation' => :'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?(:'address') + self.address = attributes[:'address'] + end + + if attributes.has_key?(:'allocation') + self.allocation = attributes[:'allocation'] + 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 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 && + address == o.address && + allocation == o.allocation + 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 + [address, allocation].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<(?.+)>\z/ + inner_type = Regexp.last_match[:inner_type] + value.map { |v| _deserialize(inner_type, v) } + when /\AHash<(?.+), (?.+)>\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/swagger.yaml b/swagger.yaml index f76e1f4..aeebeaf 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -6,7 +6,8 @@ info: paths: /: get: - description: "Simple check" + summary: "Simple service check" + description: "Simple service check. API version is returned." parameters: [] responses: 200: @@ -17,8 +18,9 @@ paths: description: "KO" schema: type: "string" - /list: + /network: get: + summary: "Networks list" parameters: - in: "query" name: "user" @@ -38,6 +40,7 @@ paths: type: "string" /network/{id}: get: + summary: "Information about network" parameters: - in: "path" name: "id" @@ -60,15 +63,41 @@ paths: schema: type: "string" definitions: + Range: + description: "Address range" + type: "object" + properties: + address: + type: "string" + description: "Address range (CIDR notation)" + allocation: + description: "Address allocation type (static, dynamic)" + type: "string" Network: + description: "Network object" type: "object" required: - "id" properties: id: + description: "OpenNebula ID" type: "integer" format: "int64" title: + description: "Network title" + type: "string" + description: + description: "Network summary" type: "string" user: + description: "Owner" + type: "string" + vlan: + description: "VLAN ID" + type: "integer" + format: "int64" + range: + $ref: "#/definitions/Range" + zone: + description: "Availability zone (cluster)" type: "string"