mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-20 10:50:08 +03:00
parent
ce142ca471
commit
99cfdf7843
@ -1033,6 +1033,7 @@ RUBY_LIB_FILES="src/mad/ruby/ActionManager.rb \
|
||||
src/vmm_mad/remotes/equinix/equinix_driver.rb \
|
||||
src/vnm_mad/remotes/elastic/aws_vnm.rb \
|
||||
src/vnm_mad/remotes/elastic/equinix_vnm.rb \
|
||||
src/vnm_mad/remotes/elastic/equinix.rb \
|
||||
src/vnm_mad/remotes/elastic/vultr_vnm.rb"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
@ -9,7 +9,7 @@ plain:
|
||||
connection:
|
||||
token: 'Equinix token'
|
||||
project: 'Equinix project'
|
||||
facility: 'am6'
|
||||
metro: 'am'
|
||||
|
||||
inputs:
|
||||
- name: 'equinix_os'
|
@ -9,7 +9,7 @@ plain:
|
||||
connection:
|
||||
token: 'Equinix token'
|
||||
project: 'Equinix project'
|
||||
facility: 'ny5'
|
||||
metro: 'ny'
|
||||
|
||||
inputs:
|
||||
- name: 'equinix_os'
|
@ -9,7 +9,7 @@ plain:
|
||||
connection:
|
||||
token: 'Equinix token'
|
||||
project: 'Equinix project'
|
||||
facility: 'sv15'
|
||||
metro: 'sv'
|
||||
|
||||
inputs:
|
||||
- name: 'equinix_os'
|
@ -9,7 +9,7 @@ plain:
|
||||
connection:
|
||||
token: 'Equinix token'
|
||||
project: 'Equinix project'
|
||||
facility: 'ty11'
|
||||
metro: 'ty'
|
||||
|
||||
inputs:
|
||||
- name: 'equinix_os'
|
@ -1,5 +1,5 @@
|
||||
name: 'Equinix'
|
||||
image: 'EQUINIX'
|
||||
provision_type: 'metal'
|
||||
location_key: 'facility'
|
||||
color: '#364562'
|
||||
location_key: 'metro'
|
||||
color: '#364562'
|
||||
|
@ -38,7 +38,7 @@ const provision = {
|
||||
packet_project: {
|
||||
type: 'string',
|
||||
},
|
||||
facility: {
|
||||
metro: {
|
||||
type: 'string',
|
||||
},
|
||||
plan: {
|
||||
@ -66,12 +66,12 @@ const provision = {
|
||||
},
|
||||
im_mad: {
|
||||
type: 'string',
|
||||
enum: ['kvm', 'firecraker'],
|
||||
enum: ['kvm', 'firecracker'],
|
||||
required: true,
|
||||
},
|
||||
vm_mad: {
|
||||
type: 'string',
|
||||
enum: ['kvm', 'firecraker'],
|
||||
enum: ['kvm', 'firecracker'],
|
||||
required: true,
|
||||
},
|
||||
provision: {
|
||||
|
@ -102,7 +102,10 @@ $LOAD_PATH << EQUINIX_LOCATION
|
||||
$LOAD_PATH << RUBY_LIB_LOCATION
|
||||
$LOAD_PATH << LIB_LOCATION + '/oneprovision/lib'
|
||||
|
||||
require 'packet'
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
require 'json'
|
||||
require 'equinix'
|
||||
require 'base64'
|
||||
require 'nokogiri'
|
||||
require 'opennebula'
|
||||
@ -168,9 +171,9 @@ begin
|
||||
provider = provision.provider
|
||||
connect = provider.body['connection']
|
||||
|
||||
pk_token = connect['token']
|
||||
pk_project = connect['project']
|
||||
pk_facility = connect['facility']
|
||||
eq_token = connect['token']
|
||||
eq_project = connect['project']
|
||||
eq_metro = connect['metro']
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Connect to Equinix and allocate a new IP
|
||||
@ -184,31 +187,36 @@ begin
|
||||
cidr = IPAddr.new(private_cidr)
|
||||
mask = 0x0FFFFFFFF >> cidr.prefix
|
||||
|
||||
equinix = Packet::Client.new
|
||||
equinix.auth_token = pk_token
|
||||
ip_req = { 'type' => data.xpath('//AR/EQUINIX_IP_TYPE').text,
|
||||
'quantity' => data.xpath('//AR/SIZE').text.to_i,
|
||||
'metro' => eq_metro,
|
||||
'fail_on_approval_required' => true }
|
||||
|
||||
ip = Packet::Ip.new
|
||||
ip.project_id = pk_project
|
||||
ip.facility = pk_facility
|
||||
ip.type = data.xpath('//AR/EQUINIX_IP_TYPE').text
|
||||
ip.quantity = data.xpath('//AR/SIZE').text.to_i
|
||||
|
||||
if ip.quantity != 1
|
||||
if ip_req['quantity'] != 1
|
||||
STDERR.puts 'Only address ranges of size 1 are supported'
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
if ip.type.empty?
|
||||
ip.type = IP_TYPE[0]
|
||||
elsif !IP_TYPE.include?(ip.type)
|
||||
STDERR.puts "Type #{ip.type} not supported. " \
|
||||
if ip_req['type'].empty?
|
||||
ip_req['type'] = IP_TYPE[0]
|
||||
elsif !IP_TYPE.include?(ip_req['type'])
|
||||
STDERR.puts "Type #{ip_req['type']} not supported. " \
|
||||
"Must be: #{IP_TYPE.join(', ')}"
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
equinix.create_ip(ip)
|
||||
eq = Equinix.new(eq_token)
|
||||
|
||||
ipmd5 = Digest::MD5.hexdigest(ip.address.to_s).to_i(16) & mask
|
||||
resp = eq.api_call("/projects/#{eq_project}/ips", Net::HTTP::Post, ip_req)
|
||||
|
||||
unless resp.code == '201'
|
||||
STDERR.puts "Equinix API failure, HTTP #{resp.code}, #{resp.message}, #{resp.body}"
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
addr, id = JSON.parse(resp.body).fetch_values('address', 'id')
|
||||
|
||||
ipmd5 = Digest::MD5.hexdigest(addr).to_i(16) & mask
|
||||
eip = IPAddr.new(ipmd5, Socket::AF_INET)
|
||||
|
||||
ipvm = (eip & mask) | cidr
|
||||
@ -218,12 +226,12 @@ begin
|
||||
AR = [
|
||||
TYPE = "IP4",
|
||||
IP = "#{ipvm}",
|
||||
SIZE = "#{ip.quantity}",
|
||||
SIZE = "#{ip_req['quantity']}",
|
||||
IPAM_MAD = "equinix",
|
||||
GATEWAY = "#{ipgw}",
|
||||
EXTERNAL_IP = "#{ip.address}",
|
||||
EXTERNAL_IP = "#{addr}",
|
||||
NETWORK_MASK = "255.255.255.254",
|
||||
EQUINIX_IP_ID = "#{ip.id}",
|
||||
EQUINIX_IP_ID = "#{id}",
|
||||
PROVISION_ID = "#{provision_id}"
|
||||
]
|
||||
EOF
|
||||
|
@ -69,7 +69,10 @@ $LOAD_PATH << EQUINIX_LOCATION
|
||||
$LOAD_PATH << RUBY_LIB_LOCATION
|
||||
$LOAD_PATH << LIB_LOCATION + '/oneprovision/lib'
|
||||
|
||||
require 'packet'
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
require 'json'
|
||||
require 'equinix'
|
||||
require 'base64'
|
||||
require 'nokogiri'
|
||||
require 'opennebula'
|
||||
@ -99,13 +102,12 @@ begin
|
||||
|
||||
provider = provision.provider
|
||||
connect = provider.body['connection']
|
||||
pk_token = connect['token']
|
||||
|
||||
eq_token = connect['token']
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Connect to Equinix and delete the IP
|
||||
# --------------------------------------------------------------------------
|
||||
equinix = Packet::Client.new
|
||||
equinix.auth_token = pk_token
|
||||
|
||||
equinix_id = data.xpath('//AR/EQUINIX_IP_ID').text.to_s
|
||||
|
||||
@ -114,7 +116,13 @@ begin
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
equinix.delete_ip(equinix_id)
|
||||
eq = Equinix.new(eq_token)
|
||||
resp = eq.api_call("/ips/#{equinix_id}", Net::HTTP::Delete)
|
||||
|
||||
unless resp.code == '204'
|
||||
STDERR.puts "Equinix API failure, HTTP #{resp.code}, #{resp.message}, #{resp.body}"
|
||||
exit(-1)
|
||||
end
|
||||
rescue StandardError => e
|
||||
STDERR.puts e.to_s
|
||||
exit(-1)
|
||||
|
@ -26,12 +26,10 @@ module OneProvision
|
||||
|
||||
# OpenNebula - Terraform equivalence
|
||||
TYPES = {
|
||||
:datastore => 'packet_volume',
|
||||
:host => 'packet_device',
|
||||
:network => 'packet_reserved_ip_block'
|
||||
:host => 'equinix_metal_device'
|
||||
}
|
||||
|
||||
KEYS = ['project', 'token', 'facility']
|
||||
KEYS = ['project', 'token', 'metro']
|
||||
|
||||
# Class constructor
|
||||
#
|
||||
|
@ -1,6 +1,6 @@
|
||||
#resource "packet_volume" "device_<%= obj['ID'] %>" {
|
||||
# description = "<%= obj['ID'] %>_volume"
|
||||
# facility = "<%= provision['FACILITY'] %>"
|
||||
# metro = "<%= provision['metro'] %>"
|
||||
# project_id = "<%= provision['PROJECT'] %>"
|
||||
# plan = "<%= provision['PLAN'] %>"
|
||||
# size = "<%= obj['TOTAL_MB'] %>"
|
||||
|
@ -1,7 +1,7 @@
|
||||
resource "packet_device" "device_<%= obj['ID'] %>" {
|
||||
resource "equinix_metal_device" "device_<%= obj['ID'] %>" {
|
||||
hostname = "<%= provision['HOSTNAME'] %>"
|
||||
plan = "<%= provision['PLAN'] %>"
|
||||
facilities = ["<%= provision['FACILITY'] %>"]
|
||||
metro = "<%= provision['METRO'] %>"
|
||||
operating_system = "<%= provision['OS'] %>"
|
||||
project_id = "<%= provision['PROJECT']%>"
|
||||
billing_cycle = "hourly"
|
||||
@ -10,10 +10,10 @@ resource "packet_device" "device_<%= obj['ID'] %>" {
|
||||
}
|
||||
|
||||
output "device_<%= obj['ID'] %>_ip" {
|
||||
value = packet_device.device_<%= obj['ID'] %>.network[0].address
|
||||
value = equinix_metal_device.device_<%= obj['ID'] %>.network[0].address
|
||||
}
|
||||
|
||||
output "device_<%= obj['ID'] %>_id" {
|
||||
value = packet_device.device_<%= obj['ID'] %>.id
|
||||
value = equinix_metal_device.device_<%= obj['ID'] %>.id
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<%# resource "packet_reserved_ip_block" "device_<%= obj['ID'] %1>" { %>
|
||||
<%# project_id = "<%= provision['PROJECT'] %1>" %>
|
||||
<%# facility = "<%= provision['FACILITY'] %1>" %>
|
||||
<%# metro = "<%= provision['METRO'] %1>" %>
|
||||
<%# <% if obj['AR_POOL'] && obj['AR_POOL']['AR'] && obj['AR_POOL']['AR']['SIZE'] %1> %>
|
||||
<%# quantity = "<%= obj['AR_POOL']['AR']['SIZE'] %1>" %>
|
||||
<%# <% else %1> %>
|
||||
|
@ -1,13 +1,12 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
packet = {
|
||||
source = "packethost/packet"
|
||||
equinix = {
|
||||
source = "equinix/equinix"
|
||||
}
|
||||
}
|
||||
required_version = ">= 0.13"
|
||||
}
|
||||
|
||||
provider "packet" {
|
||||
auth_token = "<%= conn['TOKEN'] %>"
|
||||
provider "equinix" {
|
||||
auth_token = "<%= conn['TOKEN'] %>"
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ class AWSProvider
|
||||
|
||||
0
|
||||
rescue StandardError => e
|
||||
OpenNebula.log_error("Error assiging #{ip}:#{e.message}")
|
||||
OpenNebula.log_error("Error assigning #{ip}:#{e.message}")
|
||||
1
|
||||
end
|
||||
|
||||
@ -133,7 +133,7 @@ class AWSProvider
|
||||
:private_ip_addresses => [aws_ip.private_ip_address] }
|
||||
)
|
||||
rescue StandardError
|
||||
OpenNebula.log_error("Error unassiging #{ip}:#{e.message}")
|
||||
OpenNebula.log_error("Error unassigning #{ip}:#{e.message}")
|
||||
end
|
||||
|
||||
end
|
||||
|
42
src/vnm_mad/remotes/elastic/equinix.rb
Normal file
42
src/vnm_mad/remotes/elastic/equinix.rb
Normal file
@ -0,0 +1,42 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2002-2023, 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.equinix.com/metal/v1'
|
||||
|
||||
# Class covering Equinix client
|
||||
class Equinix
|
||||
|
||||
def initialize(token)
|
||||
@eq_token = token
|
||||
@eq_endpoint = ENDPOINT
|
||||
end
|
||||
|
||||
def api_call(path, type = Net::HTTP::Get, data = {})
|
||||
uri = URI.parse(@eq_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'] = @eq_token
|
||||
req.body = data.to_json unless data.empty?
|
||||
|
||||
return https.request(req)
|
||||
end
|
||||
|
||||
end
|
@ -51,46 +51,81 @@ end
|
||||
$LOAD_PATH << RUBY_LIB_LOCATION
|
||||
$LOAD_PATH << EQUINIX_LOCATION
|
||||
|
||||
require 'packet'
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
require 'json'
|
||||
require 'equinix'
|
||||
|
||||
# Class covering Equinix functionality for Elastic driver
|
||||
class EquinixProvider
|
||||
|
||||
def initialize(provider, host)
|
||||
connect = provider.body['connection']
|
||||
|
||||
@client = Packet::Client.new(connect['token'])
|
||||
@eq_token = provider.body['connection']['token']
|
||||
@deploy_id = host['TEMPLATE/PROVISION/DEPLOY_ID']
|
||||
end
|
||||
|
||||
def assign(_ip, external, _opts = {})
|
||||
@client.assign_cidr_device("#{external}/32", @deploy_id)
|
||||
0
|
||||
rescue Packet::Error => e
|
||||
# potential VM poweroff(itself) + resume
|
||||
if e.message == '{"errors"=>["Address has already been taken"]}'
|
||||
return 0
|
||||
ip_req = { :manageable => true,
|
||||
:address => external }
|
||||
|
||||
eq = Equinix.new(@eq_token)
|
||||
|
||||
resp = eq.api_call("/devices/#{@deploy_id}/ips",
|
||||
Net::HTTP::Post,
|
||||
ip_req)
|
||||
|
||||
# HTTP 422 is returned if IP already assigned to the device
|
||||
# e.g. VM poweroff from inside + onevm resume
|
||||
unless resp.code == '201' || resp.code == '422'
|
||||
STDERR.puts "Error assigning #{external}:#{resp.message}"
|
||||
return 1
|
||||
end
|
||||
|
||||
OpenNebula.log_error("Error assiging #{external}:#{e.message}")
|
||||
1
|
||||
return 0
|
||||
rescue StandardError => e
|
||||
OpenNebula.log_error("Error assiging #{external}:#{e.message}")
|
||||
OpenNebula.log_error("Error assigning #{external}:#{e.message}")
|
||||
1
|
||||
end
|
||||
|
||||
def unassign(_ip, external, _opts = {})
|
||||
dev = @client.get_device(@deploy_id)
|
||||
eq = Equinix.new(@eq_token)
|
||||
|
||||
ip = dev.ip_addresses.select do |i|
|
||||
i['address'] == external &&
|
||||
i['cidr'] == 32 &&
|
||||
i['address_family'] == 4
|
||||
# find assignment id for external in device
|
||||
resp = eq.api_call("/devices/#{@deploy_id}/ips")
|
||||
|
||||
unless resp.code == '200'
|
||||
STDERR.puts "Error unassigning #{external}:#{resp.message}"
|
||||
return 1
|
||||
end
|
||||
|
||||
@client.delete_ip(ip[0]['id'])
|
||||
assignment_id = nil
|
||||
JSON.parse(resp.body)['ip_addresses'].each do |ip|
|
||||
if ip['address'] == external
|
||||
assignment_id = ip['id']
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
unless assignment_id
|
||||
STDERR.puts "Error unassigning #{external}:Can not find " <<
|
||||
"{external} ip in device #{@deploy_id}"
|
||||
# although unexpected still harmless, consider OK
|
||||
return 0
|
||||
end
|
||||
|
||||
# unassign external ip
|
||||
resp = eq.api_call("/ips/#{assignment_id}", Net::HTTP::Delete)
|
||||
|
||||
unless resp.code == '204'
|
||||
STDERR.puts "Equinix API failure, HTTP #{resp.code}, " <<
|
||||
"#{resp.message}, #{resp.body}"
|
||||
return 1
|
||||
end
|
||||
|
||||
return 0
|
||||
rescue StandardError => e
|
||||
OpenNebula.log_error("Error assiging #{external}:#{e.message}")
|
||||
OpenNebula.log_error("Error unassigning #{external}:#{e.message}")
|
||||
1
|
||||
end
|
||||
|
||||
def activate(cmds, nic)
|
||||
|
@ -78,7 +78,7 @@ class VultrProvider
|
||||
if VultrError.error?(rc)
|
||||
return 0 if rc.message == 'IP is already attached to a server'
|
||||
|
||||
OpenNebula.log_error("Error assiging #{rc.message}")
|
||||
OpenNebula.log_error("Error assigning #{rc.message}")
|
||||
return 1
|
||||
end
|
||||
|
||||
@ -94,7 +94,7 @@ class VultrProvider
|
||||
rc = @client.detach_nic(@deploy_id, opts[:vultr_id])
|
||||
|
||||
if VultrError.error?(rc)
|
||||
OpenNebula.log_error("Error unassiging #{rc.message}")
|
||||
OpenNebula.log_error("Error unassigning #{rc.message}")
|
||||
return 1
|
||||
end
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user