1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-11 04:58:16 +03:00

F #6756: Scaleway drivers for OneProvision

This is the first version of a ScaleWay drivers for OneProvision. The
drivers include the following features:

- Full support for defining Scaleway zones and regions, predefined files
  for Paris-1, Amsterdam-1 and Warsaw-3.
- Edge cluster based on metal instances
- Flexible IP support for VMs running on Scaleway clusters
- Private networking based on VXLAN and FRR (bgp-evpn). Future versions
  of this driver will consider extending IPAM to use Scaleway private
  networks.
- Playbook includes an /etc/hosts update to support name resolution of
  scaleway instances (needed by libvirt for live migration)

This commit also updates firewall to allow libvirt ports.

co-authored-by: Victor Palma <vpalma@opennebula.io>
This commit is contained in:
Miguel E. Ruiz 2024-12-05 15:44:42 +01:00 committed by Ruben S. Montero
parent 0d41fe3937
commit 2af3757059
No known key found for this signature in database
GPG Key ID: A0CEA6FA880A1D87
34 changed files with 1275 additions and 1 deletions

View File

@ -594,7 +594,7 @@ MARKET_MAD = [
IPAM_MAD = [
EXECUTABLE = "one_ipam",
ARGUMENTS = "-t 1 -i dummy,aws,equinix,vultr"
ARGUMENTS = "-t 1 -i dummy,aws,equinix,vultr,scaleway"
]
#*******************************************************************************

View File

@ -14,6 +14,12 @@
- { protocol: 'tcp', port: 179 }
# TCP/8742 default VXLAN port on Linux (UDP/4789 default IANA)
- { protocol: 'udp', port: 8472 }
# Port 16509 is needed for connecting to the destination host by using TLS.
- { protocol: 'tcp', port: 16509 }
# Port 16514 is needed for connecting to the destination host by using TCP.
- { protocol: 'tcp', port: 16514 }
# Ports 49152-49215 are needed by QEMU for transferring the memory and disk migration data.
- { protocol: 'tcp', port: '49152:49215' }
- role: frr
# Use /16 for the internal management network address
frr_prefix_length: 16

View File

@ -13,6 +13,12 @@
- { protocol: 'tcp', port: 179 }
# TCP/8742 default VXLAN port on Linux (UDP/4789 default IANA)
- { protocol: 'udp', port: 8472 }
# Port 16509 is needed for connecting to the destination host by using TLS.
- { protocol: 'tcp', port: 16509 }
# Port 16514 is needed for connecting to the destination host by using TCP.
- { protocol: 'tcp', port: 16514 }
# Ports 49152-49215 are needed by QEMU for transferring the memory and disk migration data.
- { protocol: 'tcp', port: '49152:49215' }
- role: frr
#bond0_0 is attached to the project private network
frr_iface: 'bond0_0'

View File

@ -0,0 +1,17 @@
# Hosts
## Description
Sets the IP address of all hosts on the /etc/hosts file of each host that belongs to the cluster.
## Requirements
No special requirements.
## Variables
None
## Todo list
None

View File

@ -0,0 +1,5 @@
- name: Add IP address of all hosts to all hosts
ansible.builtin.lineinfile:
dest: /etc/hosts
line: "{{ hostvars[item].ansible_default_ipv4.address }} {{ hostvars[item].ansible_hostname }}"
with_items: "{{ groups.all }}"

View File

@ -0,0 +1,26 @@
---
- hosts: nodes
roles:
- ddc
- opennebula-repository
- opennebula-node-kvm
- opennebula-ssh
- hosts
- role: iptables
iptables_base_rules_strict: false
iptables_base_rules_interface: "{{ ansible_default_ipv4.interface }}"
iptables_base_rules_services:
- { protocol: 'tcp', port: 22 }
# TCP/179 bgpd (TODO: only needed on Route Refector(s))
- { protocol: 'tcp', port: 179 }
# TCP/8742 default VXLAN port on Linux (UDP/4789 default IANA)
- { protocol: 'udp', port: 8472 }
# Port 16509 is needed for connecting to the destination host by using TLS.
- { protocol: 'tcp', port: 16509 }
# Port 16514 is needed for connecting to the destination host by using TCP.
- { protocol: 'tcp', port: 16514 }
# Ports 49152-49215 are needed by QEMU for transferring the memory and disk migration data.
- { protocol: 'tcp', port: '49152:49215' }
- role: frr
# Use /16 for the internal management network address
frr_prefix_length: 16

View File

@ -0,0 +1,25 @@
name: 'scaleway-paris'
description: 'Provision cluster in Scaleway Paris'
provider: 'scaleway'
plain:
provision_type: 'metal'
connection:
access_key: 'Scaleway Access Key'
secret_key: 'Scaleway Secret Key'
project_id: 'Scaleway Project ID'
zone: 'fr-par-1'
region: 'fr-par'
inputs:
- name: 'scw_baremetal_os'
type: 'text'
default: 'Ubuntu'
description: 'Scaleway host operating system'
- name: 'scw_offer'
type: 'list'
default: 'EM-A115X-SSD'
options:
- 'EM-A115X-SSD'

View File

@ -0,0 +1,25 @@
name: 'scaleway-amsterdam'
description: 'Provision cluster in Scaleway Amsterdam'
provider: 'scaleway'
plain:
provision_type: 'metal'
connection:
access_key: 'Scaleway Access Key'
secret_key: 'Scaleway Secret Key'
project_id: 'Scaleway Project ID'
zone: 'nl-ams-1'
region: 'nl-ams'
inputs:
- name: 'scw_baremetal_os'
type: 'text'
default: 'Ubuntu'
description: 'Scaleway host operating system'
- name: 'scw_offer'
type: 'list'
default: 'EM-A115X-SSD'
options:
- 'EM-A115X-SSD'

View File

@ -0,0 +1,25 @@
name: 'scaleway-warsaw'
description: 'Provision cluster in Scaleway Warsaw'
provider: 'scaleway'
plain:
provision_type: 'metal'
connection:
access_key: 'Scaleway Access Key'
secret_key: 'Scaleway Secret Key'
project_id: 'Scaleway Project ID'
zone: 'pl-waw-3'
region: 'pl-waw'
inputs:
- name: 'scw_baremetal_os'
type: 'text'
default: 'Ubuntu'
description: 'Scaleway host operating system'
- name: 'scw_offer'
type: 'list'
default: 'EM-A115X-SSD'
options:
- 'EM-A115X-SSD'

View File

@ -0,0 +1,40 @@
---
# ---------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
# ---------------------------------------------------------------------------- #
#-------------------------------------------------------------------------------
# datastores: Defines the storage area for the cluster using the SSH replication
# drivers. It creates the following datastores, using Replica driver:
# 1. Image datastore, ${cluster_name}-image
# 2. System datastore, ${cluster_name}-system
# 3. File datastore, ${cluster_name}-files
#
# Configuration/Input attributes:
# - replica_host: The host that will hold the cluster replicas and snapshots.
#-------------------------------------------------------------------------------
datastores:
- name: "${provision}-image"
type: 'image_ds'
ds_mad: 'fs'
tm_mad: 'ssh'
safe_dirs: "/var/tmp /tmp"
- name: "${provision}-system"
type: 'system_ds'
tm_mad: 'ssh'
safe_dirs: "/var/tmp /tmp"
replica_host: "${updates.first_host}"

View File

@ -0,0 +1,23 @@
---
# ---------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
# ---------------------------------------------------------------------------- #
#-------------------------------------------------------------------------------
# defaults: Common configuration attributes for provision objects
#--------------------------------------------------------------------------------
defaults:
configuration: {}

View File

@ -0,0 +1,19 @@
---
# ---------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
# ---------------------------------------------------------------------------- #
image: 'OPENNEBULA-ONPREMISE'
provider: 'scaleway'

View File

@ -0,0 +1,54 @@
---
# ---------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
# ---------------------------------------------------------------------------- #
inputs:
- name: 'number_hosts'
type: text
description: 'Number of metal servers to create'
default: '1'
- name: 'dns'
type: text
description: 'Comma separated list of DNS servers for public network'
default: '1.1.1.1'
- name: 'number_public_ips'
type: text
description: 'Number of public IPs to get'
default: '1'
- name: 'scw_offer'
type: text
description: 'Scaleway offer (device type)'
default: 'EM-A115X-SSD'
options:
- 'EM-A115X-SSD'
- name: 'scw_baremetal_os'
type: text
description: 'Scaleway host operating system'
default: 'Ubuntu'
options:
- 'Ubuntu'
- name: 'one_hypervisor'
type: list
description: 'Virtualization technology for the cluster hosts'
default: 'kvm'
options:
- 'kvm'
- 'lxc'

View File

@ -0,0 +1,41 @@
---
# ---------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
# ---------------------------------------------------------------------------- #
networks:
- name: "${provision}-public"
vn_mad: 'elastic'
bridge: 'br0'
netrole: 'public'
dns: "${input.dns}"
provision:
count: "${input.number_public_ips}"
ar:
- provision_id: "${provision_id}"
size: '1'
scaleway_ip_type: 'ipv4'
ipam_mad: 'scaleway'
vntemplates:
- name: "${provision}-private"
vn_mad: 'vxlan'
phydev: "${updates.default_ipv4_nic}"
automatic_vlan_id: 'yes'
netrole: 'private'
vxlan_mode: 'evpn'
vxlan_tep: 'dev'
ip_link_conf: 'nolearning='
cluster_ids: "${cluster.0.id}"

View File

@ -0,0 +1,86 @@
---
# ---------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
# ---------------------------------------------------------------------------- #
#-------------------------------------------------------------------------------
# This is the canonical description file for a cluster build with 'Scaleway'
# resources using the KVM hypervisor.
# ------------------------------------------------------------------------------
name: 'scaleway-edge-cluster'
description: 'Scaleway edge cluster'
extends:
- common.d/defaults.yml
- common.d/resources.yml
- common.d/hosts.yml
- scaleway.d/datastores.yml
- scaleway.d/fireedge.yml
- scaleway.d/inputs.yml
- scaleway.d/networks.yml
#-------------------------------------------------------------------------------
# ansible
# ver_min: ansible core required minimal version (>=)
# ver_max: ansible core required maximal version (<)
# playbook: Ansible playbook used for hosts configuration.
#-------------------------------------------------------------------------------
ansible:
ver_min: 2.8
ver_max: 2.17
playbook:
- scaleway
#-------------------------------------------------------------------------------
# defaults: Common configuration attributes for provision objects
#--------------------------------------------------------------------------------
defaults:
provision:
provider_name: 'scaleway'
offer: "${input.scw_offer}"
os: "${input.scw_baremetal_os}"
connection:
remote_user: 'ubuntu'
#-------------------------------------------------------------------------------
# cluster: Parameters for the OpenNebula cluster. Applies to all the Hosts
#--------------------------------------------------------------------------------
# name: of the cluster
# description: Additional information
# reserved_cpu: In percentage. It will be subtracted from the TOTAL CPU
# reserved_memory: In percentage. It will be subtracted from the TOTAL MEM
#--------------------------------------------------------------------------------
cluster:
name: "${provision}"
description: 'Scaleway edge cluster'
reserved_cpu: '0'
reserved_mem: '0'
datastores:
- 1
- 2
provision:
cidr: '172.18.0.0/24'
#-------------------------------------------------------------------------------
# AWS provision parameters.
#-------------------------------------------------------------------------------
# This section is used by provision drivers. DO NOT MODIFY IT
#
# CIDR: Private IP block for the cluster. This value HAS TO MATCH that on
# cluster.
#-------------------------------------------------------------------------------
scw_configuration:
cidr: '172.18.0.0/24'

View File

@ -0,0 +1,6 @@
name: 'SCALEWAY'
image: 'SCALEWAY'
provision_type:
- 'metal'
location_key: 'region'
color: '#8D40EE'

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,21 @@
#!/bin/bash
# -------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
#--------------------------------------------------------------------------- #
STDIN=`cat -`
exit 0

View File

@ -0,0 +1,21 @@
#!/bin/bash
# -------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
#--------------------------------------------------------------------------- #
STDIN=`cat -`
exit 0

View File

@ -0,0 +1,111 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
#--------------------------------------------------------------------------- #
###############################################################################
# This script is used to get a free IP address (or set of IPs). The IP will be
# used by OpenNebula VMs and should not be allocated to any other host in the
# network.
#
# STDIN Input:
# - Base64 encoded XML with the AR description and the address request
#
# XML format
# <IPAM_DRIVER_ACTION_DATA>
# <AR>
# <DEPLOY_ID>Scaleway AR id</DEPLOY_ID>
# <SCALEWAY_ACCESS_KEY>access key</SCALEWAY_ACCESS_KEY>
# <SCALEWAY_SECRET_KEY>secret key</SCALEWAY_SECRET_KEY>
# <SCALEWAY_PROJECT_ID>Scaleway Project ID</SCALEWAY_PROJECT_ID>
# <SCALEWAY_ZONE>zone</SCALEWAY_ZONE>
# </AR>
# <ADDRESS>
# <IP>
# <SIZE> Number of IPs to allocate</SIZE>
# </IP>
# </ADDRESS>
# </IPAM_DRIVER_ACTION_DATA>
#
# This scrit MUST output the leased IP range, if the "size" IPs cannot be
# assgined the sript must return -1, otherwise it must exit 0. The answer to
# OpenNebula needs to include the ADDRESS spec:
#
# ADDRESS = [ IP = "10.0.0.2", SIZE=34 ]
#
################################################################################
ONE_LOCATION = ENV['ONE_LOCATION'] unless defined?(ONE_LOCATION)
if !ONE_LOCATION
RUBY_LIB_LOCATION ||= '/usr/lib/one/ruby'
GEMS_LOCATION ||= '/usr/share/one/gems'
else
RUBY_LIB_LOCATION ||= ONE_LOCATION + '/lib/ruby'
GEMS_LOCATION ||= ONE_LOCATION + '/share/gems'
end
# %%RUBYGEMS_SETUP_BEGIN%%
if File.directory?(GEMS_LOCATION)
real_gems_path = File.realpath(GEMS_LOCATION)
if !defined?(Gem) || Gem.path != [real_gems_path]
$LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }
# Suppress warnings from Rubygems
# https://github.com/OpenNebula/one/issues/5379
begin
verb = $VERBOSE
$VERBOSE = nil
require 'rubygems'
Gem.use_paths(real_gems_path)
ensure
$VERBOSE = verb
end
end
end
# %%RUBYGEMS_SETUP_END%%
$LOAD_PATH << RUBY_LIB_LOCATION
require 'base64'
require 'nokogiri'
begin
data = Nokogiri::XML(Base64.decode64(STDIN.read))
size = data.xpath('//ADDRESS/SIZE').text
ip = data.xpath('//AR/IP').text
if size.to_i != 1
STDERR.puts 'Only IP requests of size 1 are supported'
exit(-1)
end
if ip.empty?
STDERR.puts 'Empty IP address in request'
exit(-1)
end
puts <<-EOF
ADDRESS = [
IP = "#{ip}",
SIZE = "1"
]
EOF
rescue StandardError => e
STDERR.puts e.to_s
exit(-1)
end

View File

@ -0,0 +1,237 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
#--------------------------------------------------------------------------- #
###############################################################################
# This script is used to register a new IP network in the IPAM. The network may
# be selected by a pool of free networks or if an specific network is requested
# its availability maybe checked by the IPAM driver.
#
# The IPAM driver must return an OpenNebula AddressRange definition, potentially
# augmented with network specific variables to be used by VMs (e.g. GATEWAYS,
# MASK...)
#
# STDIN Input:
# - Base64 encoded XML with AR request
#
# XML format
# <IPAM_DRIVER_ACTION_DATA>
# <AR>
# <TYPE>Type of the Ip (ipv4/ipv6)</TYPE>
# <PROVISION_ID>ID</PROVISION_ID>
# </AR>
# </IPAM_DRIVER_ACTION_DATA>
#
# The response MUST include IPAM_MAD, TYPE, IP and SIZE attributes, example:
# - A basic network definition
# AR = [
# IPAM_MAD = "scaleway",
# TYPE = "IP4",
# IP = "10.0.0.1",
# SIZE = "255",
# DEPLOY_ID = "..",
# ]
#
# - A complete network definition. Custom attributes (free form, key-value)
# can be added, named cannot be repeated.
# AR = [
# IPAM_MAD = "scaleway",
# TYPE = "IP4",
# IP = "10.0.0.2",
# SIZE = "200",
# DEPLOY_ID = "..",
# NETWORK_ADDRESS = "10.0.0.0",
# NETWORK_MASK = "255.255.255.0",
# GATEWAY = "10.0.0.1",
# DNS = "10.0.0.1",
# IPAM_ATTR = "10.0.0.240",
# OTHER_IPAM_ATTR = ".mydoamin.com"
# ]
################################################################################
ONE_LOCATION = ENV['ONE_LOCATION'] unless defined?(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
# %%RUBYGEMS_SETUP_BEGIN%%
if File.directory?(GEMS_LOCATION)
real_gems_path = File.realpath(GEMS_LOCATION)
if !defined?(Gem) || Gem.path != [real_gems_path]
$LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }
# Suppress warnings from Rubygems
# https://github.com/OpenNebula/one/issues/5379
begin
verb = $VERBOSE
$VERBOSE = nil
require 'rubygems'
Gem.use_paths(real_gems_path)
ensure
$VERBOSE = verb
end
end
end
# %%RUBYGEMS_SETUP_END%%
$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << LIB_LOCATION + '/oneprovision/lib'
require 'net/http'
require 'uri'
require 'json'
require 'scaleway'
require 'base64'
require 'nokogiri'
require 'opennebula'
require 'oneprovision'
require 'ipaddr'
require 'digest'
IP_TYPE = ['ipv4', 'ipv6']
DEFAULT_PRIVATE_CIDR = '172.16.0.0/12'
# IP Address class
class IPAddr
# Add ^ operator to the IPAddr class
def ^(other)
clone.set(@addr ^ other.to_i)
end
# Add prefix method, to work with older ipadddr & ruby
def prefix
case @family
when Socket::AF_INET
n = IN4MASK ^ @mask_addr
i = 32
when Socket::AF_INET6
n = IN6MASK ^ @mask_addr
i = 128
else
raise AddressFamilyError, 'unsupported address family'
end
while n>0
n >>= 1
i -= 1
end
i
end
end
begin
data = Nokogiri::XML(Base64.decode64(STDIN.read))
# --------------------------------------------------------------------------
# Get connection details for the provider
# --------------------------------------------------------------------------
provision_id = data.xpath('//AR/PROVISION_ID').text
if provision_id.empty?
STDERR.puts 'Missing provision id in address range'
exit(-1)
end
one = OpenNebula::Client.new
provision = OneProvision::Provision.new_with_id(provision_id, one)
rc = provision.info
if OpenNebula.is_error?(rc)
STDERR.puts rc.message
exit(-1)
end
provider = provision.provider
connect = provider.body['connection']
sw_secret_key = connect['secret_key']
sw_project_id = connect['project_id']
sw_zone = connect['zone']
# --------------------------------------------------------------------------
# Connect to Scaleway and allocate a new Flexible IP
# --------------------------------------------------------------------------
private_cidr = data.xpath('//AR/PRIVATE_CIDR').text
if private_cidr.empty?
private_cidr = DEFAULT_PRIVATE_CIDR
end
cidr = IPAddr.new(private_cidr)
mask = 0x0FFFFFFFF >> cidr.prefix
sw_ip_type = data.xpath('//AR/SCALEWAY_IP_TYPE').text
if sw_ip_type.empty?
sw_ip_type = IP_TYPE[0]
elsif !IP_TYPE.include?(sw_ip_type)
STDERR.puts "Type #{sw_ip_type} not supported. " \
"Must be: #{IP_TYPE.join(', ')}"
exit(-1)
end
fip_req = { :description => '',
:project_id => sw_project_id,
:is_ipv6 => sw_ip_type == 'ipv6',
:server_id => nil }
sw = Scaleway.new(sw_secret_key)
resp = sw.api_call("/flexible-ip/v1alpha1/zones/#{sw_zone}/fips",
Net::HTTP::Post,
fip_req)
unless resp.code == '200'
STDERR.puts "Scaleway API failure, HTTP #{resp.code}, #{resp.message}, #{resp.body}"
exit(-1)
end
addr, id = JSON.parse(resp.body).fetch_values('ip_address', 'id')
ipmd5 = Digest::MD5.hexdigest(addr).to_i(16) & mask
eip = IPAddr.new(ipmd5, Socket::AF_INET)
ipvm = (eip & mask) | cidr
ipgw = ipvm ^ 1
puts <<-EOF
AR = [
TYPE = "IP4",
IP = "#{ipvm}",
SIZE = "1",
IPAM_MAD = "scaleway",
GATEWAY = "#{ipgw}",
EXTERNAL_IP = "#{addr}",
NETWORK_MASK = "255.255.255.254",
SCALEWAY_IP_ID = "#{id}",
PROVISION_ID = "#{provision_id}"
]
EOF
rescue StandardError => e
STDERR.puts e.to_s
exit(-1)
end

View File

@ -0,0 +1,124 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
#--------------------------------------------------------------------------- #
###############################################################################
# This script is used to unregister a new IP network in the IPAM.
#
# STDIN Input:
# - Base64 encoded XML with AR request
#
################################################################################
ONE_LOCATION = ENV['ONE_LOCATION'] unless defined?(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
# %%RUBYGEMS_SETUP_BEGIN%%
if File.directory?(GEMS_LOCATION)
real_gems_path = File.realpath(GEMS_LOCATION)
if !defined?(Gem) || Gem.path != [real_gems_path]
$LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }
# Suppress warnings from Rubygems
# https://github.com/OpenNebula/one/issues/5379
begin
verb = $VERBOSE
$VERBOSE = nil
require 'rubygems'
Gem.use_paths(real_gems_path)
ensure
$VERBOSE = verb
end
end
end
# %%RUBYGEMS_SETUP_END%%
$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << LIB_LOCATION + '/oneprovision/lib'
require 'net/http'
require 'uri'
require 'json'
require 'scaleway'
require 'base64'
require 'nokogiri'
require 'opennebula'
require 'oneprovision'
require 'ipaddr'
require 'digest'
begin
data = Nokogiri::XML(Base64.decode64(STDIN.read))
# --------------------------------------------------------------------------
# Get connection details for the provider
# --------------------------------------------------------------------------
provision_id = data.xpath('//AR/PROVISION_ID').text
if provision_id.empty?
STDERR.puts 'Missing provision id in address range'
exit(-1)
end
one = OpenNebula::Client.new
provision = OneProvision::Provision.new_with_id(provision_id, one)
rc = provision.info
if OpenNebula.is_error?(rc)
STDERR.puts rc.message
exit(-1)
end
provider = provision.provider
connect = provider.body['connection']
sw_token = connect['token']
sw_zone = provider.body['zone']
# --------------------------------------------------------------------------
# Connect to Scaleway and delete the Flexible IP
# --------------------------------------------------------------------------
scaleway_id = data.xpath('//AR/SCALEWAY_IP_ID').text.to_s
if scaleway_id.empty?
STDERR.puts 'Missing Scaleway range ID'
exit(-1)
end
sw = Scaleway.new(sw_token)
resp = sw.api_call("/flexible-ip/v1alpha1/zones/#{sw_zone}/fips/#{scaleway_id}",
Net::HTTP::Delete)
unless resp.code == '204'
STDERR.puts "Scaleway API failure, HTTP #{resp.code}, #{resp.message}, #{resp.body}"
exit(-1)
end
exit(0)
rescue StandardError => e
STDERR.puts e.to_s
exit(-1)
end

View File

@ -0,0 +1,69 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2024, 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. #
#--------------------------------------------------------------------------- #
#--------------------------------------------------------------------------- #
# This file contains an example of a provider class. You just need to:
#
# Copy this file to <<PROVIDER>>.rb
# Replace variables between << >> by the correct value
# Remove all the unneeded comments
#
# You can check this documentation TODO: add link for more information
#--------------------------------------------------------------------------- #
require 'terraform/terraform'
# Module OneProvision
module OneProvision
# <<PROVIDER NAME>> Terraform Provider
class Scaleway < Terraform
NAME = Terraform.append_provider(__FILE__, name)
# OpenNebula - Terraform equivalence
TYPES = {
:host => 'scaleway_baremetal_os'
}
KEYS = ['access_key', 'secret_key', 'project_id']
# Class constructor
#
# @param provider [Provider]
# @param state [String] Terraform state in base64
# @param conf [String] Terraform config state in base64
def initialize(provider, state, conf)
# If credentials are stored into a file, set this variable to true
# If not, leave it as it is
@file_credentials = false
super
end
# Get user data to add into the VM
#
# @param ssh_key [String] SSH keys to add
def user_data(ssh_key)
user_data = []
ssh_key.split("\n").each {|key| user_data << key.to_s }
user_data
end
end
end

View File

@ -0,0 +1,31 @@
resource "scaleway_vpc" "device_<%= obj['ID'] %>" {
name = "<%= obj['NAME'] %>_vpc"
enable_routing = true
region = "<%= provision['REGION'] %>"
}
resource "scaleway_vpc_private_network" "device_<%= obj['ID'] %>" {
vpc_id = scaleway_vpc.device_<%= obj['ID'] %>.id
name = "<%= obj['NAME'] %>_subnet"
ipv4_subnet {
subnet = "<%= provision['CIDR'] ? provision['CIDR'] : '172.18.0.0/24'%>"
}
zone = "<%= provision['ZONE'] %>"
}
resource "scaleway_vpc_public_gateway" "device_<%= obj['ID'] %>" {
name = "<%= obj['NAME'] %>_gateway"
type = "VPC-GW-S"
bastion_enabled = true
bastion_port = 61000
zone = "<%= provision['ZONE'] %>"
}
resource scaleway_vpc_gateway_network "device_<%= obj['ID'] %>" {
gateway_id = scaleway_vpc_public_gateway.device_<%= obj['ID'] %>.id
private_network_id = scaleway_vpc_private_network.device_<%= obj['ID'] %>.id
ipam_config {
push_default_route = true
}
zone = "<%= provision['ZONE'] %>"
}

View File

@ -0,0 +1,38 @@
<% obj['user_data'].each_with_index do |key, index| %>
resource "scaleway_iam_ssh_key" "ssh_key_<%= obj['ID'] %>_<%= index %>" {
name = "ssh_key_<%= index %>"
public_key = "<%= key %>"
}
<% end %>
data "scaleway_baremetal_os" "device_<%= obj['ID'] %>" {
name = "Ubuntu"
version = "22.04 LTS (Jammy Jellyfish)"
zone = "<%= provision['ZONE'] %>"
}
data "scaleway_baremetal_offer" "device_<%= obj['ID'] %>" {
name = "<%= provision['OFFER'] %>"
zone = "<%= provision['ZONE'] %>"
}
resource "scaleway_baremetal_server" "device_<%= obj['ID'] %>" {
name = "<%= provision['HOSTNAME'] %>"
offer = data.scaleway_baremetal_offer.device_<%= obj['ID'] %>.offer_id
os = data.scaleway_baremetal_os.device_<%= obj['ID'] %>.os_id
zone = "<%= provision['ZONE'] %>"
ssh_key_ids = [
<% obj['user_data'].each_with_index do |_, index| %>
scaleway_iam_ssh_key.ssh_key_<%= obj['ID'] %>_<%= index %>.id<%= "," unless index == obj['user_data'].size - 1 %>
<% end %>
]
}
output "device_<%= obj['ID'] %>_id" {
value = scaleway_baremetal_server.device_<%= obj['ID'] %>.id
}
output "device_<%= obj['ID'] %>_ip" {
value = scaleway_baremetal_server.device_<%= obj['ID'] %>.ipv4[0].address
}

View File

@ -0,0 +1,16 @@
terraform {
required_providers {
scaleway = {
source = "scaleway/scaleway"
}
}
required_version = ">= 0.13"
}
provider "scaleway" {
access_key = "<%= conn['ACCESS_KEY'] %>"
secret_key = "<%= conn['SECRET_KEY'] %>"
project_id = "<%= conn['PROJECT_ID'] %>"
zone = "<%= conn['ZONE'] %>"
region = "<%= conn['REGION'] %>"
}

View File

@ -193,6 +193,9 @@ class ElasticDriver < VNMMAD::VNMDriver
when 'vultr_virtual', 'vultr_metal'
require 'vultr_vnm'
VultrProvider.new(provider, host)
when 'scaleway'
require 'scaleway_vnm'
ScalewayProvider.new(provider, host)
else
nil
end

View File

@ -0,0 +1,42 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2024, 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 'net/http'
require 'uri'
require 'json'
ENDPOINT = 'https://api.scaleway.com'
# Class covering Scaleway client
class Scaleway
def initialize(token)
@sw_token = token
@sw_endpoint = ENDPOINT
end
def api_call(path, type = Net::HTTP::Get, data = {})
uri = URI.parse(@sw_endpoint + path)
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
req = type.new(uri.path)
req['Content-Type'] = 'application/json'
req['X-Auth-Token'] = @sw_token
req.body = data.to_json unless data.empty?
return https.request(req)
end
end

View File

@ -0,0 +1,157 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2024, 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'] unless defined?(ONE_LOCATION)
if !ONE_LOCATION
LIB_LOCATION ||= '/usr/lib/one'
RUBY_LIB_LOCATION ||= '/usr/lib/one/ruby'
GEMS_LOCATION ||= '/usr/share/one/gems'
SCALEWAY_LOCATION ||= '/usr/lib/one/ruby/vendors/packethost/lib'
else
LIB_LOCATION ||= ONE_LOCATION + '/lib'
RUBY_LIB_LOCATION ||= ONE_LOCATION + '/lib/ruby'
GEMS_LOCATION ||= ONE_LOCATION + '/share/gems'
SCALEWAY_LOCATION ||= ONE_LOCATION + '/lib/ruby/vendors/packethost/lib'
end
# %%RUBYGEMS_SETUP_BEGIN%%
if File.directory?(GEMS_LOCATION)
real_gems_path = File.realpath(GEMS_LOCATION)
if !defined?(Gem) || Gem.path != [real_gems_path]
$LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }
# Suppress warnings from Rubygems
# https://github.com/OpenNebula/one/issues/5379
begin
verb = $VERBOSE
$VERBOSE = nil
require 'rubygems'
Gem.use_paths(real_gems_path)
ensure
$VERBOSE = verb
end
end
end
# %%RUBYGEMS_SETUP_END%%
$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << SCALEWAY_LOCATION
require 'net/http'
require 'uri'
require 'json'
require 'scaleway'
# Class covering Scaleway functionality for Elastic driver
class ScalewayProvider
def initialize(provider, host)
connection = provider.body['connection']
@secret_key = connection['secret_key']
@deploy_id = host['TEMPLATE/PROVISION/DEPLOY_ID'].split('/')[1]
@zone = connection['zone']
@sw = Scaleway.new(@secret_key)
end
def assign(_ip, external, _opts = {})
# First of all, we need the fip_id of the external IP
flexible_ip = sw_flexible_ip(external)
unless flexible_ip
STDERR.puts "Error assigning #{external}: IP not found"
return 1
end
if flexible_ip['status'] == 'attached'
STDERR.puts "Error assigning #{external}: IP already attached"
return 1
end
fip_req = { :fips_ids => [flexible_ip['id']],
:server_id => @deploy_id }
resp = @sw.api_call("/flexible-ip/v1alpha1/zones/#{@zone}/fips/attach",
Net::HTTP::Post,
fip_req)
# HTTP 409 is returned if IP is being assigned to the device
unless resp.code == '200' || resp.code == '409'
STDERR.puts "Error assigning #{external}:#{resp.message}"
return 1
end
return 0
rescue StandardError => e
OpenNebula.log_error("Error assigning #{external}:#{e.message}")
1
end
def unassign(_ip, external, _opts = {})
# First of all, we need the fip_id of the external IP
flexible_ip = sw_flexible_ip(external)
unless flexible_ip
STDERR.puts "Error unassigning #{external}:Can not find " <<
"#{external} ip in device #{@deploy_id}"
# although unexpected still harmless, consider OK
return 0
end
fip_req = { :fips_ids => [flexible_ip['id']] }
resp = @sw.api_call("/flexible-ip/v1alpha1/zones/#{@zone}/fips/detach",
Net::HTTP::Post,
fip_req)
# HTTP 409 is returned if IP is being unassigned to the device
unless resp.code == '200' || resp.code == '409'
STDERR.puts "Error unassigning #{external}:#{resp.message}"
return 1
end
return 0
rescue StandardError => e
OpenNebula.log_error("Error unassigning #{external}:#{e.message}")
1
end
def activate(cmds, nic)
cmds.add :iptables,
"-t nat -A POSTROUTING -s #{nic[:ip]} -j SNAT " \
"--to-source #{nic[:external_ip]}"
cmds.add :iptables,
"-t nat -A PREROUTING -d #{nic[:external_ip]} -j DNAT " \
"--to-destination #{nic[:ip]}"
end
def deactivate(cmds, nic)
cmds.add :iptables,
"-t nat -D POSTROUTING -s #{nic[:ip]} -j SNAT " \
"--to-source #{nic[:external_ip]}"
cmds.add :iptables,
"-t nat -D PREROUTING -d #{nic[:external_ip]} -j DNAT " \
"--to-destination #{nic[:ip]}"
end
private
def sw_flexible_ip(external)
resp_fips = @sw.api_call("/flexible-ip/v1alpha1/zones/#{@zone}/fips")
fips = JSON.parse(resp_fips.body)['flexible_ips']
fips.find {|fip| fip['ip_address'] == external }
end
end