admin_user: 'nowadmin'
admin_password: 'the-best-strongest-password-ever'
endpoint: http://nebula.example.com:2633/RPC2
+ bridge: br0
+ device: eth0
Launch NOW:
List networks example:
- curl http://now.example.com:9292/network?user=myuser
+ *curl http://now.example.com:9292/network?user=myuser*
+
+Create the network:
+
+ *curl -i -X POST -d '{ "title": "example1", "description": "Example network", "range": { "address": "fc00::0001::/64", "allocation": "dynamic" }, "vlan": 1}' http://now.example.com:9292/network?user=myuser*
+
## Development
require ::File.expand_path('../../version', __FILE__)
module Now
- # HTTP REST API between NOW and rOCCI server
+ # HTTP REST API of NOW (for usage by rOCCI server)
class Application < Sinatra::Base
attr_accessor :nebula
register Sinatra::CrossOrigin
end
end
+ post '/network' do
+ cross_origin
+ request.body.rewind
+ begin
+ netinfo = JSON.parse request.body.read
+ switch_user(params['user'])
+ # Now::Network expects Now::Range object
+ if netinfo.key?('range')
+ netinfo['range'] = Now::Range.from_hash(netinfo['range'])
+ end
+ network = Now::Network.new(netinfo)
+ id = nebula.create_network(network)
+ rescue NowError => e
+ logger.error "[HTTP #{e.code}] #{e.message}"
+ halt e.code, e.message
+ rescue JSON::ParserError => e
+ logger.error "[HTTP 400] #{e.message}"
+ halt 400, e.message
+ end
+
+ body id
+ status 201
+ end
+
get '/network/:id' do
cross_origin
begin
break
end
end
+ config['template_dir'] = ::File.expand_path('../../templates', __FILE__)
#logger.debug "[config] Configuration: #{config}"
replace config
+require 'erb'
require 'opennebula'
require 'yaml'
require 'ipaddress'
# for testing
attr_accessor :ctx
@ctx = nil
+ @user = nil
@server_ctx = nil
@user_ctx = nil
expiration = Time.now.to_i + EXPIRE_LENGTH
user_token = server_auth.login_token(expiration, user)
+ @user = user
@user_ctx = one_connect(@url, user_token)
@ctx = @user_ctx
end
logger.debug "Authentication to #{admin_user}"
direct_token = "#{admin_user}:#{admin_password}"
+ @user = admin_user
@server_ctx = one_connect(@url, direct_token)
@ctx = @server_ctx
end
return network
end
+ def create_network(netinfo)
+ #logger.debug "[create_network] #{netinfo}"
+ logger.info '[create_network] Network ID ignored (set by OpenNebula)' if netinfo.id
+ logger.info "[create_network] Network owner ignored (will be '#{@user}')" if netinfo.user
+ logger.warn '[create_network] Bridge not configured (bridge)' unless config.key? 'bridge'
+ logger.warn '[create_network] Physical drvice not configured (device)' unless config.key? 'device'
+ range = netinfo.range
+
+ if range && range.address && range.address.ipv6?
+ logger.warn "[create_network] Network prefix 64 for IPv6 network required (#{range.address.to_string})" unless range.address.prefix == 64
+ end
+ vn_generic = OpenNebula::VirtualNetwork.build_xml
+ vn = OpenNebula::VirtualNetwork.new(vn_generic, @ctx)
+ b = binding
+ template = ERB.new(::File.new(::File.join(config['template_dir'], 'network.erb')).read, 0, '%').result b
+ template_ar = ERB.new(::File.new(::File.join(config['template_dir'], 'range.erb')).read, 0, '%').result b
+ template += "\n"
+ template += template_ar
+ logger.debug "[create_network] template: #{template}"
+
+ check(vn.allocate(template))
+ id = vn.id.to_s
+ logger.debug "[create_network] created network: #{id}"
+
+ return id
+ end
+
private
def error_one2http(errno)
end
def parse_range(vn_id, vn, ar)
- id = ar['AR_ID'] || '(undef)'
- type = ar['TYPE']
- ip = ar['NETWORK_ADDRESS'] || vn['NETWORK_ADDRESS']
- mask = ar['NETWORK_MASK'] || vn['NETWORK_MASK']
+ id = ar && ar['AR_ID'] || '(undef)'
+ type = ar && ar['TYPE']
+ ip = ar && ar['NETWORK_ADDRESS'] || vn['NETWORK_ADDRESS']
+ mask = ar && ar['NETWORK_MASK'] || vn['NETWORK_MASK']
case type
when 'IP4'
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
description: "KO"
schema:
type: "string"
+ post:
+ summary: "Create a new network"
+ parameters:
+ - in: "query"
+ name: "user"
+ description: "OpenNebula user identity"
+ required: false
+ type: "string"
+ - in: "body"
+ name: "network"
+ description: "Network details"
+ required: true
+ schema:
+ $ref: "#/definitions/Network"
+ responses:
+ 201:
+ description: "Network created"
+ default:
+ description: "Error creating network"
+ schema:
+ type: "string"
/network/{id}:
get:
summary: "Information about network"
Network:
description: "Network object"
type: "object"
- required:
- - "id"
properties:
id:
description: "OpenNebula ID"
type: "integer"
format: "int64"
range:
- description: "IP address range"
$ref: "#/definitions/Range"
zone:
description: "Availability zone (cluster)"
--- /dev/null
+% if netinfo.title
+NAME = "<%= netinfo.title %>"
+% end
+
+% if netinfo.description
+DESCRIPTION = "<%= netinfo.description %>"
+% end
+
+% if netinfo.zone
+CLUSTERS = <%= netinfo.zone %>
+% end
+
+% if config.key? 'bridge'
+BRIDGE = <%= config['bridge'] %>
+% end
+
+% if config.key? 'device'
+PHYDEV = <%= config['device'] %>
+% end
+
+% if netinfo.vlan
+VLAN_ID = <%= netinfo.vlan %>
+VN_MAD = vxlan
+% end
--- /dev/null
+% range = netinfo.range
+%if range
+%
+% if range.address.ipv4?
+NETWORK_ADDRESS = <%= range.address.network.to_s %>
+NETWORK_MASK = <%= range.address.netmask %>
+
+AR = [
+ TYPE = IP4,
+ IP = <%= range.address.first.to_s %>,
+ SIZE = <%= range.address.size - 2 %>
+]
+% end
+%
+% if range.address.ipv6?
+NETWORK_ADDRESS = <%= range.address.network.to_s %>
+NETWORK_MASK = <%= range.address.prefix %>
+
+AR = [
+ TYPE = IP6,
+% if IPAddress('fc00::/7').include? range.address
+ ULA_PREFIX = <%= range.address.network.to_s %>,
+% else
+ GLOBAL_PREFIX = <%= range.address.network.to_s %>,
+% end
+% # limit address range (required for OpenNebula)
+ SIZE = <%= range.address.size >= 2**31 ? 2**31 : range.address.size - 2 %>
+]
+% end
+%end