Metrics:
Enabled: false
+# Use the return of the conditional for variable assignment and comparison
+# (wtf)
+Style/ConditionalAssignment:
+ Enabled: false
+
# Favor format over sprintf
# (easy to read)
Style/FormatString:
## Overview
This is the component to extend OpenNebula network orchestration capabilities.
-## Launch
-```
-rackup
-```
+## Admin Usage
+
+At OpenNebula host:
+
+ oneuser create nowadmin --driver server_cipher 'the-best-strongest-password-ever'
+ oneuser chgrp nowadmin oneadmin
+
+At NOW host (configration `/etc/now.yaml`):
+
+ opennebula:
+ admin_user: 'nowadmin'
+ admin_password: 'the-best-strongest-password-ever'
+ endpoint: http://nebula.example.com:2633/RPC2
+
+Launch NOW:
+
+ rackup
+
+## Usage
+
+List networks example:
+
+ curl http://now.example.com:9292/list?user=myuser
## Development
-```
-make run
-```
-It calls `rackup` through bundler.
+Using `make`:
+
+ make check
+ make run
+
+Or directly:
+
+ bundle install
+ bundle exec rubocop
+ bundle exec rackup
before do
# to sinatra request logger point to proper object
env['rack.logger'] = $logger
+
+ switch_user(params['user'])
+ end
+
+ helpers do
+ def switch_user(user)
+ if user.nil?
+ @nebula.switch_server()
+ else
+ @nebula.switch_user(user)
+ end
+ end
end
get '/' do
networks = @nebula.list_networks
JSON.pretty_generate(networks)
rescue NowError => e
+ logger.error "[HTTP #{e.code}] #{e.message}"
halt e.code, e.message
end
end
network = @nebula.get(params['id'])
JSON.pretty_generate(network)
rescue NowError => e
+ logger.error "[HTTP #{e.code}] #{e.message}"
halt e.code, e.message
end
end
end
module Now
+
+ EXPIRE_LENGTH = 8 * 60 * 60
+
# NOW core class for communication with OpenNebula
class Nebula
- attr_accessor :config, :logger, :client
+ attr_accessor :logger
+ @ctx = nil
+ @server_ctx = nil
+ @user_ctx = nil
def load_config(file)
c = YAML.load_file(file)
- @logger.debug "Config file '#{file}' loaded"
+ logger.debug "Config file '#{file}' loaded"
return c
rescue Errno::ENOENT
- @logger.debug "Config file '#{file}' not found"
+ logger.debug "Config file '#{file}' not found"
return {}
end
def one_connect(url, credentials)
- @logger.debug "Connecting to #{url}..."
- @client = OpenNebula::Client.new(credentials, url)
+ logger.debug "Connecting to #{url} ..."
+ return OpenNebula::Client.new(credentials, url)
+ end
+
+ def switch_user(user)
+ admin_user = @config['opennebula']['admin_user']
+ admin_password = @config['opennebula']['admin_password']
+ logger.debug "Authentication from #{admin_user} to #{user}"
+
+ server_auth = ServerCipherAuth.new(admin_user, admin_password)
+ expiration = Time.now.to_i + EXPIRE_LENGTH
+ user_token = server_auth.login_token(expiration, user)
+
+ @user_ctx = one_connect(@url, user_token)
+ @ctx = @user_ctx
+ end
+
+ def switch_server()
+ admin_user = @config['opennebula']['admin_user']
+ admin_password = @config['opennebula']['admin_password']
+ logger.debug "Authentication to #{admin_user}"
+
+ direct_token = "#{admin_user}:#{admin_password}"
+ @server_ctx = one_connect(@url, direct_token)
+ @ctx = @server_ctx
end
def initialize()
@logger = $logger
- @logger.info "Starting Network Orchestrator Wrapper (NOW #{VERSION})"
+ logger.info "Starting Network Orchestrator Wrapper (NOW #{VERSION})"
@config = {}
c = load_config(::File.expand_path('../../etc/now.yaml', __FILE__))
@config = @config.deep_merge(c)
- #@logger.debug "Configuration: #{@config}"
+ #logger.debug "Configuration: #{@config}"
c = load_config('/etc/now.yaml')
@config = @config.deep_merge(c)
- #@logger.debug "Configuration: #{@config}"
+ #logger.debug "Configuration: #{@config}"
- url = @config['opennebula']['endpoint']
- credentials = "#{@config['opennebula']['admin_user']}:#{@config['opennebula']['admin_password']}"
- one_connect(url, credentials)
+ @url = @config['opennebula']['endpoint']
end
def list_networks()
- vn_pool = OpenNebula::VirtualNetworkPool.new(client, -1)
+ vn_pool = OpenNebula::VirtualNetworkPool.new(@ctx, -1)
check(vn_pool.info)
networks = []
def get(network_id)
vn_generic = OpenNebula::VirtualNetwork.build_xml(network_id)
- vn = OpenNebula::VirtualNetwork.new(vn_generic, @client)
+ vn = OpenNebula::VirtualNetwork.new(vn_generic, @ctx)
check(vn.info)
id = vn.id
title = vn.name
- @logger.debug "OpenNebula get(#{network_id}) ==> #{id}, #{title}"
+ logger.debug "OpenNebula get(#{network_id}) ==> #{id}, #{title}"
network = Network.new(id: id, title: title)
return network.to_hash
--- /dev/null
+# -------------------------------------------------------------------------- #
+# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs #
+# #
+# 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. #
+#--------------------------------------------------------------------------- #
+
+require 'openssl'
+require 'digest/sha1'
+
+require 'base64'
+require 'fileutils'
+
+module Now
+ # Server authentication class. This method can be used by OpenNebula services
+ # to let access authenticated users by other means. It is based on OpenSSL
+ # symmetric ciphers
+ class ServerCipherAuth
+ ###########################################################################
+ # Constants with paths to relevant files and defaults
+ ###########################################################################
+ CIPHER = 'aes-256-cbc'
+
+ def initialize(srv_user, srv_passwd)
+ @srv_user = srv_user
+ @srv_passwd = srv_passwd
+
+ if srv_passwd.nil? || srv_passwd.empty?
+ @key = ''
+ else
+ @key = ::Digest::SHA1.hexdigest(@srv_passwd)
+ end
+
+ @cipher = ::OpenSSL::Cipher::Cipher.new(CIPHER)
+ end
+
+ # Creates a ServerCipher for client usage
+ def self.new_client(srv_user, srv_passwd)
+ new(srv_user, srv_passwd)
+ end
+
+ # Generates a login token in the form:
+ # - server_user:target_user:time_expires
+ # The token is then encrypted with the contents of one_auth
+ def login_token(expire, target_user = nil)
+ target_user ||= @srv_user
+ token_txt = "#{@srv_user}:#{target_user}:#{expire}"
+
+ token = encrypt(token_txt)
+ token64 = ::Base64.encode64(token).strip.delete("\n")
+
+ "#{@srv_user}:#{target_user}:#{token64}"
+ end
+
+ # Returns a valid password string to create a user using this auth driver
+ def password
+ @srv_passwd
+ end
+
+ private
+
+ def encrypt(data)
+ @cipher.encrypt
+ @cipher.key = @key
+
+ rc = @cipher.update(data)
+ rc << @cipher.final
+
+ rc
+ end
+
+ def decrypt(data)
+ @cipher.decrypt
+ @cipher.key = @key
+
+ rc = @cipher.update(::Base64.decode64(data))
+ rc << @cipher.final
+
+ rc
+ end
+ end
+end
type: "string"
/list:
get:
- parameters: []
+ parameters:
+ - in: "query"
+ name: "user"
+ description: "OpenNebula user identity"
+ required: false
+ type: "string"
responses:
200:
description: "List existing networks"
required: true
type: "integer"
format: "int64"
+ - in: "query"
+ name: "user"
+ description: "OpenNebula user identity"
+ required: false
+ type: "string"
responses:
200:
description: "Get information about network"