1
0
mirror of https://github.com/OpenNebula/one.git synced 2024-12-22 13:33:52 +03:00

F #5076: Implement provider/provision template

co-authored-by: Alejandro Huertas <ahuertas@opennebula.io>
This commit is contained in:
Tino Vazquez 2020-10-13 13:38:19 +02:00
parent 994098a249
commit 728142e205
53 changed files with 2519 additions and 677 deletions

View File

@ -18,7 +18,7 @@
#define DOCUMENT_H_
#include "PoolObjectSQL.h"
#include "Template.h"
#include "DocumentTemplate.h"
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
@ -48,7 +48,7 @@ public:
*/
std::unique_ptr<Template> get_new_template() const override
{
return std::make_unique<Template>();
return std::make_unique<DocumentTemplate>();
}
/**
@ -57,7 +57,7 @@ public:
*/
std::unique_ptr<Template> clone_template() const
{
return std::make_unique<Template>(*obj_template);
return std::make_unique<DocumentTemplate>(*obj_template);
};
/**

View File

@ -28,7 +28,10 @@ class DocumentPool : public PoolSQL
{
public:
DocumentPool(SqlDB * db) : PoolSQL(db, one_db::doc_table) {};
DocumentPool(SqlDB * db, std::vector<const SingleAttribute *>& ea) :
PoolSQL(db, one_db::doc_table) {
DocumentTemplate::parse_encrypted(ea);
};
~DocumentPool(){};

View File

@ -0,0 +1,64 @@
/* -------------------------------------------------------------------------- */
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems */
/* */
/* 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. */
/* -------------------------------------------------------------------------- */
#ifndef DOCUMENT_TEMPLATE_H_
#define DOCUMENT_TEMPLATE_H_
#include "Template.h"
/**
* Document Template class, it represents the attributes of a Document
*/
class DocumentTemplate : public Template
{
public:
DocumentTemplate() : Template(false,'=',"TEMPLATE"){};
~DocumentTemplate(){};
DocumentTemplate(DocumentTemplate& dt):Template(dt){};
DocumentTemplate(const Template& tmpl):Template(tmpl){};
// -------------------------------------------------------------------------
// Encrypted attributes interface implementation
// -------------------------------------------------------------------------
virtual void encrypt(const std::string& one_key)
{
Template::encrypt(one_key, encrypted);
}
virtual void decrypt(const std::string& one_key)
{
Template::decrypt(one_key, encrypted);
}
static void parse_encrypted(std::vector<const SingleAttribute *>& ea)
{
Template::parse_encrypted(ea, encrypted);
}
private:
/**
* Encrypted attribute list for DocumentTemplates
*/
static std::map<std::string, std::set<std::string> > encrypted;
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#endif /*DOCUMENT_TEMPLATE_H_*/

View File

@ -292,9 +292,12 @@ LIB_DIRS="$LIB_LOCATION/ruby \
$LIB_LOCATION/ruby/vcenter_driver \
$LIB_LOCATION/ruby/nsx_driver \
$LIB_LOCATION/oneprovision/lib \
$LIB_LOCATION/oneprovision/lib/resources \
$LIB_LOCATION/oneprovision/lib/resources/virtual \
$LIB_LOCATION/oneprovision/lib/resources/physical"
$LIB_LOCATION/oneprovision/lib/provision \
$LIB_LOCATION/oneprovision/lib/provision_template \
$LIB_LOCATION/oneprovision/lib/provider \
$LIB_LOCATION/oneprovision/lib/provision/resources \
$LIB_LOCATION/oneprovision/lib/provision/resources/virtual \
$LIB_LOCATION/oneprovision/lib/provision/resources/physical"
VAR_DIRS="$VAR_LOCATION/remotes \
$VAR_LOCATION/remotes/etc \
@ -725,9 +728,12 @@ INSTALL_ONEPROVISION_FILES=(
ONEPROVISION_TEMPLATES_FILES:$SHARE_LOCATION/oneprovision
ONEPROVISION_EXAMPLES_FILES:$SHARE_LOCATION/oneprovision
ONEPROVISION_LIB_FILES:$LIB_LOCATION/oneprovision/lib
ONEPROVISION_LIB_RESOURCES_FILES:$LIB_LOCATION/oneprovision/lib/resources
ONEPROVISION_LIB_PHYSICAL_R_FILES:$LIB_LOCATION/oneprovision/lib/resources/physical
ONEPROVISION_LIB_VIRTUAL_R_FILES:$LIB_LOCATION/oneprovision/lib/resources/virtual
ONEPROVISION_LIB_PROVISION_FILES:$LIB_LOCATION/oneprovision/lib/provision
ONEPROVISION_LIB_RESOURCES_FILES:$LIB_LOCATION/oneprovision/lib/provision/resources
ONEPROVISION_LIB_PHYSICAL_R_FILES:$LIB_LOCATION/oneprovision/lib/provision/resources/physical
ONEPROVISION_LIB_VIRTUAL_R_FILES:$LIB_LOCATION/oneprovision/lib/provision/resources/virtual
ONEPROVISION_LIB_PROVIDER_FILES:$LIB_LOCATION/oneprovision/lib/provider
ONEPROVISION_LIB_PROVISION_TEMPLATE_FILES:$LIB_LOCATION/oneprovision/lib/provision_template
)
INSTALL_SUNSTONE_RUBY_FILES=(
@ -2360,11 +2366,17 @@ CLI_CONF_FILES="src/cli/etc/onegroup.yaml \
# Provision files
#-----------------------------------------------------------------------------
ONEPROVISION_BIN_FILES="src/cli/oneprovision"
ONEPROVISION_BIN_FILES="src/cli/oneprovision \
src/cli/oneprovider \
src/cli/oneprovision-template"
ONEPROVISION_ONE_LIB_FILES="src/cli/one_helper/oneprovision_helper.rb"
ONEPROVISION_ONE_LIB_FILES="src/cli/one_helper/oneprovision_helper.rb \
src/cli/one_helper/oneprovider_helper.rb \
src/cli/one_helper/oneprovision_template_helper.rb"
ONEPROVISION_CONF_FILES="src/cli/etc/oneprovision.yaml"
ONEPROVISION_CONF_FILES="src/cli/etc/oneprovision.yaml \
src/cli/etc/oneprovider.yaml \
src/cli/etc/oneprovision_template.yaml"
ONEPROVISION_ANSIBLE_FILES="share/oneprovision/ansible"
@ -2372,31 +2384,40 @@ ONEPROVISION_TEMPLATES_FILES="share/oneprovision/templates"
ONEPROVISION_EXAMPLES_FILES="share/oneprovision/examples"
ONEPROVISION_LIB_FILES="src/oneprovision/lib/ansible.rb \
src/oneprovision/lib/oneprovision.rb \
src/oneprovision/lib/driver.rb \
src/oneprovision/lib/provision.rb \
src/oneprovision/lib/provision_pool.rb \
src/oneprovision/lib/resources.rb \
src/oneprovision/lib/utils.rb"
ONEPROVISION_LIB_FILES="src/oneprovision/lib/oneprovision.rb \
src/oneprovision/lib/provision_element.rb"
ONEPROVISION_LIB_RESOURCES_FILES="src/oneprovision/lib/resources/virtual.rb \
src/oneprovision/lib/resources/resource.rb \
src/oneprovision/lib/resources/physical.rb"
ONEPROVISION_LIB_PROVISION_FILES="src/oneprovision/lib/provision/ansible.rb \
src/oneprovision/lib/provision/oneprovision.rb \
src/oneprovision/lib/provision/driver.rb \
src/oneprovision/lib/provision/provision.rb \
src/oneprovision/lib/provision/provision_pool.rb \
src/oneprovision/lib/provision/resources.rb \
src/oneprovision/lib/provision/utils.rb"
ONEPROVISION_LIB_PHYSICAL_R_FILES="src/oneprovision/lib/resources/physical/cluster.rb \
src/oneprovision/lib/resources/physical/datastore.rb \
src/oneprovision/lib/resources/physical/host.rb \
src/oneprovision/lib/resources/physical/physical_resource.rb \
src/oneprovision/lib/resources/physical/network.rb"
ONEPROVISION_LIB_RESOURCES_FILES="src/oneprovision/lib/provision/resources/virtual.rb \
src/oneprovision/lib/provision/resources/resource.rb \
src/oneprovision/lib/provision/resources/physical.rb"
ONEPROVISION_LIB_VIRTUAL_R_FILES="src/oneprovision/lib/resources/virtual/virtual_resource.rb \
src/oneprovision/lib/resources/virtual/virtual_sync_resource.rb \
src/oneprovision/lib/resources/virtual/image.rb \
src/oneprovision/lib/resources/virtual/marketplaceapp.rb \
src/oneprovision/lib/resources/virtual/template.rb \
src/oneprovision/lib/resources/virtual/flowtemplate.rb \
src/oneprovision/lib/resources/virtual/vntemplate.rb"
ONEPROVISION_LIB_PHYSICAL_R_FILES="src/oneprovision/lib/provision/resources/physical/cluster.rb \
src/oneprovision/lib/provision/resources/physical/datastore.rb \
src/oneprovision/lib/provision/resources/physical/host.rb \
src/oneprovision/lib/provision/resources/physical/physical_resource.rb \
src/oneprovision/lib/provision/resources/physical/network.rb"
ONEPROVISION_LIB_VIRTUAL_R_FILES="src/oneprovision/lib/provision/resources/virtual/virtual_resource.rb \
src/oneprovision/lib/provision/resources/virtual/virtual_sync_resource.rb \
src/oneprovision/lib/provision/resources/virtual/image.rb \
src/oneprovision/lib/provision/resources/virtual/marketplaceapp.rb \
src/oneprovision/lib/provision/resources/virtual/template.rb \
src/oneprovision/lib/provision/resources/virtual/flowtemplate.rb \
src/oneprovision/lib/provision/resources/virtual/vntemplate.rb"
ONEPROVISION_LIB_PROVIDER_FILES="src/oneprovision/lib/provider/provider.rb \
src/oneprovision/lib/provider/provider_pool.rb"
ONEPROVISION_LIB_PROVISION_TEMPLATE_FILES="src/oneprovision/lib/provision_template/provision_template.rb \
src/oneprovision/lib/provision_template/provision_template_pool.rb"
#-----------------------------------------------------------------------------
# Sunstone files

View File

@ -991,9 +991,10 @@ VM_ENCRYPTED_ATTR = "CONTEXT/PASSWORD"
# VNET_ENCRYPTED_ATTR = "PROVISION/PACKET_TOKEN
# DDC encrypted attrs
HOST_ENCRYPTED_ATTR = "PROVISION/PACKET_TOKEN"
HOST_ENCRYPTED_ATTR = "PROVISION/EC2_ACCESS"
HOST_ENCRYPTED_ATTR = "PROVISION/EC2_SECRET"
HOST_ENCRYPTED_ATTR = "PROVISION/PACKET_TOKEN"
HOST_ENCRYPTED_ATTR = "PROVISION/EC2_ACCESS"
HOST_ENCRYPTED_ATTR = "PROVISION/EC2_SECRET"
DOCUMENT_ENCRYPTED_ATTR = "PROVISION_BODY"
VNET_ENCRYPTED_ATTR = "AR/PACKET_TOKEN"

View File

@ -520,6 +520,8 @@ AllCops:
- src/tm_mad/one_tm.rb
- src/oca/ruby/opennebula/flow/grammar.rb
- src/oca/ruby/opennebula/flow/validator.rb
- src/sunstone/models/OpenNebulaValidateFireedge.rb
- src/sunstone/OpenNebulaVMRC.rb
NewCops: enable
########

View File

@ -45,7 +45,10 @@ COMMANDS=(
'onemarketapp' 'manages appliances from Marketplaces'
'onevmgroup' 'manages VM groups'
'onevntemplate' 'manages Virtual Network Templates'
'oneprovision' 'manages OpenNebula provisions'
'oneprovision' 'manages OpenNebula provisions'
'oneprovider' 'manages OpenNebula providers'
'oneprovision-template' 'manages OpenNebula provision templates'
'oneflow' 'Manage oneFlow Services'
'oneflow-template' 'Manage oneFlow Templates'

View File

@ -518,6 +518,52 @@ _oneprovision() {
fi
}
_oneprovision_template() {
local cur prev opts cmd
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="create instantiate list show delete update"
cmd=oneprovision-template
if [ "$COMP_CWORD" == 1 ]; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
elif [ "$COMP_CWORD" == 2 ]; then
case "$prev" in
show|delete|update|instantiate)
_complete $cmd ID
;;
create)
COMPREPLY=( $(compgen -A file -- "${cur}") )
return 0
;;
esac
fi
}
_oneprovider() {
local cur prev opts cmd
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="create list show delete update"
cmd=oneprovider
if [ "$COMP_CWORD" == 1 ]; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
elif [ "$COMP_CWORD" == 2 ]; then
case "$prev" in
show|delete|update)
_complete $cmd ID
;;
create)
COMPREPLY=( $(compgen -A file -- "${cur}") )
return 0
;;
esac
fi
}
_onesecgroup() {
local cur prev opts cmd
COMPREPLY=()
@ -864,6 +910,8 @@ complete -F _oneimage oneimage
complete -F _onemarket onemarket
complete -F _onemarketapp onemarketapp
complete -F _oneprovision oneprovision
complete -F _oneprovision_template oneprovision-template
complete -F _oneprovider oneprovider
complete -F _onesecgroup onesecgroup
complete -F _oneshowback oneshowback
complete -F _onetemplate onetemplate

View File

@ -0,0 +1,19 @@
---
:ID:
:desc: Provider identifier
:size: 4
:NAME:
:desc: Name of the Provider
:size: 15
:left: true
:expand: true
:REGTIME:
:desc: Registration time of the Provider
:size: 15
:default:
- :ID
- :NAME
- :REGTIME

View File

@ -0,0 +1,19 @@
---
:ID:
:desc: Provision Template identifier
:size: 4
:NAME:
:desc: Name of the Provision Template
:size: 15
:left: true
:expand: true
:REGTIME:
:desc: Registration time of the Provision Template
:size: 15
:default:
- :ID
- :NAME
- :REGTIME

View File

@ -613,8 +613,15 @@ EOT
pname = pool.pool_name
ename = pool.element_name
page = pool.to_hash
elems = page["#{pname}"]["#{ename}"]
if options[:decrypt]
page = pool.map do |element|
element.info(true)
element.to_hash[ename]
end
else
page = pool.to_hash
elems = page[pname][ename]
end
if elems.class == Array
elements = elems.length
@ -1561,6 +1568,39 @@ EOT
end
end
def OpenNebulaHelper.update_obj(obj, file)
rc = obj.info(true)
return rc if OpenNebula.is_error?(rc)
if file
path = file
else
tmp = Tempfile.new(obj['ID'])
path = tmp.path
tmp.write(yield(obj)) if block_given?
tmp.flush
if ENV['EDITOR']
editor_path = ENV['EDITOR']
else
editor_path = EDITOR_PATH
end
system("#{editor_path} #{path}")
unless $CHILD_STATUS.exitstatus.zero?
STDERR.puts 'Editor not defined'
exit(-1)
end
tmp.close
end
obj.update(File.read(path))
end
def OpenNebulaHelper.editor_input(contents=nil)
require 'tempfile'

View File

@ -0,0 +1,199 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# 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 'json'
require 'yaml'
require 'provision_element'
# OneProvider Helper
class OneProviderHelper < OpenNebulaHelper::OneHelper
TAG = OneProvision::ProvisionElement::TEMPLATE_TAG
# Resource name
def self.rname
'DOCUMENT'
end
# Configuration file name
def self.conf_file
'oneprovider.yaml'
end
# Format pool for CLI list operation
def format_pool(_)
config_file = self.class.table_conf
CLIHelper::ShowTable.new(config_file, self) do
column :ID, 'Provider identifier', :size => 4 do |p|
p['ID']
end
column :NAME, 'Name of the provider', :left, :size => 25 do |p|
p['NAME']
end
column :REGTIME,
'Registration time of the Provider',
:size => 15 do |p|
p.extend(CLIHelper::HashWithSearch)
p = JSON.parse(p.dsearch("TEMPLATE/#{TAG}"))
OpenNebulaHelper.time_to_str(p['registration_time'])
end
default :ID, :NAME, :REGTIME
end
end
# Shows provider information
#
# @param id [Integer] Provider ID
# @param options [Hash] User CLI options
#
# @return [Array] [rc, information to show]
def show_resource(id, options)
# Add body paremter to get resource in hash format
options[:body] = true
options[:decrypt] = true
info = super
return info if info[0] != 0
if options[:json]
info[1]['DOCUMENT']['TEMPLATE'][TAG] = JSON.parse(
info[1]['DOCUMENT']['TEMPLATE'][TAG]
)
info[1] = JSON.pretty_generate(info[1])
elsif options[:yaml]
yaml = YAML.safe_load(info[1])
yaml['DOCUMENT']['TEMPLATE'][TAG] = JSON.parse(
yaml['DOCUMENT']['TEMPLATE'][TAG]
)
info[1] = yaml.to_yaml(:indent => 4)
end
info
end
# Format resource to output it in show command
#
# @param provider [Hash] Provider information
def format_resource(provider, _)
str_h1 = '%-80s'
id = provider['ID']
body = provider.body
CLIHelper.print_header(str_h1 % "PROVIDER #{id} INFORMATION")
puts format('ID : %<s>s', :s => id)
puts format('NAME : %<s>s', :s => provider['NAME'])
# Get max size to adjust all the values
size = body['connection'].keys.map {|k| k.size }.max
data = {}
# Generate data value with the new key adjusted format
body['connection'].map {|k, v| data[k.ljust(size)] = v }
puts
CLIHelper.print_header(str_h1 % 'CONNECTION INFORMATION')
data.each do |key, value|
CLIHelper.scr_bold
print "#{key} : "
CLIHelper.scr_restore
puts value
end
end
#######################################################################
# Helper provider functions
#######################################################################
# Creates a new provider
#
# @param template [String] Path to file with template information
#
# @param [Integer] Provider ID
def create(template)
rc = validate(template)
return rc if OpenNebula.is_error?(rc)
xml = OneProvision::Provider.build_xml
provider = OneProvision::Provider.new(xml, @client)
rc = provider.allocate(rc)
return rc if OpenNebula.is_error?(rc)
rc = provider.info(true)
return rc if OpenNebula.is_error?(rc)
provider['ID']
end
# Updates provider information
#
# @param provider [OneProvision::Provider] Provider object
# @param file [String] Path to file with update content
#
# @return [0, OpenNebula::Error]
def update(provider, file)
OpenNebulaHelper.update_obj(provider, file) do |prov|
JSON.pretty_generate(prov.body)
end
end
private
# Validates that configuration file can be loaded and it follows structure
#
# @param template [String] Path to file with template information
#
# @return [YAML] Template in YAML format
def validate(template)
begin
template = YAML.load_file(template)
raise 'Name not found' unless template['name']
raise 'Connection info not found' unless template['connection']
template
rescue StandardError => e
OpenNebula::Error.new("ERROR: #{e}")
end
end
# Returns a new provider object
#
# @param id [Integer] Provider ID
def factory(id = nil)
if id
OneProvision::Provider.new_with_id(id, @client)
else
OneProvision::Provider.new(OneProvision::Provider.build_xml,
@client)
end
end
# Returns provider pool
def factory_pool(_)
OneProvision::ProviderPool.new(@client)
end
end

View File

@ -16,9 +16,227 @@
require 'json'
require 'oneprovision'
# OneProvision Helper
class OneProvisionHelper < OpenNebulaHelper::OneHelper
########################################################################
# Global Options
########################################################################
############################ Output Modes ##############################
VERBOSE = {
:name => 'verbose',
:short => '-d',
:large => '--verbose',
:description => 'Set verbose logging mode'
}
DEBUG = {
:name => 'debug',
:short => '-D',
:large => '--debug',
:description => 'Set debug logging mode',
:format => String
}
############################# Run Modes ################################
BATCH = {
:name => 'batch',
:short => '-b',
:large => '--batch',
:description => 'Run in non-interactive mode (no questions)',
:format => String
}
FAIL_MODES = {
:name => 'fail_modes',
:large => '--fail-modes mode1,mode2',
:description => 'Fail modes to apply in order',
:format => Array,
:proc => lambda do |_, options|
options = options[:fail_modes].map do |v|
v = v.downcase.to_sym
unless OneProvision::DEFAULT_FAIL_MODES.include?(v)
STDERR.puts "Wrong fail mode `#{v}`"
exit(-1)
end
v
end
[0, options]
end
}
FAIL_RETRY = {
:name => 'fail_retry',
:large => '--fail-retry number',
:description => 'Set batch failover mode to number of retries',
:format => Integer
}
FAIL_CLEANUP = {
:name => 'fail_cleanup',
:large => '--fail-cleanup',
:description => 'Set batch failover mode to clean up and quit'
}
FAIL_SKIP = {
:name => 'fail_skip',
:large => '--fail-skip',
:description => 'Set batch failover mode to skip failing part'
}
FAIL_QUIT = {
:name => 'fail_quit',
:large => '--fail-quit',
:description => 'Set batch failover mode to quit (default)'
}
FAIL_SLEEP = {
:name => 'fail_sleep',
:large => '--fail-sleep seconds',
:description => 'Time in seconds between each fail mode is executed ' \
'and between each retry',
:format => Integer
}
############################## Create ##################################
PING_TIMEOUT = {
:name => 'ping_timeout',
:large => '--ping-timeout seconds',
:description => 'Set timeout for ping ' \
"(default: #{OneProvision::PING_TIMEOUT_DEFAULT} secs)",
:format => Integer
}
PING_RETRIES = {
:name => 'ping_retries',
:large => '--ping-retries number',
:description => 'Set retries for ping ' \
"(default: #{OneProvision::PING_RETRIES_DEFAULT})",
:format => Integer
}
SKIP_PROVISION = {
:name => 'skip_provision',
:large => '--skip-provision',
:description => 'Skip provision and configuration hosts phases'
}
SKIP_CONFIG = {
:name => 'skip_config',
:large => '--skip-config',
:description => 'Skip configuration hosts phase'
}
WAIT_READY = {
:name => 'wait_ready',
:large => '--wait-ready',
:description => 'Wait resources to be ready in OpenNebula'
}
WAIT_TIMEOUT = {
:name => 'wait_timeout',
:large => '--wait-timeout timeout',
:description => 'Timeout to wait objects to be ready',
:format => Integer
}
PROVIDER = {
:name => 'provider',
:large => '--provider provider',
:description => 'Provider to deploy provision',
:format => String
}
########################################################################
THREADS = {
:name => 'threads',
:short => '-t threads',
:large => '--threads threads',
:description => 'Set threads for create (default: ' \
"#{OneProvision::THREADS_DEFAULT})",
:format => Integer
}
FORCE = {
:name => 'force',
:short => '-F',
:large => '--force',
:description => 'Force configure to execute',
:format => String
}
HARD = {
:name => 'hard',
:short => '-H',
:large => '--hard',
:description => 'Reset the host',
:format => String
}
CLEANUP = {
:name => 'cleanup',
:large => '--cleanup',
:description => 'Delete all vms and images first, ' \
'then delete the resources.'
}
CLEANUP_TIMEOUT = {
:name => 'cleanup_timeout',
:large => '--cleanup-timeout timeout',
:description => 'Change the default timeout when deleting VMs/Images.'
}
DUMP = {
:name => 'dump',
:large => '--dump',
:description => 'Dump the configuration file result.'
}
DONE = {
:name => 'done',
:large => '--done',
:description => 'List provisions in DONE state'
}
########################################################################
MODES = CommandParser::OPTIONS - [CommandParser::VERBOSE] +
[VERBOSE,
DEBUG,
BATCH,
FAIL_RETRY,
FAIL_CLEANUP,
FAIL_SKIP,
FAIL_QUIT,
FAIL_MODES,
FAIL_SLEEP]
CREATE_OPTIONS = [THREADS,
MODES,
PING_TIMEOUT,
PING_RETRIES,
CLEANUP,
CLEANUP_TIMEOUT,
SKIP_PROVISION,
SKIP_CONFIG,
WAIT_READY,
WAIT_TIMEOUT,
PROVIDER]
ONE_OPTIONS = CommandParser::OPTIONS +
CLIHelper::OPTIONS +
OpenNebulaHelper::OPTIONS
# Resource name
def self.rname
'DOCUMENT'
@ -53,27 +271,37 @@ class OneProvisionHelper < OpenNebulaHelper::OneHelper
end
column :CLUSTERS, 'Number of Clusters', :size => 8 do |p|
p = OneProvisionHelper.body(p)
p.extend(CLIHelper::HashWithSearch)
p = JSON.parse(p.dsearch('TEMPLATE/BODY'))
p['provision']['infrastructure']['clusters'].size rescue 0
end
column :HOSTS, 'Number of Hosts', :size => 5 do |p|
p = OneProvisionHelper.body(p)
p.extend(CLIHelper::HashWithSearch)
p = JSON.parse(p.dsearch('TEMPLATE/BODY'))
p['provision']['infrastructure']['hosts'].size rescue 0
end
column :NETWORKS, 'Number of Networks', :size => 8 do |p|
p = OneProvisionHelper.body(p)
p.extend(CLIHelper::HashWithSearch)
p = JSON.parse(p.dsearch('TEMPLATE/BODY'))
p['provision']['infrastructure']['networks'].size rescue 0
end
column :DATASTORES, 'Number of Datastores', :size => 10 do |p|
p = OneProvisionHelper.body(p)
p.extend(CLIHelper::HashWithSearch)
p = JSON.parse(p.dsearch('TEMPLATE/BODY'))
p['provision']['infrastructure']['datastores'].size rescue 0
end
column :STAT, 'Provision state', :size => 12 do |p|
p = OneProvisionHelper.body(p)
p.extend(CLIHelper::HashWithSearch)
p = JSON.parse(p.dsearch('TEMPLATE/BODY'))
OneProvision::Provision::STATE_STR[p['state']] rescue '-'
end
@ -113,10 +341,11 @@ class OneProvisionHelper < OpenNebulaHelper::OneHelper
body = provision.body
CLIHelper.print_header(str_h1 % "PROVISION #{id} INFORMATION")
puts format('ID : %<s>s', :s => id)
puts format('NAME : %<s>s', :s => provision['NAME'])
puts format('STATE : %<s>s',
puts format('ID : %<s>s', :s => id)
puts format('NAME : %<s>s', :s => provision['NAME'])
puts format('STATE : %<s>s',
:s => OneProvision::Provision::STATE_STR[body['state']])
puts format('PROVIDER : %<s>s', :s => body['provider'])
infrastructure = provision.infrastructure_objects
resource = provision.resource_objects
@ -138,17 +367,27 @@ class OneProvisionHelper < OpenNebulaHelper::OneHelper
# Creates a new provision
#
# @param config [String] Path to deployment file
# @param cleanup [Boolean] True to cleanup everything
# @param timeout [Integer] Timeout in seconds to connect to hosts
# @param skip [Symbol] Skip provision, configuration or anything
# @param config [String] Path to deployment file
# @param cleanup [Boolean] True to cleanup everything
# @param timeout [Integer] Timeout in seconds to connect to hosts
# @param skip [Symbol] Skip provision, configuration or anything
# @param provider [String/Integer] Provider to deploy the provision
#
# @param [Intenger] New provision ID
def create(config, cleanup, timeout, skip)
def create(config, cleanup, timeout, skip, provider)
xml = OneProvision::Provision.build_xml
provision = OneProvision::Provision.new(xml, @client)
provision.deploy(config, cleanup, timeout, skip)
# If user has sepcified a provider, get it from the pool
if provider
provider = OneProvision::Provider.by_name(@client, provider)
return provider if OpenNebula.is_error?(provider)
return OpenNebula::Error.new('Provider not found') unless provider
end
provision.deploy(config, cleanup, timeout, skip, provider)
end
# Configures provision hosts
@ -300,7 +539,8 @@ class OneProvisionHelper < OpenNebulaHelper::OneHelper
# Iterate over pool to get IDs and get the filter option
pool.each do |obj|
body = OneProvisionHelper.body(obj)
obj.extend(CLIHelper::HashWithSearch)
body = JSON.parse(obj.dsearch('TEMPLATE/BODY'))
next unless body['provision'][path][type.downcase]
@ -358,17 +598,6 @@ class OneProvisionHelper < OpenNebulaHelper::OneHelper
OneProvision::ProvisionPool.new(@client)
end
class << self
# Returns provision body in JSON format
#
# @param document [Hash] Provision document
def body(document)
JSON.parse(document['TEMPLATE']['BODY'])
end
end
# Returns helper and filter depending on type
#
# @param type [String] Helper type

View File

@ -0,0 +1,199 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# 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 'json'
require 'yaml'
# OneProvisionTemplate Helper
class OneProvisionTemplateHelper < OpenNebulaHelper::OneHelper
TAG = OneProvision::ProvisionElement::TEMPLATE_TAG
# Resource name
def self.rname
'DOCUMENT'
end
# Configuration file name
def self.conf_file
'oneprovision_template.yaml'
end
# Format pool for CLI list operation
def format_pool(_)
config_file = self.class.table_conf
CLIHelper::ShowTable.new(config_file, self) do
column :ID, 'Provision Template identifier', :size => 4 do |p|
p['ID']
end
column :NAME, 'Name of the template', :left, :size => 25 do |p|
p['NAME']
end
column :REGTIME,
'Registration time of the Provision Template',
:size => 15 do |p|
p.extend(CLIHelper::HashWithSearch)
p = JSON.parse(p.dsearch("TEMPLATE/#{TAG}"))
OpenNebulaHelper.time_to_str(p['registration_time'])
end
default :ID, :NAME, :REGTIME
end
end
# Shows provision template information
#
# @param id [Integer] Template ID
# @param options [Hash] User CLI options
#
# @return [Array] [rc, information to show]
def show_resource(id, options)
# Add body paremter to get resource in hash format
options[:body] = true
options[:decrypt] = true
info = super
return info if info[0] != 0
if options[:json]
info[1]['DOCUMENT']['TEMPLATE'][TAG] = JSON.parse(
info[1]['DOCUMENT']['TEMPLATE'][TAG]
)
info[1] = JSON.pretty_generate(info[1])
elsif options[:yaml]
yaml = YAML.safe_load(info[1])
yaml['DOCUMENT']['TEMPLATE'][TAG] = JSON.parse(
yaml['DOCUMENT']['TEMPLATE'][TAG]
)
info[1] = yaml.to_yaml(:indent => 4)
end
info
end
# Format resource to output it in show command
#
# @param template [Hash] Provision template information
def format_resource(template, _)
str_h1 = '%-80s'
id = template['ID']
body = template.body
CLIHelper.print_header(str_h1 % "PROVISION TEMPLATE #{id} INFORMATION")
puts format('ID : %<s>s', :s => id)
puts format('NAME : %<s>s', :s => template['NAME'])
puts
CLIHelper.print_header(str_h1 % 'PROVISION TEMPLATE')
puts JSON.pretty_generate(body)
end
#######################################################################
# Helper provision template functions
#######################################################################
# Creates a new provision template
#
# @param template [String] Path to file with template information
#
# @param [Integer] Provision Template ID
def create(template)
rc = validate(template)
return rc if OpenNebula.is_error?(rc)
xml = OneProvision::ProvisionTemplate.build_xml
template = OneProvision::ProvisionTemplate.new(xml, @client)
rc = template.allocate(rc)
return rc if OpenNebula.is_error?(rc)
rc = template.info(true)
return rc if OpenNebula.is_error?(rc)
template['ID']
end
# Updates provision template information
#
# @param template [OneProvision::ProvisionTemplate] Template object
# @param file [String] Path to file with update content
#
# @return [0, OpenNebula::Error]
def update(template, file)
OpenNebulaHelper.update_obj(template, file) do |tmpl|
JSON.pretty_generate(tmpl.body)
end
end
# Updates provision template information
#
# @param template [OneProvision::ProvisionTemplate] Template object
#
# @return [0, OpenNebula::Error]
def instantiate(template)
rc = template.info(true)
return rc if OpenNebula.is_error?(rc)
template.instantiate
end
private
# Validates that configuration file can be loaded and it follows structure
#
# @param template [String] Path to file with template information
#
# @return [YAML] Template in YAML format
def validate(template)
begin
template = OneProvision::Utils.read_config(template)
provider = OneProvision::Provision.read_provider(template)
raise 'Name not found' unless template['name']
raise 'Provider not found in template' unless provider
template
rescue StandardError => e
OpenNebula::Error.new("ERROR: #{e}")
end
end
# Returns a new provision template object
#
# @param id [Integer] Provision Template ID
def factory(id = nil)
if id
OneProvision::ProvisionTemplate.new_with_id(id, @client)
else
xml = OneProvision::ProvisionTemplate.build_xml
OneProvision::ProvisionTemplate.new(xml, @client)
end
end
# Returns provision template pool
def factory_pool(_)
OneProvision::ProvisionTemplatePool.new(@client)
end
end

214
src/cli/oneprovider Executable file
View File

@ -0,0 +1,214 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# 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. #
#--------------------------------------------------------------------------- #
ONE_LOCATION = ENV['ONE_LOCATION']
if !ONE_LOCATION
LIB_LOCATION = '/usr/lib/one'
RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
GEMS_LOCATION = '/usr/share/one/gems'
else
LIB_LOCATION = ONE_LOCATION + '/lib'
RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
GEMS_LOCATION = ONE_LOCATION + '/share/gems'
end
if File.directory?(GEMS_LOCATION)
$LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }
require 'rubygems'
Gem.use_paths(File.realpath(GEMS_LOCATION))
end
$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << RUBY_LIB_LOCATION + '/cli'
$LOAD_PATH << LIB_LOCATION + '/oneprovision/lib'
$LOAD_PATH << LIB_LOCATION + '/oneflow/lib'
require 'command_parser'
require 'one_helper'
require 'one_helper/onegroup_helper'
require 'one_helper/oneprovider_helper'
require 'oneprovision'
CommandParser::CmdParser.new(ARGV) do
usage '`oneprovider` <command> [<file>] [<args>] [<options>]'
version OpenNebulaHelper::ONE_VERSION
helper = OneProviderHelper.new
before_proc do
helper.set_client(options)
end
########################################################################
# Global options
########################################################################
cmd_options = CommandParser::OPTIONS - [CommandParser::VERBOSE]
set :option, cmd_options + OpenNebulaHelper::CLIENT_OPTIONS
########################################################################
# Formatters for arguments
########################################################################
set :format, :providerid, OneProviderHelper.to_id_desc do |arg|
helper.to_id(arg)
end
set :format, :providerid_list, OneProviderHelper.list_to_id_desc do |arg|
helper.list_to_id(arg)
end
set :format, :groupid, OneGroupHelper.to_id_desc do |arg|
h = OneGroupHelper.new
h.set_client(options)
h.to_id(arg)
end
set :format, :userid, OpenNebulaHelper.rname_to_id_desc('USER') do |arg|
OpenNebulaHelper.rname_to_id(arg, 'USER')
end
########################################################################
# Commands
########################################################################
create_desc = <<-EOT.unindent
Allocate a new provider. Configuration file must be written in YAML.
$ cat packet.yaml
name: packet
connection:
packet_token: "Packet token"
packet_project: "Packet project"
facility: "Packet facility"
$ cat ec2.yaml
name: "ec2",
connection:
ec2_access: "AWS access key"
ec2_secret: "AWS secret key"
region_name: "AWS region name""
EOT
command :create, create_desc, :template do
rc = helper.create(args[0])
if OpenNebula.is_error?(rc)
STDERR.puts rc.message
exit(-1)
else
puts "ID: #{rc}"
0
end
end
###
delete_desc = <<-EOT.unindent
Delete a provider
EOT
command :delete, delete_desc, [:range, :providerid_list] do
helper.perform_actions(args[0], options, 'deleted') do |provider|
provider.delete
end
end
###
list_desc = <<-EOT.unindent
List all avaliable providers
EOT
command :list,
list_desc,
:options => CLIHelper::OPTIONS + [OpenNebulaHelper::FORMAT] do
options[:decrypt] = true
helper.list_pool(options)
end
###
show_desc = <<-EOT.unindent
Show provider details
EOT
command :show,
show_desc,
:providerid,
:options => OpenNebulaHelper::FORMAT do
helper.show_resource(args[0], options)
end
###
update_desc = <<-EOT.unindent
Update provider information
EOT
command :update, update_desc, :providerid, [:file, nil] do
helper.perform_action(args[0], options, 'updated') do |obj|
helper.update(obj, args[1])
end
end
###
chgrp_desc = <<-EOT.unindent
Changes the Provider group
EOT
command :chgrp, chgrp_desc, [:range, :providerid_list], :groupid do
helper.perform_actions(args[0], options, 'Group changed') do |p|
p.chown(-1, args[1].to_i)
end
end
###
chown_desc = <<-EOT.unindent
Changes the Provider owner and group
EOT
command :chown,
chown_desc,
[:range, :providerid_list],
:userid,
[:groupid, nil] do
args[2].nil? ? gid = -1 : gid = args[2].to_i
helper.perform_actions(args[0], options, 'Owner/Group changed') do |p|
p.chown(args[1].to_i, gid)
end
end
###
chmod_desc = <<-EOT.unindent
Changes the Provider permissions
EOT
command :chmod, chmod_desc, [:range, :providerid_list], :octet do
helper.perform_actions(args[0], options, 'Permissions changed') do |p|
p.chmod_octet(args[1])
end
end
end

View File

@ -29,7 +29,7 @@ else
RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
GEMS_LOCATION = ONE_LOCATION + '/share/gems'
REMOTES_LOCATION = ONE_LOCATION + '/var/remotes'
ANSIBLE_LOCATION = ONE_LOCATION + '/usr/share/oneprovision/ansible'
ANSIBLE_LOCATION = ONE_LOCATION + '/share/oneprovision/ansible'
end
if File.directory?(GEMS_LOCATION)
@ -43,16 +43,6 @@ $LOAD_PATH << RUBY_LIB_LOCATION + '/cli'
$LOAD_PATH << LIB_LOCATION + '/oneprovision/lib'
$LOAD_PATH << LIB_LOCATION + '/oneflow/lib'
PING_TIMEOUT_DEFAULT = 20
PING_RETRIES_DEFAULT = 10
MAX_RETRIES_DEFAULT = 3
RUN_MODE_DEFAULT = :interactive
FAIL_CHOICE_DEFAULT = :quit
DEFAULT_FAIL_MODES = [:cleanup, :retry, :skip, :quit]
CLEANUP_DEFAULT = false
THREADS_DEFAULT = 3
WAIT_TIMEOUT_DEFAULT = 60
require 'command_parser'
require 'one_helper'
@ -79,211 +69,23 @@ CommandParser::CmdParser.new(ARGV) do
end
########################################################################
# Global Options
# Global options
########################################################################
############################ Output Modes ##############################
VERBOSE = {
:name => 'verbose',
:short => '-d',
:large => '--verbose',
:description => 'Set verbose logging mode'
}
DEBUG = {
:name => 'debug',
:short => '-D',
:large => '--debug',
:description => 'Set debug logging mode',
:format => String
}
############################# Run Modes ################################
BATCH = {
:name => 'batch',
:short => '-b',
:large => '--batch',
:description => 'Run in non-interactive mode (no questions)',
:format => String
}
FAIL_MODES = {
:name => 'fail_modes',
:large => '--fail-modes mode1,mode2',
:description => 'Fail modes to apply in order',
:format => Array,
:proc => lambda do |_, options|
options = options[:fail_modes].map do |v|
v = v.downcase.to_sym
unless DEFAULT_FAIL_MODES.include?(v)
STDERR.puts "Wrong fail mode `#{v}`"
exit(-1)
end
v
end
[0, options]
end
}
FAIL_RETRY = {
:name => 'fail_retry',
:large => '--fail-retry number',
:description => 'Set batch failover mode to number of retries',
:format => Integer
}
FAIL_CLEANUP = {
:name => 'fail_cleanup',
:large => '--fail-cleanup',
:description => 'Set batch failover mode to clean up and quit'
}
FAIL_SKIP = {
:name => 'fail_skip',
:large => '--fail-skip',
:description => 'Set batch failover mode to skip failing part'
}
FAIL_QUIT = {
:name => 'fail_quit',
:large => '--fail-quit',
:description => 'Set batch failover mode to quit (default)'
}
FAIL_SLEEP = {
:name => 'fail_sleep',
:large => '--fail-sleep seconds',
:description => 'Time in seconds between each fail mode is executed ' \
'and between each retry',
:format => Integer
}
############################## Create ##################################
PING_TIMEOUT = {
:name => 'ping_timeout',
:large => '--ping-timeout seconds',
:description => 'Set timeout for ping ' \
"(default: #{PING_TIMEOUT_DEFAULT} secs)",
:format => Integer
}
PING_RETRIES = {
:name => 'ping_retries',
:large => '--ping-retries number',
:description => 'Set retries for ping ' \
"(default: #{PING_RETRIES_DEFAULT})",
:format => Integer
}
SKIP_PROVISION = {
:name => 'skip_provision',
:large => '--skip-provision',
:description => 'Skip provision and configuration hosts phases'
}
SKIP_CONFIG = {
:name => 'skip_config',
:large => '--skip-config',
:description => 'Skip configuration hosts phase'
}
WAIT_READY = {
:name => 'wait_ready',
:large => '--wait-ready',
:description => 'Wait resources to be ready in OpenNebula'
}
WAIT_TIMEOUT = {
:name => 'wait_timeout',
:large => '--wait-timeout timeout',
:description => 'Timeout to wait objects to be ready',
:format => Integer
}
cmd_options = CommandParser::OPTIONS - [CommandParser::VERBOSE]
set :option, cmd_options + OpenNebulaHelper::CLIENT_OPTIONS
########################################################################
THREADS = {
:name => 'threads',
:short => '-t threads',
:large => '--threads threads',
:description => "Set threads for create (default: #{THREADS_DEFAULT})",
:format => Integer
}
FORCE = {
:name => 'force',
:short => '-F',
:large => '--force',
:description => 'Force configure to execute',
:format => String
}
HARD = {
:name => 'hard',
:short => '-H',
:large => '--hard',
:description => 'Reset the host',
:format => String
}
CLEANUP = {
:name => 'cleanup',
:large => '--cleanup',
:description => 'Delete all vms and images first, ' \
'then delete the resources.'
}
CLEANUP_TIMEOUT = {
:name => 'cleanup_timeout',
:large => '--cleanup-timeout timeout',
:description => 'Change the default timeout when deleting VMs/Images.'
}
DUMP = {
:name => 'dump',
:large => '--dump',
:description => 'Dump the configuration file result.'
}
DONE = {
:name => 'done',
:large => '--done',
:description => 'List provisions in DONE state'
}
# Formatters for arguments
########################################################################
MODES = CommandParser::OPTIONS - [CommandParser::VERBOSE] +
[VERBOSE,
DEBUG,
BATCH,
FAIL_RETRY,
FAIL_CLEANUP,
FAIL_SKIP,
FAIL_QUIT,
FAIL_MODES,
FAIL_SLEEP]
set :format, :provisionid, OneProvisionHelper.to_id_desc do |arg|
helper.to_id(arg)
end
CREATE_OPTIONS = [THREADS,
MODES,
PING_TIMEOUT,
PING_RETRIES,
CLEANUP,
CLEANUP_TIMEOUT,
SKIP_PROVISION,
SKIP_CONFIG,
WAIT_READY,
WAIT_TIMEOUT]
ONE_OPTIONS = CommandParser::OPTIONS +
CLIHelper::OPTIONS +
OpenNebulaHelper::OPTIONS
set :format, :provisionid_list, OneProvisionHelper.list_to_id_desc do |arg|
helper.list_to_id(arg)
end
########################################################################
# Provision Commands
@ -293,7 +95,10 @@ CommandParser::CmdParser.new(ARGV) do
Provision a new cluster via bare metal provider
EOT
command :create, create_desc, :config, :options => CREATE_OPTIONS do
command :create,
create_desc,
:config,
:options => OneProvisionHelper::CREATE_OPTIONS do
helper.parse_options(options)
if options[:cleanup_timeout].nil?
@ -311,7 +116,11 @@ CommandParser::CmdParser.new(ARGV) do
skip = :none
end
rc = helper.create(args[0], (options.key? :cleanup), timeout, skip)
rc = helper.create(args[0],
(options.key? :cleanup),
timeout,
skip,
options[:provider])
if OpenNebula.is_error?(rc)
STDERR.puts rc.message
@ -328,7 +137,10 @@ CommandParser::CmdParser.new(ARGV) do
Validate configuration file
EOT
command :validate, validate_desc, [:config_file], :options => DUMP do
command :validate,
validate_desc,
[:config_file],
:options => OneProvisionHelper::DUMP do
dump = options.key? :dump
OneProvision::Utils.validate_configuration(args[0], dump)
@ -342,9 +154,8 @@ CommandParser::CmdParser.new(ARGV) do
command :list,
provision_list_desc,
:options => CommandParser::OPTIONS +
CLIHelper::OPTIONS +
[OpenNebulaHelper::FORMAT, DONE] do
:options => CLIHelper::OPTIONS +
[OpenNebulaHelper::FORMAT, OneProvisionHelper::DONE] do
if !(options.key? :filter) && !(options.key? :done)
options[:filter] = ['STAT!=DONE']
end
@ -361,7 +172,7 @@ CommandParser::CmdParser.new(ARGV) do
command :show,
provision_show_desc,
:provisionid,
:options => CommandParser::OPTIONS + OpenNebulaHelper::FORMAT do
:options => OpenNebulaHelper::FORMAT do
helper.show_resource(args[0], options)
end
@ -374,7 +185,8 @@ CommandParser::CmdParser.new(ARGV) do
command :configure,
provision_configure_desc,
:provisionid,
:options => [MODES, FORCE] do
:options => [OneProvisionHelper::MODES,
OneProvisionHelper::FORCE] do
helper.parse_options(options)
rc = helper.configure(args[0], options.key?(:force))
@ -396,7 +208,10 @@ CommandParser::CmdParser.new(ARGV) do
command :delete,
provision_delete_desc,
:provisionid,
:options => [MODES, THREADS, CLEANUP, CLEANUP_TIMEOUT] do
:options => [OneProvisionHelper::MODES,
OneProvisionHelper::THREADS,
OneProvisionHelper::CLEANUP,
OneProvisionHelper::CLEANUP_TIMEOUT] do
helper.parse_options(options)
if options[:cleanup_timeout].nil?
@ -426,7 +241,7 @@ CommandParser::CmdParser.new(ARGV) do
command [:host, :resume],
host_resume_desc,
[:range, :hostid_list],
:options => [MODES] do
:options => [OneProvisionHelper::MODES] do
operation = { :operation => 'resume', :message => 'enabled' }
rc = helper.resources_operation(args, operation, options, 'HOSTS')
@ -446,7 +261,7 @@ CommandParser::CmdParser.new(ARGV) do
command [:host, :poweroff],
host_poweroff_desc,
[:range, :hostid_list],
:options => [MODES] do
:options => [OneProvisionHelper::MODES] do
operation = { :operation => 'poweroff', :message => 'disabled' }
rc = helper.resources_operation(args, operation, options, 'HOSTS')
@ -466,7 +281,7 @@ CommandParser::CmdParser.new(ARGV) do
command [:host, :reboot],
host_reboot_desc,
[:range, :hostid_list],
:options => [MODES, HARD] do
:options => [OneProvisionHelper::MODES, OneProvisionHelper::HARD] do
operation = { :operation => 'reboot', :message => 'enabled' }
rc = helper.resources_operation(args, operation, options, 'HOSTS')
@ -486,7 +301,7 @@ CommandParser::CmdParser.new(ARGV) do
command [:host, :delete],
host_delete_desc,
[:range, :hostid_list],
:options => [MODES] do
:options => [OneProvisionHelper::MODES] do
operation = { :operation => 'delete', :message => 'deleted' }
rc = helper.resources_operation(args, operation, options, 'HOSTS')
@ -506,8 +321,9 @@ CommandParser::CmdParser.new(ARGV) do
command [:host, :configure],
host_configure_desc,
[:range, :hostid_list],
:options => [MODES, FORCE] do
operation = { :operation => 'configure', :message => 'enabled' }
:options => [OneProvisionHelper::MODES,
OneProvisionHelper::FORCE] do
operation = { :operation => 'configure', :message => 'enabled' }
rc = helper.resources_operation(args, operation, options, 'HOSTS')
@ -545,7 +361,8 @@ CommandParser::CmdParser.new(ARGV) do
command [:host, :list],
host_list_desc,
:options => ONE_OPTIONS + [OpenNebulaHelper::DESCRIBE] do
:options => OneProvisionHelper::ONE_OPTIONS +
[OpenNebulaHelper::DESCRIBE] do
helper.list_objects('HOSTS', options)
end
@ -557,7 +374,7 @@ CommandParser::CmdParser.new(ARGV) do
command [:host, :top],
host_top_desc,
:options => ONE_OPTIONS do
:options => OneProvisionHelper::ONE_OPTIONS do
helper.list_objects('HOSTS', options, true)
end
@ -574,7 +391,8 @@ CommandParser::CmdParser.new(ARGV) do
command [resource.chomp('s').to_sym, :list],
list_desc,
:options => ONE_OPTIONS + [OpenNebulaHelper::DESCRIBE] do
:options => OneProvisionHelper::ONE_OPTIONS +
[OpenNebulaHelper::DESCRIBE] do
helper.list_objects(resource.upcase, options)
end
end
@ -591,7 +409,8 @@ CommandParser::CmdParser.new(ARGV) do
command [resource.chomp('s').to_sym, :delete],
delete_desc,
[:range, :id_list],
:options => [MODES, FORCE] do
:options => [OneProvisionHelper::MODES,
OneProvisionHelper::FORCE] do
rc = helper.resources_operation(args,
{},
options,

253
src/cli/oneprovision-template Executable file
View File

@ -0,0 +1,253 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# 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. #
#--------------------------------------------------------------------------- #
ONE_LOCATION = ENV['ONE_LOCATION']
if !ONE_LOCATION
LIB_LOCATION = '/usr/lib/one'
RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
GEMS_LOCATION = '/usr/share/one/gems'
REMOTES_LOCATION = '/var/lib/one/remotes'
ANSIBLE_LOCATION = '/usr/share/one/oneprovision/ansible'
else
LIB_LOCATION = ONE_LOCATION + '/lib'
RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
GEMS_LOCATION = ONE_LOCATION + '/share/gems'
REMOTES_LOCATION = ONE_LOCATION + '/var/remotes'
ANSIBLE_LOCATION = ONE_LOCATION + '/share/oneprovision/ansible'
end
if File.directory?(GEMS_LOCATION)
$LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }
require 'rubygems'
Gem.use_paths(File.realpath(GEMS_LOCATION))
end
$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << RUBY_LIB_LOCATION + '/cli'
$LOAD_PATH << LIB_LOCATION + '/oneprovision/lib'
$LOAD_PATH << LIB_LOCATION + '/oneflow/lib'
require 'command_parser'
require 'one_helper'
require 'one_helper/onegroup_helper'
require 'one_helper/oneprovision_helper'
require 'one_helper/oneprovision_template_helper'
require 'oneprovision'
CommandParser::CmdParser.new(ARGV) do
usage '`oneprovision-template` <command> [<file>] [<args>] [<options>]'
version OpenNebulaHelper::ONE_VERSION
helper = OneProvisionTemplateHelper.new
before_proc do
helper.set_client(options)
end
########################################################################
# Global options
########################################################################
cmd_options = CommandParser::OPTIONS - [CommandParser::VERBOSE]
set :option, cmd_options + OpenNebulaHelper::CLIENT_OPTIONS
########################################################################
# Formatters for arguments
########################################################################
set :format, :templateid, OneProvisionTemplateHelper.to_id_desc do |arg|
helper.to_id(arg)
end
set :format,
:templateid_list,
OneProvisionTemplateHelper.list_to_id_desc do |arg|
helper.list_to_id(arg)
end
set :format, :groupid, OneGroupHelper.to_id_desc do |arg|
h = OneGroupHelper.new
h.set_client(options)
h.to_id(arg)
end
set :format, :userid, OpenNebulaHelper.rname_to_id_desc('USER') do |arg|
OpenNebulaHelper.rname_to_id(arg, 'USER')
end
########################################################################
# Commands
########################################################################
create_desc = <<-EOT.unindent
Allocate a new template. File must be written in YAML.
EOT
command :create, create_desc, :template do
rc = helper.create(args[0])
if OpenNebula.is_error?(rc)
STDERR.puts rc.message
exit(-1)
else
puts "ID: #{rc}"
0
end
end
###
delete_desc = <<-EOT.unindent
Delete a template
EOT
command :delete, delete_desc, [:range, :templateid_list] do
helper.perform_actions(args[0], options, 'deleted') do |template|
template.delete
end
end
###
list_desc = <<-EOT.unindent
List all avaliable templates
EOT
command :list,
list_desc,
:options => CLIHelper::OPTIONS + [OpenNebulaHelper::FORMAT] do
options[:decrypt] = true
helper.list_pool(options)
end
###
show_desc = <<-EOT.unindent
Show template details
EOT
command :show,
show_desc,
:templateid,
:options => OpenNebulaHelper::FORMAT do
helper.show_resource(args[0], options)
end
###
update_desc = <<-EOT.unindent
Update template information
EOT
command :update, update_desc, :templateid, [:file, nil] do
helper.perform_action(args[0], options, 'updated') do |obj|
helper.update(obj, args[1])
end
end
###
instantiate_desc = <<-EOT.unindent
Instantiate the template
EOT
command :instantiate,
instantiate_desc,
:templateid,
[:extra_file, nil],
:options => OneProvisionHelper::CREATE_OPTIONS do
helper.perform_action(args[0], options, 'instantiated') do |obj|
h = OneProvisionHelper.new
h.parse_options(options)
if options[:cleanup_timeout].nil?
timeout = 20
else
timeout = options[:cleanup_timeout]
end
# Get skip mode
if options.key? :skip_provision
skip = :all
elsif options.key? :skip_config
skip = :config
else
skip = :none
end
rc = obj.instantiate((options.key? :cleanup),
timeout,
skip,
options[:provider],
args[1])
if OpenNebula.is_error?(rc)
STDERR.puts rc.message
exit(-1)
else
puts "ID: #{rc}"
0
end
end
end
###
chgrp_desc = <<-EOT.unindent
Changes the Provision Template group
EOT
command :chgrp, chgrp_desc, [:range, :templateid_list], :groupid do
helper.perform_actions(args[0], options, 'Group changed') do |p|
p.chown(-1, args[1].to_i)
end
end
###
chown_desc = <<-EOT.unindent
Changes the Provision Template owner and group
EOT
command :chown,
chown_desc,
[:range, :templateid_list],
:userid,
[:groupid, nil] do
args[2].nil? ? gid = -1 : gid = args[2].to_i
helper.perform_actions(args[0], options, 'Owner/Group changed') do |p|
p.chown(args[1].to_i, gid)
end
end
###
chmod_desc = <<-EOT.unindent
Changes the Provision Template permissions
EOT
command :chmod, chmod_desc, [:range, :templateid_list], :octet do
helper.perform_actions(args[0], options, 'Permissions changed') do |p|
p.chmod_octet(args[1])
end
end
end

View File

@ -36,11 +36,11 @@ Document::Document( int id,
{
if (_template_contents)
{
obj_template = move(_template_contents);
obj_template = make_unique<DocumentTemplate>(*_template_contents);
}
else
{
obj_template = make_unique<Template>();
obj_template = make_unique<DocumentTemplate>();
}
set_umask(_umask);
@ -72,6 +72,9 @@ int Document::insert(SqlDB *db, string& error_str)
return -1;
}
// Encrypt all the secrets
encrypt();
// ------------------------------------------------------------------------
// Insert the Document
// ------------------------------------------------------------------------

View File

@ -0,0 +1,22 @@
/* -------------------------------------------------------------------------- */
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems */
/* */
/* 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. */
/* -------------------------------------------------------------------------- */
#include "DocumentTemplate.h"
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
std::map<std::string, std::set<std::string> > DocumentTemplate::encrypted;

View File

@ -22,7 +22,8 @@ lib_name='nebula_document'
# Sources to generate the library
source_files=[
'Document.cc'
'Document.cc',
'DocumentTemplate.cc'
]
# Build library

View File

@ -765,7 +765,11 @@ void Nebula::start(bool bootstrap_only)
dspool = new DatastorePool(logdb, inherit_ds_attrs, ds_encrypted_attrs);
/* ----- Document, Zone, VDC, VMTemplate, SG and Makerket Pools ----- */
docpool = new DocumentPool(logdb);
vector<const SingleAttribute *> doc_encrypted_attrs;
nebula_configuration->get("DOCUMENT_ENCRYPTED_ATTR", doc_encrypted_attrs);
docpool = new DocumentPool(logdb, doc_encrypted_attrs);
zonepool = new ZonePool(db_ptr, is_federation_slave());
vdcpool = new VdcPool(db_ptr, is_federation_slave());

View File

@ -86,8 +86,8 @@ module OpenNebula
#
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
def info()
rc = super(DOCUMENT_METHODS[:info], 'DOCUMENT')
def info(decrypt = false)
rc = super(DOCUMENT_METHODS[:info], 'DOCUMENT', decrypt)
if !OpenNebula.is_error?(rc) && self['TYPE'].to_i != document_type
return OpenNebula::Error.new("[DocumentInfo] Error getting document [#{@pe_id}].")

View File

@ -21,6 +21,15 @@ module OpenNebula
TEMPLATE_TAG = "BODY"
# Returns current template tag
def template_tag
if @tag
@tag
else
TEMPLATE_TAG
end
end
# Allocate a new Document containing the json inside the TEMPLATE
#
# @param [String] template_json json to be inserted in the TEMPLATE
@ -30,7 +39,9 @@ module OpenNebula
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
#
def allocate(template_json, name=nil)
def allocate(template_json, name = nil, tag = nil)
@tag = tag if tag
text = build_template_xml(template_json, name)
super(text)
@ -41,8 +52,8 @@ module OpenNebula
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
#
def info
rc = super
def info(decrypt = false)
rc = super(decrypt)
if OpenNebula.is_error?(rc)
return rc
end
@ -90,10 +101,10 @@ module OpenNebula
def to_json(pretty_generate=true)
hash = self.to_hash
body = hash['DOCUMENT']['TEMPLATE']["#{TEMPLATE_TAG}"]
body = hash['DOCUMENT']['TEMPLATE']["#{template_tag}"]
if body
body_hash = JSON.parse(body)
hash['DOCUMENT']['TEMPLATE']["#{TEMPLATE_TAG}"] = body_hash
hash['DOCUMENT']['TEMPLATE']["#{template_tag}"] = body_hash
end
if pretty_generate
@ -106,7 +117,7 @@ module OpenNebula
# Fill the @body hash with the values of the template
def load_body
body_str = self["TEMPLATE/#{TEMPLATE_TAG}"]
body_str = self["TEMPLATE/#{template_tag}"]
if body_str
begin
@ -134,9 +145,9 @@ module OpenNebula
text << "<NAME>#{name}</NAME>" if name
text << "<#{TEMPLATE_TAG}>"
text << "<#{template_tag}>"
text << "<![CDATA[#{template_json}]]>"
text << "</#{TEMPLATE_TAG}>"
text << "</#{template_tag}>"
text << "</TEMPLATE>"

View File

@ -0,0 +1,61 @@
:plans:
Type 2A5:
cpu: 1
memory: 96.0
c2.large.arm:
cpu: 1
memory: 128.0
c2.medium.x86:
cpu: 1
memory: 64.0
c3.medium.x86:
cpu: 1
memory: 64.0
c3.small.x86:
cpu: 1
memory: 32.0
g2.large.x86:
cpu: 2
memory: 192.0
m2.xlarge.x86:
cpu: 2
memory: 384.0
m3.large.x86:
cpu: 1
memory: 256.0
n2.xlarge.x86:
cpu: 2
memory: 384.0
s3.xlarge.x86:
cpu: 2
memory: 192.0
x2.xlarge.x86:
cpu: 2
memory: 384.0
x.large.arm:
cpu: 2
memory: 256.0
c1.large.arm:
cpu: 2
memory: 128.0
c1.large.arm.xda:
cpu: 2
memory: 128.0
c1.small.x86:
cpu: 1
memory: 32.0
c1.xlarge.x86:
cpu: 2
memory: 128.0
m1.xlarge.x86:
cpu: 2
memory: 256.0
s1.large.x86:
cpu: 1
memory: 64.0
t1.small.x86:
cpu: 1
memory: 8.0
x1.small.x86:
cpu: 1
memory: 32.0

View File

@ -0,0 +1,52 @@
#!/usr/bin/env ruby
require 'json'
require 'net/http'
unless ARGV[0]
STDERR.puts 'No API token provied'
STDERR.puts '`packet_plans.rb` <API_TOKEN>'
exit(-1)
end
uri = URI('https://api.packet.net/plans')
req = Net::HTTP::Get.new(uri)
req['Accept'] = 'application/json'
req['X-Auth-Token'] = ARGV[0]
res = Net::HTTP.new(uri.hostname, uri.port)
res.use_ssl = true
response = res.request(req)
unless response.is_a? Net::HTTPOK
STDERR.puts 'Error getting plans from Packet'
exit(-1)
end
plans = JSON.parse(response.body)['plans']
file = ":plans:\n"
plans.each do |plan|
specs = plan['specs']
specs['cpus'].nil? ? cpu = 0 : cpu = specs['cpus'][0]['count']
specs['memory'].nil? ? mem = 0 : mem = specs['memory']['total']
next if cpu == 0 || mem == 0
file << " #{plan['name']}:\n"
file << " cpu: #{cpu}\n"
file << " memory: #{mem[/\d+/]}.0\n"
end
puts file
puts
print 'Do you want to overwrite file packet_driver.conf? '
over = STDIN.readline.chop.downcase
exit if over != 'yes'
File.open('packet_driver.conf', 'w') {|f| f.write(file) }

View File

@ -14,298 +14,24 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'ansible'
require 'driver'
require 'provision'
require 'provision_pool'
require 'resources'
require 'utils'
require 'logger'
require 'singleton'
require 'provision_element'
require 'provision/oneprovision'
require 'provision_template/provision_template'
require 'provision_template/provision_template_pool'
require 'provider/provider'
require 'provider/provider_pool'
# OneProvision module
module OneProvision
# Singleton OneProvision Logger
class OneProvisionLogger
include Singleton
attr_reader :logger
# Class constructor
def initialize
@logger = Logger.new(STDERR)
end
# Gets the logger
#
# @param options [Key-Value Object] CLI options
def self.get_logger(options)
format = '%Y-%m-%d %H:%M:%S'
instance.logger.formatter = proc do |severity, datetime, _p, msg|
"#{datetime.strftime(format)} #{severity.ljust(5)} : #{msg}\n"
end
if options.key? :debug
instance.logger.level = Logger::DEBUG
elsif options.key? :verbose
instance.logger.level = Logger::INFO
else
instance.logger.level = Logger::UNKNOWN
end
end
# Shows a debug message
#
# @param msg [String] Message to show
def self.debug(msg)
instance.logger.debug(msg)
end
# Shows an error message
#
# @param msg [String] Message to show
def self.error(msg)
instance.logger.error(msg)
end
# Shows an info message
#
# @param msg [String] Message to show
def self.info(msg)
instance.logger.info(msg)
end
# Shows a warning message
#
# @param msg [String] Message to show
def self.warn(msg)
instance.logger.warn(msg)
end
end
# Singleton running mode
class Mode
include Singleton
attr_reader :run_mode
# Class constructor
def initialize
@run_mode = {}
end
# Gets running mode
#
# @param options [Key-Value Object] CLI options
def self.get_run_mode(options)
if options.key? :batch
instance.run_mode[:mode] = :batch
else
instance.run_mode[:mode] = RUN_MODE_DEFAULT
end
instance.run_mode[:fail_modes] = []
if options.key? :fail_modes
instance.run_mode[:fail_modes] << options[:fail_modes]
end
options.each do |key, _|
next unless key =~ /^fail_/
next if key =~ /sleep/ || key =~ /modes/
instance.run_mode[:fail_modes] << key.to_s.split('_')[1].to_sym
end
if instance.run_mode[:fail_modes].empty?
instance.run_mode[:fail_modes] << FAIL_CHOICE_DEFAULT
end
instance.run_mode[:fail_modes].flatten!
if options.key? :fail_sleep
instance.run_mode[:fail_sleep] = options[:fail_sleep]
end
if options[:fail_retry]
instance.run_mode[:max_retries] = options[:fail_retry].to_i
else
instance.run_mode[:max_retries] = MAX_RETRIES_DEFAULT
end
instance.run_mode[:cleanup] = CLEANUP_DEFAULT
end
# Gets cleanup value
#
# @return [Boolean] Cleanup
def self.cleanup
instance.run_mode[:cleanup]
end
# Gets fail choice value
#
# @return [Sym] Choice value
def self.fail_choice
instance.run_mode[:fail_modes][0]
end
# Gets time between fail modes
#
# @return [Integer] Time in seconds
def self.fail_sleep
instance.run_mode[:fail_sleep]
end
# Gets next fail choice value
#
# @return [Sym] Choice value
def self.next_fail_choice
instance.run_mode[:fail_modes].shift
end
# Gets max retries value
#
# @return [Integer] Maximun retries
def self.max_retries
instance.run_mode[:max_retries]
end
# Gets mode value
#
# @return [Sym] Mode value
def self.mode
instance.run_mode[:mode]
end
# Sets cleanup value
#
# @param value [Boolean] New cleanup value
def self.new_cleanup(value)
instance.run_mode[:cleanup] = value
end
end
# Singleton options
class Options
include Singleton
attr_reader :run_options
# Class constructor
def initialize
@run_options = {}
end
# Gets options
#
# @param options [Key-Value Object] CLI Options
def self.get_run_options(options)
if options.key? :ping_timeout
instance.run_options[:ping_timeout] = options[:ping_timeout]
else
instance.run_options[:ping_timeout] = PING_TIMEOUT_DEFAULT
end
if options.key? :ping_retries
instance.run_options[:ping_retries] = options[:ping_retries]
else
instance.run_options[:ping_retries] = PING_RETRIES_DEFAULT
end
if options.key? :threads
instance.run_options[:threads] = options[:threads]
else
instance.run_options[:threads] = THREADS_DEFAULT
end
end
# Gets ping retries value
#
# @return [Integer] Ping retries value
def self.ping_retries
instance.run_options[:ping_retries]
end
# Gets ping timeout value
#
# @return [Integer] Ping timeout value
def self.ping_timeout
instance.run_options[:ping_timeout]
end
# Gets number of threads
#
# @return [Integer] Number of threads
def self.threads
instance.run_options[:threads]
end
end
# Singleton virtual objects options
class ObjectOptions
include Singleton
attr_reader :obj_opts
# Class constructor
def initialize
@obj_opts = {}
end
# Gets options
#
# @param options [Key-Value Object] CLI Options
def self.get_obj_options(options)
if options.key? :wait_ready
instance.obj_opts[:wait_ready] = true
else
instance.obj_opts[:wait_ready] = false
end
if options.key? :wait_timeout
instance.obj_opts[:wait_timeout] = options[:wait_timeout]
else
instance.obj_opts[:wait_timeout] = WAIT_TIMEOUT_DEFAULT
end
end
# Get wait and timeout
#
# @param meta [Hash] Meta information from object
#
# @return [boolean, integer] [wait, timeout]
def self.get_wait(meta)
if meta
wait = meta['wait']
timeout = meta['wait_timeout']
end
case wait
when false
[wait, nil]
when true
if timeout
[wait, timeout]
else
[wait, instance.obj_opts[:wait_timeout]]
end
else
[instance.obj_opts[:wait_ready],
instance.obj_opts[:wait_timeout]]
end
end
end
PING_TIMEOUT_DEFAULT = 20
PING_RETRIES_DEFAULT = 10
MAX_RETRIES_DEFAULT = 3
RUN_MODE_DEFAULT = :interactive
FAIL_CHOICE_DEFAULT = :quit
DEFAULT_FAIL_MODES = [:cleanup, :retry, :skip, :quit]
CLEANUP_DEFAULT = false
THREADS_DEFAULT = 3
WAIT_TIMEOUT_DEFAULT = 60
end

View File

@ -0,0 +1,143 @@
# #
# 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 'base64'
module OneProvision
# Provider class as wrapper of DocumentJSON
class Provider < ProvisionElement
DOCUMENT_TYPE = 102
# These attributes can not be changed when updating the provider
UNMUTABLE_ATTRS = %w[provider registration_time]
# Allocates a new document
#
# @param template [Hash] Document information
def allocate(template)
rc = Provider.by_name(@client, template['name'])
return rc if OpenNebula.is_error?(rc)
if rc
return OpenNebula::Error.new("Provider #{template['name']} " \
'already exists')
end
rc = nil
begin
rc = to_json(template)
return rc if OpenNebula.is_error?(rc)
rescue StandardError => e
return OpenNebula::Error.new(e)
end
super(rc, template['name'], TEMPLATE_TAG)
end
# Delete provider
def delete
info(true)
# Check if the provider is being used in any provision
pool = ProvisionPool.new(@client)
rc = pool.info_all
return rc if OpenNebula.is_error?(rc)
pool.each do |p|
next unless p.body['provider'] == body['provider']
next if p.body['state'] == Provision::STATE['DONE']
return OpenNebula::Error.new(
'Provider can not be deleted, it is used by ' \
"provision with ID: #{p['ID']}"
)
end
pool = ProvisionTemplatePool.new(@client)
rc = pool.info_all
return rc if OpenNebula.is_error?(rc)
pool.each do |p|
next unless Integer(p.body['provider']) == Integer(self['ID'])
return OpenNebula::Error.new(
'Provider can not be deleted, it is used by ' \
"provision template with ID: #{p['ID']}"
)
end
super
end
# Gets connection information
def connection
conn = @body['connection']
conn['provider'] = self['NAME']
conn
end
# Gets provider object by name or ID
#
# @param client [Client] Client to make OpenNebula calls
# @param provider [String] Provider name or ID
#
# @return [Provider]
def self.by_name(client, provider)
if provider.to_s.match(/^[0123456789]+$/)
provider = Provider.new_with_id(Integer(provider), client)
rc = provider.info(true)
return rc if OpenNebula.is_error?(rc)
provider
else
pool = ProviderPool.new(client)
rc = pool.info_group
return rc if OpenNebula.is_error?(rc)
pool.find {|p| p['NAME'] == provider }
end
end
private
# Generates document JSON information
#
# @param template [Hash] Provision information
#
# @return [JSON] Document information in JSON format
def to_json(template)
document = {}
# Create document JSON
document['provider'] = template['name']
document['connection'] = template['connection']
document['registration_time'] = Time.now.to_i
document.to_json
end
end
end

View File

@ -0,0 +1,39 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# 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 'opennebula/document_pool_json'
module OneProvision
# ProviderPool class
class ProviderPool < DocumentPoolJSON
DOCUMENT_TYPE = 102
def initialize(client, user_id = -2)
super(client, user_id)
end
def factory(element_xml)
provider = OneProvision::Provider.new(element_xml, @client)
provider.info(true)
provider.load_body
provider
end
end
end

View File

@ -0,0 +1,311 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# 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 'provision/ansible'
require 'provision/driver'
require 'provision/provision'
require 'provision/provision_pool'
require 'provision/resources'
require 'provision/utils'
require 'logger'
require 'singleton'
module OneProvision
# Singleton OneProvision Logger
class OneProvisionLogger
include Singleton
attr_reader :logger
# Class constructor
def initialize
@logger = Logger.new(STDERR)
end
# Gets the logger
#
# @param options [Key-Value Object] CLI options
def self.get_logger(options)
format = '%Y-%m-%d %H:%M:%S'
instance.logger.formatter = proc do |severity, datetime, _p, msg|
"#{datetime.strftime(format)} #{severity.ljust(5)} : #{msg}\n"
end
if options.key? :debug
instance.logger.level = Logger::DEBUG
elsif options.key? :verbose
instance.logger.level = Logger::INFO
else
instance.logger.level = Logger::UNKNOWN
end
end
# Shows a debug message
#
# @param msg [String] Message to show
def self.debug(msg)
instance.logger.debug(msg)
end
# Shows an error message
#
# @param msg [String] Message to show
def self.error(msg)
instance.logger.error(msg)
end
# Shows an info message
#
# @param msg [String] Message to show
def self.info(msg)
instance.logger.info(msg)
end
# Shows a warning message
#
# @param msg [String] Message to show
def self.warn(msg)
instance.logger.warn(msg)
end
end
# Singleton running mode
class Mode
include Singleton
attr_reader :run_mode
# Class constructor
def initialize
@run_mode = {}
end
# Gets running mode
#
# @param options [Key-Value Object] CLI options
def self.get_run_mode(options)
if options.key? :batch
instance.run_mode[:mode] = :batch
else
instance.run_mode[:mode] = RUN_MODE_DEFAULT
end
instance.run_mode[:fail_modes] = []
if options.key? :fail_modes
instance.run_mode[:fail_modes] << options[:fail_modes]
end
options.each do |key, _|
next unless key =~ /^fail_/
next if key =~ /sleep/ || key =~ /modes/
instance.run_mode[:fail_modes] << key.to_s.split('_')[1].to_sym
end
if instance.run_mode[:fail_modes].empty?
instance.run_mode[:fail_modes] << FAIL_CHOICE_DEFAULT
end
instance.run_mode[:fail_modes].flatten!
if options.key? :fail_sleep
instance.run_mode[:fail_sleep] = options[:fail_sleep]
end
if options[:fail_retry]
instance.run_mode[:max_retries] = Integer(options[:fail_retry])
else
instance.run_mode[:max_retries] = MAX_RETRIES_DEFAULT
end
instance.run_mode[:cleanup] = CLEANUP_DEFAULT
end
# Gets cleanup value
#
# @return [Boolean] Cleanup
def self.cleanup
instance.run_mode[:cleanup]
end
# Gets fail choice value
#
# @return [Sym] Choice value
def self.fail_choice
instance.run_mode[:fail_modes][0]
end
# Gets time between fail modes
#
# @return [Integer] Time in seconds
def self.fail_sleep
instance.run_mode[:fail_sleep]
end
# Gets next fail choice value
#
# @return [Sym] Choice value
def self.next_fail_choice
instance.run_mode[:fail_modes].shift
end
# Gets max retries value
#
# @return [Integer] Maximun retries
def self.max_retries
instance.run_mode[:max_retries]
end
# Gets mode value
#
# @return [Sym] Mode value
def self.mode
instance.run_mode[:mode]
end
# Sets cleanup value
#
# @param value [Boolean] New cleanup value
def self.new_cleanup(value)
instance.run_mode[:cleanup] = value
end
end
# Singleton options
class Options
include Singleton
attr_reader :run_options
# Class constructor
def initialize
@run_options = {}
end
# Gets options
#
# @param options [Key-Value Object] CLI Options
def self.get_run_options(options)
if options.key? :ping_timeout
instance.run_options[:ping_timeout] = options[:ping_timeout]
else
instance.run_options[:ping_timeout] = PING_TIMEOUT_DEFAULT
end
if options.key? :ping_retries
instance.run_options[:ping_retries] = options[:ping_retries]
else
instance.run_options[:ping_retries] = PING_RETRIES_DEFAULT
end
if options.key? :threads
instance.run_options[:threads] = options[:threads]
else
instance.run_options[:threads] = THREADS_DEFAULT
end
end
# Gets ping retries value
#
# @return [Integer] Ping retries value
def self.ping_retries
instance.run_options[:ping_retries]
end
# Gets ping timeout value
#
# @return [Integer] Ping timeout value
def self.ping_timeout
instance.run_options[:ping_timeout]
end
# Gets number of threads
#
# @return [Integer] Number of threads
def self.threads
instance.run_options[:threads]
end
end
# Singleton virtual objects options
class ObjectOptions
include Singleton
attr_reader :obj_opts
# Class constructor
def initialize
@obj_opts = {}
end
# Gets options
#
# @param options [Key-Value Object] CLI Options
def self.get_obj_options(options)
if options.key? :wait_ready
instance.obj_opts[:wait_ready] = true
else
instance.obj_opts[:wait_ready] = false
end
if options.key? :wait_timeout
instance.obj_opts[:wait_timeout] = options[:wait_timeout]
else
instance.obj_opts[:wait_timeout] = WAIT_TIMEOUT_DEFAULT
end
end
# Get wait and timeout
#
# @param meta [Hash] Meta information from object
#
# @return [boolean, integer] [wait, timeout]
def self.get_wait(meta)
if meta
wait = meta['wait']
timeout = meta['wait_timeout']
end
case wait
when false
[wait, nil]
when true
if timeout
[wait, timeout]
else
[wait, instance.obj_opts[:wait_timeout]]
end
else
[instance.obj_opts[:wait_ready],
instance.obj_opts[:wait_timeout]]
end
end
end
end

View File

@ -58,16 +58,27 @@ module OneProvision
# Allocates a new document
#
# @param template [Hash] Document information
def allocate(template)
super(generate_document_json(template), template['name'])
# @param template [Hash] Document information
# @param provider [String] Provision provider
def allocate(template, provider)
rc = nil
begin
rc = to_json(template, provider)
return rc if OpenNebula.is_error?(rc)
rescue StandardError => e
return OpenNebula::Error.new(e)
end
super(rc, template['name'])
end
# Returns provision state
#
# @return [Intenger] Provision state
def state
@body['state'].to_i
Integer(@body['state'])
end
# Returns provision state in string format
@ -116,16 +127,58 @@ module OneProvision
# Deploys a new provision
#
# @param config [String] Path to the configuration file
# @param cleanup [Boolean] True to delete everything if something fails
# @param timeout [Integer] Timeout for deleting running VMs
# @param skip [Symbol] Skip provision, config or none phases
def deploy(config, cleanup, timeout, skip)
# @param config [String] Path to deployment file
# @param cleanup [Boolean] True to cleanup everything
# @param timeout [Integer] Timeout in seconds to connect to hosts
# @param skip [Symbol] Skip provision, configuration or anything
# @param provider [Provider] Provider to deploy the provision
#
# @return [Integer] Provision ID
def deploy(config, cleanup, timeout, skip, provider)
Ansible.check_ansible_version
begin
cfg = Utils.read_config(config)
if provider
# Respect user information, only add provider info if
# the user hasn't specified any
cfg['defaults'] = {} unless cfg['defaults']
# Get user UID to see if merge is possible or not
# only merge attributes if the provider is owned by user
info = Nokogiri::XML(@client.call('user.info', -1))
uid = info.xpath('/USER/ID').text
# Get provider connection information
conn = provider.connection
if uid == provider['UID']
cfg['defaults']['provision'] = conn.merge(
cfg['defaults']['provision']
)
OneProvisionLogger.debug('Merging provider connection')
else
cfg['defaults']['provision'] = conn
OneProvisionLogger.debug('Using provider connection')
end
end
# read provision file
cfg = Utils.create_config(Utils.read_config(config))
cfg = Utils.create_config(cfg)
# read provider information
if provider
provider = provider['NAME']
else
provider = Provision.read_provider(cfg)
end
unless provider
return OpenNebula::Error.new('No provider found')
end
# @name is used for ERB evaluation
@name = cfg['name']
@ -144,9 +197,9 @@ module OneProvision
create_infra_resources(cfg, cfg['cluster']['id'])
create_hosts(cfg, cfg['cluster']['id'])
allocate(cfg)
allocate(cfg, provider)
info
self.info
# @id is used for ERB evaluation
@id = self['ID']
@ -366,32 +419,43 @@ module OneProvision
binding
end
# Reads provider name from template
#
# @param template [Hash] Provision information
#
# @return [String] Provider name
def self.read_provider(template)
provider = nil
# Provider can be set in provision defaults or in hosts
# it's the same for all hosts, taking the first one is enough
if template['defaults'] && template['defaults']['provision']
provider = template['defaults']['provision']['provider']
end
if !provider && template['hosts'][0]['provision']
provider = template['hosts'][0]['provision']['provider']
end
provider
end
private
# Generates document JSON information
#
# @param template [Hash] Provision information
# @param template [Hash] Provision information
# @param provider [String] Provision provider
#
# @return [JSON] Document information in JSON format
def generate_document_json(template)
def to_json(template, provider)
document = {}
document['name'] = template['name']
document['description'] = template['description']
document['start_time'] = Time.now.to_i
document['state'] = STATE['DEPLOYING']
# Driver can be set in provision defaults or in hosts
# it's the same for all hosts, taking the first one is enough
if template['defaults'] && template['defaults']['provision']
driver = template['defaults']['provision']['driver']
end
if !driver && template['hosts'][0]['provision']
driver = template['hosts'][0]['provision']['driver']
end
document['provider'] = driver
document['provider'] = provider
# Fill provision objects information
document['provision'] = {}
@ -529,7 +593,7 @@ module OneProvision
host = Host.new
host = host.create(dfile.to_xml, cid, playbooks)
h['id'] = host['ID']
h['id'] = Integer(host['ID'])
h['name'] = host['NAME']
host.offline
@ -621,7 +685,7 @@ module OneProvision
Utils.exception(host.info(h['id']))
return true if host.one['HOST_SHARE/RUNNING_VMS'].to_i > 0
return true if Integer(host.one['HOST_SHARE/RUNNING_VMS']) > 0
end
false
@ -646,7 +710,7 @@ module OneProvision
next unless resource_objects['images']
resource_objects['images'].find do |value|
true if image.to_i == value['id'].to_i
true if Integer(image) == Integer(value['id'])
end
end
@ -668,7 +732,7 @@ module OneProvision
Utils.exception(host.info(h['id']))
next unless host.one['HOST_SHARE/RUNNING_VMS'].to_i > 0
next unless Integer(host.one['HOST_SHARE/RUNNING_VMS']) > 0
d_hosts << host.one
end

View File

@ -28,7 +28,7 @@ module OneProvision
end
def factory(element_xml)
provision = OpenNebula::Provision.new(element_xml, @client)
provision = OneProvision::Provision.new(element_xml, @client)
provision.load_body
provision
end

View File

@ -14,9 +14,9 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/resource'
require 'resources/physical'
require 'resources/virtual'
require 'provision/resources/resource'
require 'provision/resources/physical'
require 'provision/resources/virtual'
# Module OneProvision
module OneProvision

View File

@ -14,10 +14,10 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/physical/cluster'
require 'resources/physical/datastore'
require 'resources/physical/host'
require 'resources/physical/network'
require 'provision/resources/physical/cluster'
require 'provision/resources/physical/datastore'
require 'provision/resources/physical/host'
require 'provision/resources/physical/network'
# Module OneProvision
module OneProvision

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/resource'
require 'provision/resources/resource'
module OneProvision
@ -44,7 +44,7 @@ module OneProvision
rc = @one.info
Utils.exception(rc)
@one.id.to_i
Integer(@one.id)
end
# Info an specific object

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/physical/physical_resource'
require 'provision/resources/physical/physical_resource'
module OneProvision

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/resource'
require 'provision/resources/resource'
module OneProvision
@ -28,18 +28,18 @@ module OneProvision
# Class constructor
#
# @param driver [String] Host driver
def initialize(driver = nil)
# @param provider [String] Host provider
def initialize(provider = nil)
super()
@pool = OpenNebula::HostPool.new(@client)
@type = 'host'
@driver = driver
@pool = OpenNebula::HostPool.new(@client)
@type = 'host'
@provider = provider
end
# Checks if there are Running VMs on the HOST
def running_vms?
@one['HOST_SHARE/RUNNING_VMS'].to_i > 0
Integer(@one['HOST_SHARE/RUNNING_VMS']) > 0
end
# Establishes an SSH connection to the HOST
@ -130,7 +130,7 @@ module OneProvision
params = [resume_file.path, @one.name]
Driver.pm_driver_action(@driver, 'deploy', params, @one)
Driver.pm_driver_action(@provider, 'deploy', params, @one)
OneProvisionLogger.debug("Enabling OpenNebula host: #{@one.id}")
@ -153,7 +153,7 @@ module OneProvision
params = [deploy_id, name, 'SHUTDOWN_POWEROFF']
Driver.pm_driver_action(@driver, 'shutdown', params, @one)
Driver.pm_driver_action(@provider, 'shutdown', params, @one)
OneProvisionLogger.debug("Offlining OpenNebula host: #{@one.id}")
@ -193,7 +193,7 @@ module OneProvision
params = [deploy_id, name]
Driver.pm_driver_action(@driver, 'cancel', params, @one)
Driver.pm_driver_action(@provider, 'cancel', params, @one)
end
# delete ONE host
@ -214,8 +214,8 @@ module OneProvision
# Checks that the HOST is a baremetal HOST
def check
Utils.fail('Not a valid bare metal host') if @driver.nil?
Utils.fail('Not a valid bare metal host') if @driver.empty?
Utils.fail('Not a valid bare metal host') if @provider.nil?
Utils.fail('Not a valid bare metal host') if @provider.empty?
end
# Monitors the HOST
@ -234,7 +234,10 @@ module OneProvision
params = [deploy_id, name]
pm_ret = Driver.pm_driver_action(@driver, 'poll', params, @one)
pm_ret = Driver.pm_driver_action(@provider,
'poll',
params,
@one)
begin
poll = {}
@ -281,7 +284,7 @@ module OneProvision
@one.offline
OneProvisionLogger.info("#{message} host: #{@one.id}")
Driver.pm_driver_action(@driver, action, [deploy_id, name], @one)
Driver.pm_driver_action(@provider, action, [deploy_id, name], @one)
OneProvisionLogger.debug("Enabling OpenNebula host: #{@one.id}")

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/physical/physical_resource'
require 'provision/resources/physical/physical_resource'
module OneProvision

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/resource'
require 'provision/resources/resource'
module OneProvision
@ -40,7 +40,7 @@ module OneProvision
"#{@type} created with ID: #{@one.id}"
)
@one.id.to_i
Integer(@one.id)
end
end

View File

@ -176,7 +176,7 @@ module OneProvision
user ||= 0
group ||= 0
if user == @one['UID'].to_i || group == @one['GID'].to_i
if user == Integer(@one['UID']) || group == Integer(@one['GID'])
OneProvisionLogger.debug(
"Ownership for #{@type} #{@one.id} is already " \
"#{user}:#{group}"

View File

@ -14,11 +14,11 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/virtual/image'
require 'resources/virtual/marketplaceapp'
require 'resources/virtual/template'
require 'resources/virtual/flowtemplate'
require 'resources/virtual/vntemplate'
require 'provision/resources/virtual/image'
require 'provision/resources/virtual/marketplaceapp'
require 'provision/resources/virtual/template'
require 'provision/resources/virtual/flowtemplate'
require 'provision/resources/virtual/vntemplate'
# Module OneProvision
module OneProvision

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/virtual/virtual_resource'
require 'provision/resources/virtual/virtual_resource'
require 'models'
require 'json'

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/virtual/virtual_sync_resource'
require 'provision/resources/virtual/virtual_sync_resource'
module OneProvision
@ -49,7 +49,7 @@ module OneProvision
new_object
rc = @one.allocate(format_template(template),
template['ds_id'].to_i)
Integer(template['ds_id']))
Utils.exception(rc)
rc = @one.info
Utils.exception(rc)
@ -58,7 +58,7 @@ module OneProvision
"#{@type} created with ID: #{@one.id}"
)
return @one.id.to_i unless wait
return Integer(@one.id) unless wait
ready?(template)
end
@ -99,7 +99,7 @@ module OneProvision
raise OneProvisionLoopException
else
# if everything is ready, return the ID
@one.id.to_i
Integer(@one.id)
end
end
end

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/virtual/virtual_sync_resource'
require 'provision/resources/virtual/virtual_sync_resource'
module OneProvision
@ -55,11 +55,16 @@ module OneProvision
Utils.exception(rc)
app.extend(MarketPlaceAppExt)
url_args = "tag=#{template['tag']}" if template['tag']
rc = app.info
Utils.exception(rc)
rc = app.export(
:dsid => template['dsid'].to_i,
:dsid => Integer(template['dsid']),
:name => template['name'],
:vmtemplate_name => template['vmname']
:vmtemplate_name => template['vmname'],
:url_args => url_args
)
Utils.exception(rc[:image].first) if rc[:image]
Utils.exception(rc[:vmtemplate].first) if rc[:vmtemplate]

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/virtual/virtual_resource'
require 'provision/resources/virtual/virtual_resource'
module OneProvision

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/resource'
require 'provision/resources/resource'
module OneProvision
@ -39,7 +39,7 @@ module OneProvision
"#{@type} created with ID: #{@one.id}"
)
@one.id.to_i
Integer(@one.id)
end
end

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/resource'
require 'provision/resources/resource'
module OneProvision

View File

@ -14,7 +14,7 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'resources/virtual/virtual_resource'
require 'provision/resources/virtual/virtual_resource'
module OneProvision

View File

@ -351,7 +351,7 @@ module OneProvision
xml.VM_MAD host['vm_mad']
xml.PROVISION do
host['provision'].each do |key, value|
if key != 'driver'
if key != 'provider'
xml.send(key.upcase, value)
end
end

View File

@ -0,0 +1,64 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# 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 'opennebula/document_json'
# OneProvision module
module OneProvision
# Provision element
class ProvisionElement < DocumentJSON
TEMPLATE_TAG = 'PROVISION_BODY'
# Body tag
attr_reader :tag
# Body
attr_reader :body
# Class constructor
def initialize(xml, client)
super
@tag = TEMPLATE_TAG
end
# Replaces the template contents
#
# @param template_json [String] New template contents
#
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
def update(template_json)
template_json = JSON.parse(template_json)
if template_json.keys != @body.keys
return OpenNebula::Error.new('Keys can not be changed')
end
self.class::UNMUTABLE_ATTRS.each do |attr|
next if template_json[attr] == @body[attr]
return OpenNebula::Error.new("`#{attr}` can not be changed")
end
super(template_json.to_json)
end
end
end

View File

@ -0,0 +1,161 @@
# #
# 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 'base64'
require 'tempfile'
module OneProvision
# Provision Template class as wrapper of DocumentJSON
class ProvisionTemplate < ProvisionElement
DOCUMENT_TYPE = 104
# These attributes can not be changed when updating the template
UNMUTABLE_ATTRS = %w[name provider registration_time]
# Allocates a new document
#
# @param template [Hash] Document information
def allocate(template)
pool = ProvisionTemplatePool.new(@client)
rc = pool.info_group
return rc if OpenNebula.is_error?(rc)
rc = pool.find do |p|
p['NAME'] == template['name']
end
if rc
return OpenNebula::Error.new(
"Provision template #{template['name']} already exists"
)
end
rc = nil
begin
rc = to_json(template)
return rc if OpenNebula.is_error?(rc)
rescue StandardError => e
return OpenNebula::Error.new(e)
end
super(rc, template['name'], TEMPLATE_TAG)
end
# Instantiate the template
#
# @param cleanup [Boolean] True to cleanup everything
# @param timeout [Integer] Timeout in seconds to connect to hosts
# @param skip [Symbol] Skip provision, configuration or anything
# @param provider [String] Provider ID or name
# @param extra [String] Path to extra template information
#
# @param [Intenger] New provision ID
def instantiate(cleanup, timeout, skip, provider, extra)
rc = info(true)
return rc if OpenNebula.is_error?(rc)
# If provider option is provided, respect it and ignore provision
# template provider information
if provider
provider = Provider.by_name(@client, provider)
else
if @body['provider']
provider = Provider.new_with_id(@body['provider'], @client)
rc = provider.info(true)
return rc if OpenNebula.is_error?(rc)
@body['defaults']['provision'] = provider.connection
end
end
# Merge extra information supplied by the user with the current
# information stored in the template
if extra
begin
extra = YAML.load_file(extra)
@body = @body.merge(extra)
rescue StandardError => e
return OpenNebula::Error.new(e)
end
end
tempfile = Tempfile.new('provision-template')
tempfile << @body.to_yaml
tempfile.close
xml = OneProvision::Provision.build_xml
provision = OneProvision::Provision.new(xml, @client)
provision.deploy(tempfile.path, cleanup, timeout, skip, provider)
ensure
tempfile.unlink if tempfile
end
private
# Generates document JSON information
#
# @param template [Hash] Provision information
#
# @return [JSON] Document information in JSON format
def to_json(template)
document = {}
provider = Provision.read_provider(template)
provider = Provider.by_name(@client, provider)
return provider if OpenNebula.is_error?(provider)
return OpenNebula::Error.new('Provider not found') unless provider
# Remove provider information as it has been converted to ID
if template['defaults']['provision']['provider']
template['defaults']['provision'].delete('provider')
end
if template['hosts']
template['hosts'].each do |host|
next unless host['provision']['provider']
host['provision'].delete('provider')
end
end
# Create document JSON
document['name'] = template['name']
document['provider'] = provider['ID']
document['registration_time'] = Time.now.to_i
document['playbooks'] = template['playbook']
document['defaults'] = template['defaults']
(Provision::RESOURCES + Provision::FULL_CLUSTER).each do |resource|
next unless template[resource]
document[resource] = template[resource]
end
document['cluster'] = template['cluster'] if template['cluster']
document.to_json
end
end
end

View File

@ -0,0 +1,39 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# 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 'opennebula/document_pool_json'
module OneProvision
# Provision Template Pool class
class ProvisionTemplatePool < DocumentPoolJSON
DOCUMENT_TYPE = 104
def initialize(client, user_id = -2)
super(client, user_id)
end
def factory(element_xml)
template = OneProvision::ProvisionTemplate.new(element_xml, @client)
template.info(true)
template.load_body
template
end
end
end