AllCops:
DisplayCopNames: true
DisplayStyleGuide: true
- 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
script:
- bundle exec rubocop
+ - bundle exec rspec
- bundle exec ruby -rminitest/autorun -Ilib:test -e 'Dir.glob "./test/*_test.rb", &method(:require)'
source 'https://rubygems.org'
+gem 'ipaddress'
gem 'opennebula'
gem 'sinatra'
gem 'sinatra-cross_origin'
# recommended for sinatra
gem 'thin'
-gem 'rubocop', group: :development, require: false
-gem 'rack-test', group: :development
gem 'minitest', group: :development
+gem 'rack-test', group: :development
+gem 'rspec', group: :development
+gem 'rubocop', group: :development, require: false
require 'logger'
-Dir['./models/*.rb'].each do |file|
+Dir['./models/helpers/*.rb', './models/*.rb'].each do |file|
require file
end
require './version'
require 'opennebula'
require 'yaml'
+require 'ipaddress'
module Now
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')
+ return Now::Range.new(address: IPAddress.parse("#{ip}/#{mask}"), allocation: 'dynamic')
end
def parse_ranges(vn_id, vn)
end
def parse_network(vn)
- logger.debug "[parse_network] #{vn.to_hash}"
+ logger.debug "[parse_network] #{vn.to_xml}"
id = vn.id
title = vn.name
--- /dev/null
+module Now
+
+ # Generic hash class with custom accessors and helper methods
+ class NowHash < ::Hash
+ def self.my_accessor(*keys)
+ keys.each do |key|
+ define_method(key) do
+ return nil if !key?(key)
+ fetch(key)
+ end
+ define_method("#{key}=") do |new_value|
+ if new_value.nil?
+ delete(key)
+ else
+ store(key, new_value)
+ end
+ end
+ end
+ end
+
+ def initialize(parameters = {})
+ parameters.select! { |_k, v| !v.nil? }
+ replace(parameters)
+ end
+
+ # Conversion of the data structure to hash. Arrays and hashes are browsed, the leafs are converted by calling to_hash method or directly copied.
+ # @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.map { |v| _to_hash(v) }
+ # beware we're the Hash!!!
+ elsif value.is_a?(Hash) && !value.is_a?(Now::NowHash)
+ {}.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
-=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
+
# Network object
- class Network
+ class Network < NowHash
# OpenNebula ID
- attr_accessor :id
+ my_accessor :id
# Network title
- attr_accessor :title
+ my_accessor :title
# Network summary
- attr_accessor :description
+ my_accessor :description
# Owner
- attr_accessor :user
+ my_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',
- :'description' => :'description',
- :'user' => :'user',
- :'vlan' => :'vlan',
- :'range' => :'range',
- :'state' => :'state',
- :'zone' => :'zone'
- }
- end
+ my_accessor :vlan
- # Attribute type mapping.
- def self.swagger_types
- {
- :'id' => :'Integer',
- :'title' => :'String',
- :'description' => :'String',
- :'user' => :'String',
- :'vlan' => :'Integer',
- :'range' => :'Range',
- :'state' => :'String',
- :'zone' => :'String'
- }
+ # IP address range (reader)
+ def range
+ return nil if !key?(:range)
+ fetch(:range)
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?(:'description')
- self.description = attributes[:'description']
- end
-
- if attributes.has_key?(:'user')
- self.user = attributes[:'user']
+ # IP address range (writer)
+ def range=(new_value)
+ if !valid_range?(new_value)
+ raise NowError.new(500), 'Invalid range type'
end
+ store(:range, new_value)
+ end
- if attributes.has_key?(:'vlan')
- self.vlan = attributes[:'vlan']
- end
+ # Network state (active, inactive, error)
+ my_accessor :state
- if attributes.has_key?(:'range')
- self.range = attributes[:'range']
- end
+ # Availability zone (cluster)
+ my_accessor :zone
- if attributes.has_key?(:'state')
- self.state = attributes[:'state']
+ def initialize(parameters = {})
+ if !parameters.key?(:id)
+ raise NowError.new(500), 'ID required in network object'
end
-
- if attributes.has_key?(:'zone')
- self.zone = attributes[:'zone']
+ if parameters.key?(:range) && !valid_range?(parameters[:range])
+ raise NowError.new(500), 'Valid range object required in network object'
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
+ super
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 false if id.nil?
+ return false if !valid_range?(range)
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 &&
- description == o.description &&
- user == o.user &&
- vlan == o.vlan &&
- range == o.range &&
- state == o.state &&
- zone == o.zone
+ def ==(other)
+ return true if equal?(other)
+ self.class == other.class &&
+ id == other.id &&
+ title == other.title &&
+ description == other.description &&
+ user == other.user &&
+ vlan == other.vlan &&
+ range == other.range &&
+ state == other.state &&
+ zone == other.zone
end
# @see the `==` method
# @param [Object] Object to be compared
- def eql?(o)
- self == o
+ def eql?(other)
+ self == other
end
# Calculates hash code according to all attributes.
[id, title, description, user, vlan, range, state, zone].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)
+ each_pair do |attr, value|
+ hash[attr] = _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
+ return hash
end
+ private
+
+ def valid_range?(value)
+ value.nil? || value.is_a?(Now::Range)
+ end
end
end
-=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'
+require 'ipaddress'
module Now
- # Address range
- class Range
- # Address range (CIDR notation)
- attr_accessor :address
-
- # Address allocation type (static, dynamic)
- attr_accessor :allocation
+ # Address range
+ class Range < NowHash
- # Attribute mapping from ruby-style variable name to JSON key.
- def self.attribute_map
- {
- :'address' => :'address',
- :'allocation' => :'allocation'
- }
+ # Address range in CIDR notation (reader)
+ def address
+ fetch(:address)
end
- # Attribute type mapping.
- def self.swagger_types
- {
- :'address' => :'String',
- :'allocation' => :'String'
- }
+ # Address range in CIDR notation (writer)
+ def address=(new_value)
+ if !valid_address?(new_value)
+ raise NowError.new(500), 'Internal error: Invalid IP network address'
+ end
+ store(:address, new_value)
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}
+ # Address allocation type (static, dynamic)
+ my_accessor :allocation
- if attributes.has_key?(:'address')
- self.address = attributes[:'address']
+ def initialize(parameters = {})
+ if !parameters.key?(:address)
+ raise NowError.new(500), 'Internal error: IP network address required'
end
-
- if attributes.has_key?(:'allocation')
- self.allocation = attributes[:'allocation']
+ if !valid_address?(parameters[:address])
+ raise NowError.new(500), 'Internal error: Invalid IP network address'
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
+ super
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 !valid_address?(address)
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
+ def ==(other)
+ return true if equal?(other)
+ self.class == other.class &&
+ address == other.address &&
+ allocation == other.allocation
end
# @see the `==` method
# @param [Object] Object to be compared
- def eql?(o)
- self == o
+ def eql?(other)
+ self == other
end
# Calculates hash code according to all attributes.
[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<(?<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)
+ h = {}
+ if key?(:address)
+ h[:address] = "#{address}/#{address.prefix}"
end
- hash
+ if key?(:allocation)
+ h[:allocation] = allocation
+ end
+
+ return h
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
+ private
+
+ def valid_address?(value)
+ !value.nil? && value.is_a?(IPAddress)
end
end
--- /dev/null
+require 'spec_helper'
+
+describe Now::Network do
+ context '#type check' do
+ it 'no id raises NowError' do
+ expect { Now::Network.new }.to raise_error(Now::NowError)
+ end
+ it 'string range raises NowError' do
+ expect { Now::Network.new(id: 0, range: 'eee') }.to raise_error(Now::NowError)
+ end
+ end
+
+ context '#no addess range' do
+ let(:network) { Now::Network.new(id: 0) }
+ let(:range) { Now::Range.new(address: IPAddress.parse('fd00::/8')) }
+ let(:range2) { Now::Range.new(address: IPAddress.parse('fd00::/8')) }
+ let(:hash) { { id: 0 } }
+ let(:hash_rich) do
+ {
+ id: 1,
+ title: 'Title 1',
+ description: 'Description 1',
+ user: 'spike',
+ vlan: 100,
+ range: {
+ address: 'fd00::/8',
+ },
+ zone: '2',
+ }
+ end
+
+ it 'is a network' do
+ expect(network).to be_kind_of Now::Network
+ end
+ it 'address range is nil' do
+ expect(network.range).to be nil
+ end
+ it 'is valid' do
+ expect(network.valid?).to be true
+ end
+ it 'still valid with addess range' do
+ network.range = range
+ expect(network.valid?).to be true
+ end
+ it 'attributes can be set, rich to_hash works' do
+ network.id = 1
+ network.title = 'Title 1'
+ network.description = 'Description 1'
+ network.user = 'spike'
+ network.vlan = 100
+ network.range = range
+ network.zone = '2'
+
+ expect(network.id).to eq(1)
+ expect(network.title).to eq('Title 1')
+ expect(network.description).to eq('Description 1')
+ expect(network.user).to eq('spike')
+ expect(network.vlan).to eq(100)
+ expect(network.range).to eq(range2)
+ expect(network.zone).to eq('2')
+
+ expect(network.to_hash).to eq(hash_rich)
+ end
+ it 'to_hash works' do
+ expect(network.to_hash).to eq(hash)
+ end
+ end
+
+ context '#basic' do
+ let(:range) { Now::Range.new(address: IPAddress.parse('192.168.0.1/24')) }
+ let(:network) { Now::Network.new(id: 0, range: range) }
+ let(:hash) do
+ {
+ id: 0,
+ range: {
+ address: '192.168.0.1/24',
+ },
+ }
+ end
+
+ it 'is a network' do
+ expect(network).to be_kind_of Now::Network
+ end
+ it 'is valid' do
+ expect(network.valid?).to be true
+ end
+ it 'still valid with nil address range' do
+ network.range = nil
+ expect(network.valid?).to be true
+ end
+ it 'to_hash works' do
+ expect(network.to_hash).to eq(hash)
+ end
+ end
+
+ context '#basic IPv6' do
+ let(:range) { Now::Range.new(address: IPAddress.parse('fd00::/8')) }
+ let(:network) { Now::Network.new(id: 1, range: range) }
+ let(:hash) do
+ {
+ id: 1,
+ range: {
+ address: 'fd00::/8',
+ }
+ }
+ end
+ it 'is a network' do
+ expect(network).to be_kind_of Now::Network
+ end
+ it 'is valid' do
+ expect(network.valid?).to be true
+ end
+ it 'still valid with nil address range' do
+ network.range = nil
+ expect(network.valid?).to be true
+ end
+ it 'to_hash works' do
+ expect(network.to_hash).to eq(hash)
+ end
+ end
+
+ context '#basic set' do
+ let(:range) { Now::Range.new(address: IPAddress.parse('172.16.0.0/12')) }
+ let(:network) do
+ n = Now::Network.new(id: 2)
+ n.range = range
+ n.title = 'Title'
+ n.description = 'Description'
+ n.user = 'fluttershy'
+ n
+ end
+ let(:hash) do
+ {
+ id: 2,
+ title: 'Title',
+ description: 'Description',
+ user: 'fluttershy',
+ range: {
+ address: '172.16.0.0/12',
+ },
+ }
+ end
+
+ it 'is a network' do
+ expect(network).to be_kind_of Now::Network
+ end
+ it 'is valid' do
+ expect(network.valid?).to be true
+ end
+ it 'still valid with nil address range' do
+ network.range = nil
+ expect(network.valid?).to be true
+ end
+ it 'to_hash works' do
+ expect(network.to_hash).to eq(hash)
+ end
+ end
+
+ context '#basic IPv6 set' do
+ let(:range) { Now::Range.new(address: IPAddress.parse('fd00::/8')) }
+ let(:network) do
+ n = Now::Network.new(id: 2)
+ n.range = range
+ n.title = 'Title'
+ n.description = 'Description'
+ n.user = 'fluttershy'
+ n
+ end
+ let(:hash) do
+ {
+ id: 2,
+ title: 'Title',
+ description: 'Description',
+ user: 'fluttershy',
+ range: {
+ address: 'fd00::/8',
+ },
+ }
+ end
+
+ it 'is a network' do
+ expect(network).to be_kind_of Now::Network
+ end
+ it 'is valid' do
+ expect(network.valid?).to be true
+ end
+ it 'still valid with nil address range' do
+ network.range = nil
+ expect(network.valid?).to be true
+ end
+ it 'to_hash works' do
+ expect(network.to_hash).to eq(hash)
+ end
+ end
+end
--- /dev/null
+require 'spec_helper'
+
+describe Now::Range do
+ context '#type check' do
+ it 'string address raises NowError' do
+ expect { Now::Range.new(address: 'eee') }.to raise_error(Now::NowError)
+ end
+ it 'no address raises NowError' do
+ expect { Now::Range.new }.to raise_error(Now::NowError)
+ end
+ end
+
+ context '#basic' do
+ let(:range) { Now::Range.new(address: IPAddress.parse('192.168.0.1/24')) }
+ let(:hash) { { address: '192.168.0.1/24' } }
+
+ it 'is a range' do
+ expect(range).to be_kind_of Now::Range
+ end
+ it 'is valid' do
+ expect(range.valid?).to be true
+ end
+ it 'setting nil address raises error' do
+ expect { range.address = nil }.to raise_error(Now::NowError)
+ end
+ it 'to_hash works' do
+ expect(range.to_hash).to eq(hash)
+ end
+ end
+
+ context '#basic IPv6' do
+ let(:range) { Now::Range.new(address: IPAddress.parse('fd00::/8')) }
+ let(:hash) { { address: 'fd00::/8' } }
+
+ it 'is a range' do
+ expect(range).to be_kind_of Now::Range
+ end
+ it 'is valid' do
+ expect(range.valid?).to be true
+ end
+ it 'setting nil address raises error' do
+ expect { range.address = nil }.to raise_error(Now::NowError)
+ end
+ it 'to_hash works' do
+ expect(range.to_hash).to eq(hash)
+ end
+ end
+
+ context '#basic set' do
+ let(:range) do
+ r = Now::Range.new(address: IPAddress.parse('172.16.0.0/12'))
+ r.allocation = 'dynamic'
+ r
+ end
+ let(:hash) { { address: '172.16.0.0/12', allocation: 'dynamic' } }
+
+ it 'is a range' do
+ expect(range).to be_kind_of Now::Range
+ end
+ it 'is valid' do
+ expect(range.valid?).to be true
+ end
+ it 'setting nil address raises error' do
+ expect { range.address = nil }.to raise_error(Now::NowError)
+ end
+ it 'to_hash works' do
+ expect(range.to_hash).to eq(hash)
+ end
+ end
+
+ context '#basic IPv6 set' do
+ let(:range) do
+ r = Now::Range.new(address: IPAddress.parse('fd00::/8'))
+ r.allocation = 'dynamic'
+ r
+ end
+ let(:hash) { { address: 'fd00::/8', allocation: 'dynamic' } }
+
+ it 'is a range' do
+ expect(range).to be_kind_of Now::Range
+ end
+ it 'is valid' do
+ expect(range.valid?).to be true
+ end
+ it 'setting nil address raises error' do
+ expect { range.address = nil }.to raise_error(Now::NowError)
+ end
+ it 'to_hash works' do
+ expect(range.to_hash).to eq(hash)
+ end
+ end
+end
--- /dev/null
+require 'rspec'
+
+Dir['./models/helpers/*.rb', './models/*.rb', './lib/*.rb'].each do |file|
+ require file
+end
Range:
description: "Address range"
type: "object"
+ required:
+ - "address"
properties:
address:
type: "string"
type: "integer"
format: "int64"
range:
+ description: "IP address range"
$ref: "#/definitions/Range"
zone:
description: "Availability zone (cluster)"