From: František Dvořák Date: Tue, 13 Sep 2016 16:24:24 +0000 (+0200) Subject: Basic network create (without any authorization checks). X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=6c6780a653dbd524038ecf18fadbde82f43f71aa;p=now.git Basic network create (without any authorization checks). --- diff --git a/README.md b/README.md index c13ef84..316694d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ At NOW host (configuration `/etc/now.yaml`): admin_user: 'nowadmin' admin_password: 'the-best-strongest-password-ever' endpoint: http://nebula.example.com:2633/RPC2 + bridge: br0 + device: eth0 Launch NOW: @@ -25,7 +27,12 @@ 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 diff --git a/lib/api.rb b/lib/api.rb index 2f7da49..da91ffa 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -4,7 +4,7 @@ require 'sinatra/cross_origin' 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 @@ -51,6 +51,30 @@ module Now 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 diff --git a/lib/config.rb b/lib/config.rb index d34e010..990d6e0 100644 --- a/lib/config.rb +++ b/lib/config.rb @@ -31,6 +31,7 @@ module Now break end end + config['template_dir'] = ::File.expand_path('../../templates', __FILE__) #logger.debug "[config] Configuration: #{config}" replace config diff --git a/lib/nebula.rb b/lib/nebula.rb index a49d3b7..3cbe1a7 100644 --- a/lib/nebula.rb +++ b/lib/nebula.rb @@ -1,3 +1,4 @@ +require 'erb' require 'opennebula' require 'yaml' require 'ipaddress' @@ -11,6 +12,7 @@ module Now # for testing attr_accessor :ctx @ctx = nil + @user = nil @server_ctx = nil @user_ctx = nil @@ -28,6 +30,7 @@ module Now 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 @@ -38,6 +41,7 @@ module Now 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 @@ -79,6 +83,33 @@ module Now 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) @@ -112,10 +143,10 @@ module Now 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' diff --git a/spec/models/network_spec.rb b/spec/models/network_spec.rb index bb29130..a5db2f4 100644 --- a/spec/models/network_spec.rb +++ b/spec/models/network_spec.rb @@ -2,9 +2,6 @@ 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 diff --git a/swagger.yaml b/swagger.yaml index 47735a4..659a4a8 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -38,6 +38,27 @@ paths: 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" @@ -78,8 +99,6 @@ definitions: Network: description: "Network object" type: "object" - required: - - "id" properties: id: description: "OpenNebula ID" @@ -99,7 +118,6 @@ definitions: type: "integer" format: "int64" range: - description: "IP address range" $ref: "#/definitions/Range" zone: description: "Availability zone (cluster)" diff --git a/templates/network.erb b/templates/network.erb new file mode 100644 index 0000000..02af9ce --- /dev/null +++ b/templates/network.erb @@ -0,0 +1,24 @@ +% 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 diff --git a/templates/range.erb b/templates/range.erb new file mode 100644 index 0000000..0ae91ef --- /dev/null +++ b/templates/range.erb @@ -0,0 +1,30 @@ +% 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