2015-08-03 15:09:04 +03:00
require " chef/json_compat "
2018-04-04 22:44:39 +03:00
require " providers/abstract_provider_connector "
require " providers/openstack/openstack_provider_account "
require_relative " openstack_accounts_factory "
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
module Provider
# Provider for 'openstack'
2018-04-04 22:44:39 +03:00
class OpenstackConnector < AbstractProviderConnector
2014-06-18 15:11:47 +04:00
PROVIDER = " openstack "
def initialize config
self . connection_options = {
:provider = > PROVIDER ,
:openstack_username = > config [ :openstack_username ] ,
:openstack_api_key = > config [ :openstack_api_key ] ,
:openstack_auth_url = > config [ :openstack_auth_url ] ,
:openstack_tenant = > config [ :openstack_tenant ]
}
2014-11-20 15:08:42 +03:00
self . run_list = config [ :openstack_integration_run_list ] || [ ]
2014-06-18 15:11:47 +04:00
end
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
# Returns 'true' if all parameters defined
def configured?
o = self . connection_options
super and ! ( empty_param? ( o [ :openstack_username ] ) or empty_param? ( o [ :openstack_api_key ] ) or empty_param? ( o [ :openstack_auth_url ] ) or empty_param? ( o [ :openstack_tenant ] ) )
end
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
def name
PROVIDER
end
2014-05-08 15:34:26 +04:00
2018-04-04 22:44:39 +03:00
def security_groups filters = { }
2014-06-18 15:11:47 +04:00
convert_groups ( compute . list_security_groups . body [ " security_groups " ] )
end
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
def flavors
self . compute . list_flavors_detail . body [ " flavors " ] . map do | f |
{
" id " = > f [ " name " ] ,
" v_cpus " = > f [ " vcpus " ] ,
" ram " = > f [ " ram " ] ,
" disk " = > f [ " disk " ]
}
2014-05-08 15:34:26 +04:00
end
2014-06-18 15:11:47 +04:00
end
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
def images filters
self . compute . list_images_detail . body [ " images " ] . select { | i | filters . include? ( i [ " id " ] ) and i [ " status " ] == " ACTIVE " } . map do | i |
{
" id " = > i [ " id " ] ,
" name " = > i [ " name " ] ,
" status " = > i [ " status " ]
}
2014-05-08 15:34:26 +04:00
end
2014-06-18 15:11:47 +04:00
end
2014-05-08 15:34:26 +04:00
2015-11-17 12:14:02 +03:00
def networks_detail filters = { }
net = self . network ( filters )
2014-06-18 15:11:47 +04:00
subnets = net . list_subnets . body [ " subnets " ] . select { | s | net . current_tenant [ " id " ] == s [ " tenant_id " ] }
net . list_networks . body [ " networks " ] . select { | n | n [ " router:external " ] == false and n [ " status " ] == " ACTIVE " and net . current_tenant [ " id " ] == n [ " tenant_id " ] } . map { | n |
sn = subnets . detect { | s | n [ " subnets " ] [ 0 ] == s [ " id " ] }
{
" cidr " = > sn [ " cidr " ] ,
" name " = > n [ " name " ] ,
" id " = > n [ " id " ]
2014-05-08 15:34:26 +04:00
}
2014-06-18 15:11:47 +04:00
}
end
2014-05-08 15:34:26 +04:00
2015-11-17 12:14:02 +03:00
def networks filters = { }
2014-06-18 15:11:47 +04:00
net = self . network
net . list_networks . body [ " networks " ] . select { | n | n [ " router:external " ] == false and n [ " status " ] == " ACTIVE " and net . current_tenant [ " id " ] == n [ " tenant_id " ] } . map { | n |
{
" name " = > n [ " name " ] ,
" id " = > n [ " id " ]
2014-05-08 15:34:26 +04:00
}
2014-06-18 15:11:47 +04:00
}
end
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
def servers
list = self . compute . list_servers_detail . body [ " servers " ]
list . map do | s |
o = { " state " = > s [ " status " ] , " name " = > s [ " name " ] , " image " = > s [ " image " ] [ " id " ] , " flavor " = > s [ " flavor " ] [ " name " ] , " keypair " = > s [ " key_name " ] , " instance_id " = > s [ " id " ] }
s [ " addresses " ] . each_value do | a |
a . each do | addr |
o [ " private_ip " ] = addr [ " addr " ] if addr [ " OS-EXT-IPS:type " ] == " fixed "
2014-05-08 15:34:26 +04:00
end
end
2014-06-18 15:11:47 +04:00
o
2014-05-08 15:34:26 +04:00
end
2014-06-18 15:11:47 +04:00
end
2014-05-08 15:34:26 +04:00
2015-08-05 17:38:32 +03:00
def create_server s , image , flavor , subnets , groups , out , options = { }
2018-04-04 22:44:39 +03:00
out << " Creating server for project ' #{ s . project } - #{ s . environment } ' \n "
if s . name . nil?
2015-08-03 16:18:03 +03:00
out << " Generate new instance name: "
2018-04-04 22:44:39 +03:00
out << s . name = create_default_server_name ( s )
2015-08-03 16:18:03 +03:00
out << " \n "
end
2015-08-05 14:05:14 +03:00
networks = self . networks . select { | n | subnets . include? ( n [ " name " ] ) }
buf = subnets - networks . map { | n | n [ " name " ] }
2014-06-18 15:11:47 +04:00
unless buf . empty?
out << " No networks with names ' #{ buf . join ( " ', ' " ) } ' found "
return false
end
2015-08-05 14:05:14 +03:00
flavor = self . compute . list_flavors_detail . body [ " flavors " ] . detect { | f | f [ " name " ] == flavor } [ " id " ]
2018-04-04 22:44:39 +03:00
out << " Creating server with name ' #{ s . name } ', image ' #{ image } ', flavor ' #{ flavor } ', key ' #{ s . key } ' and networks ' #{ networks . map { | n | n [ " name " ] } . join ( " ', ' " ) } '... \n \n "
2014-06-18 15:11:47 +04:00
compute = self . compute
begin
2018-04-04 22:44:39 +03:00
o_server = compute . create_server ( s . name , image , flavor ,
2014-06-18 15:11:47 +04:00
" nics " = > networks . map { | n | { " net_id " = > n [ " id " ] } } ,
2015-08-05 14:05:14 +03:00
" security_groups " = > groups ,
2014-06-18 15:11:47 +04:00
" key_name " = > s . key )
rescue Excon :: Errors :: BadRequest = > e
response = :: Chef :: JSONCompat . from_json ( e . response . body )
if response [ 'badRequest' ] [ 'code' ] == 400
if response [ 'badRequest' ] [ 'message' ] =~ / Invalid flavorRef /
2015-08-05 14:05:14 +03:00
out << " \n ERROR: Bad request (400): Invalid flavor id specified: #{ flavor } "
2014-06-18 15:11:47 +04:00
elsif response [ 'badRequest' ] [ 'message' ] =~ / Invalid imageRef /
2015-08-05 14:05:14 +03:00
out << " \n ERROR: Bad request (400): Invalid image specified: #{ image } "
2014-05-08 15:34:26 +04:00
else
2014-06-18 15:11:47 +04:00
out << " \n ERROR: Bad request (400): #{ response [ 'badRequest' ] [ 'message' ] } "
2014-05-08 15:34:26 +04:00
end
2014-05-15 15:05:16 +04:00
out << " \n "
return false
2014-06-18 15:11:47 +04:00
else
out << " \n ERROR: Unknown server error ( #{ response [ 'badRequest' ] [ 'code' ] } ): #{ response [ 'badRequest' ] [ 'message' ] } "
2014-05-15 15:05:16 +04:00
out << " \n "
2014-05-08 15:34:26 +04:00
return false
end
2014-06-18 15:11:47 +04:00
rescue Excon :: Errors :: InternalServerError = > ise
out << " \n Error: openstack internal server error " + ise . message
out << " \n "
return false
rescue = > e2
out << " \n Error: Unknown error: " + e2 . message
out << " \n "
return false
end
sbody = o_server . body
s . id = sbody [ " server " ] [ " id " ]
2018-04-04 22:44:39 +03:00
return true
end
2014-06-18 15:11:47 +04:00
2018-04-04 22:44:39 +03:00
def waiting_server server , out
2014-06-18 15:11:47 +04:00
details , status = nil , nil
until status == " ACTIVE "
2018-04-04 22:44:39 +03:00
sleep ( 5 )
2014-06-18 15:11:47 +04:00
details = compute . get_server_details ( s . id ) . body
status = details [ " server " ] [ " status " ] . upcase
if status == " ERROR "
2015-08-04 13:20:35 +03:00
out << " error \n Server returns status 'ERROR' "
out << details [ " server " ]
2014-06-18 15:11:47 +04:00
return false
2014-05-08 15:34:26 +04:00
end
2018-04-04 22:44:39 +03:00
out << " . "
out . flush
2014-05-08 15:34:26 +04:00
end
2014-06-18 15:11:47 +04:00
network = networks [ 0 ] [ " name " ]
s . private_ip = details [ " server " ] [ " addresses " ] [ network ] [ 0 ] [ " addr " ]
2018-04-04 22:44:39 +03:00
out << " Done \n \n "
out . flush
2014-06-18 15:11:47 +04:00
true
end
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
def delete_server s
r = self . compute . delete_server ( s . id )
return r . status == 204 ? " Server with id ' #{ s . id } ' terminated " : r . body
end
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
def pause_server s
begin
self . compute . pause_server s . id
rescue Excon :: Errors :: Conflict = > e
return " pause "
2014-05-08 15:34:26 +04:00
end
2014-06-18 15:11:47 +04:00
return nil
end
2014-05-08 15:34:26 +04:00
2014-06-18 15:11:47 +04:00
def unpause_server s
begin
self . compute . unpause_server s . id
rescue Excon :: Errors :: Conflict = > e
return " unpause "
2014-05-08 15:34:26 +04:00
end
2014-06-18 15:11:47 +04:00
return nil
end
2014-05-08 15:34:26 +04:00
2014-06-20 12:59:17 +04:00
def compute
2015-07-21 19:47:16 +03:00
@compute || = connection_compute ( connection_options )
2014-06-20 12:59:17 +04:00
end
def network
connection_network ( self . connection_options )
end
2015-02-12 13:01:05 +03:00
2015-08-12 13:32:26 +03:00
def create_stack ( stack , out )
2015-04-16 17:54:40 +03:00
begin
2018-04-04 22:44:39 +03:00
out << " Creating stack for project ' #{ stack . project } ' and environment ' #{ stack . environment } '... \n "
2015-08-12 13:32:26 +03:00
stack . name = create_default_stack_name ( stack ) unless stack . name
out << " Stack name: #{ stack . name } \n "
out << " Stack template: #{ stack . stack_template } \n "
out << " Stack parameters: #{ stack . parameters } \n "
2015-08-17 17:50:24 +03:00
out . flush
2015-07-13 20:26:13 +03:00
response = orchestration . create_stack (
2015-08-12 13:32:26 +03:00
stack_name : stack . name ,
2018-04-04 22:44:39 +03:00
template : stack . stack_template_model . template_body ,
2015-08-14 17:48:07 +03:00
parameters : stack . parameters || { }
2015-07-13 20:26:13 +03:00
)
2015-08-12 13:32:26 +03:00
stack . id = response [ :body ] [ 'stack' ] [ 'id' ]
2015-08-17 17:50:24 +03:00
out << " Stack id: #{ stack . id } \n "
2015-08-18 18:41:15 +03:00
out . flush
2015-04-16 17:54:40 +03:00
rescue Excon :: Errors :: Conflict = > e
raise ProviderErrors :: NameConflict
2015-08-14 17:48:07 +03:00
rescue Excon :: Errors :: BadRequest = > br
response = :: Chef :: JSONCompat . from_json ( br . response . body )
if response [ 'code' ] == 400
out << " \n ERROR: Bad request (400): #{ response [ 'explanation' ] } "
out << " \n "
raise InvalidRecord . new ( response [ 'explanation' ] )
else
out << " \n ERROR: Unknown server error ( #{ response [ 'code' ] } ): #{ response [ 'explanation' ] } "
out << " \n "
raise InvalidRecord . new ( response [ 'explanation' ] )
end
2015-04-16 17:54:40 +03:00
end
2015-02-12 13:01:05 +03:00
end
2015-04-16 17:54:40 +03:00
2015-08-21 19:03:53 +03:00
def validate_stack_template template
r = orchestration . validate_template ( { 'template' = > template } )
pp r . body
end
2015-04-16 17:54:40 +03:00
def delete_stack ( stack )
2015-08-28 14:44:28 +03:00
begin
orchestration . delete_stack ( Fog :: Orchestration :: OpenStack :: Stack . new ( { 'id' = > stack . id , 'stack_name' = > stack . name } ) )
rescue Fog :: Compute :: OpenStack :: NotFound
puts 'already deleted'
end
2015-04-16 17:54:40 +03:00
end
2015-07-14 16:51:40 +03:00
def stack_details ( stack )
2016-02-24 21:26:44 +03:00
details = orchestration . show_stack_details ( stack . name , stack . id ) . body [ 'stack' ]
{
stack_status : details [ :stack_status ]
}
2015-07-15 18:37:27 +03:00
end
def stack_resources ( stack )
2015-08-17 17:50:24 +03:00
orchestration . list_resources ( Fog :: Orchestration :: OpenStack :: Stack . new ( { 'id' = > stack . id , 'stack_name' = > stack . name } ) ) . body [ 'resources' ]
2015-07-14 16:51:40 +03:00
end
2015-07-16 16:01:53 +03:00
def stack_resource ( stack , resource_id )
2015-09-16 12:14:01 +03:00
fog_stack = orchestration . stacks . get ( stack . name , stack . id )
2015-07-21 19:47:16 +03:00
physical_id = fog_stack ( stack ) . resources . get ( resource_id ) . physical_resource_id
2015-07-30 02:14:45 +03:00
compute . servers . get ( physical_id )
2015-07-16 16:01:53 +03:00
end
2015-08-14 17:48:07 +03:00
def stack_servers ( stack )
2015-08-31 14:26:16 +03:00
stack_resources ( stack ) . map do | r |
server = compute . servers . get ( r [ 'physical_resource_id' ] )
net = server . addresses . first
ip = net . last . first [ 'addr' ]
{
'name' = > server . name ,
'id' = > server . id ,
'key_name' = > server . key_name ,
'private_ip' = > ip ,
'public_ip' = > ip
}
end
2015-08-14 17:48:07 +03:00
end
2014-06-18 15:11:47 +04:00
private
def convert_groups list
res = { }
list . map do | g |
res [ g [ " name " ] ] = {
" description " = > g [ " description " ]
}
rules = [ ]
g [ " rules " ] . each do | r |
rules . push ( {
" protocol " = > r [ " ip_protocol " ] ,
" from " = > r [ " from_port " ] ,
" to " = > r [ " to_port " ] ,
" cidr " = > r [ " ip_range " ] [ " cidr " ]
} )
2014-05-08 15:34:26 +04:00
end
2014-06-18 15:11:47 +04:00
res [ g [ " name " ] ] [ " rules " ] = rules
2014-05-08 15:34:26 +04:00
end
2014-06-18 15:11:47 +04:00
res
end
2014-05-08 15:34:26 +04:00
2015-02-12 13:01:05 +03:00
def orchestration
2015-09-16 12:14:01 +03:00
@orchestration || = Fog :: Orchestration . new ( connection_options )
2015-02-12 13:01:05 +03:00
end
2014-05-08 15:34:26 +04:00
end
end