fluke/devops-service/providers/openstack.rb

263 lines
8.1 KiB
Ruby
Raw Normal View History

2015-08-03 15:09:04 +03:00
require "chef/json_compat"
2014-05-08 15:34:26 +04:00
require "providers/base_provider"
module Provider
# Provider for 'openstack'
class Openstack < BaseProvider
PROVIDER = "openstack"
def initialize config
self.certificate_path = config[:openstack_certificate]
self.ssh_key = config[:openstack_ssh_key]
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] || []
end
2014-05-08 15:34:26 +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
def name
PROVIDER
end
2014-05-08 15:34:26 +04:00
def groups filter=nil
convert_groups(compute.list_security_groups.body["security_groups"])
end
2014-05-08 15:34:26 +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
end
2014-05-08 15:34:26 +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
end
2014-05-08 15:34:26 +04:00
def networks_detail
net = self.network
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
}
}
end
2014-05-08 15:34:26 +04:00
def networks
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
}
}
end
2014-05-08 15:34:26 +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
o
2014-05-08 15:34:26 +04:00
end
end
2014-05-08 15:34:26 +04:00
def create_server s, out
out << "Creating server for project '#{s.project} - #{s.deploy_env}'\n"
2015-08-03 16:18:03 +03:00
if s.chef_node_name.nil?
out << "Generate new instance name: "
out << s.chef_node_name = create_default_chef_node_name(s)
out << "\n"
end
networks = self.networks.select{|n| s.options[:subnets].include?(n["name"])}
buf = s.options[:subnets] - networks.map{|n| n["name"]}
unless buf.empty?
out << "No networks with names '#{buf.join("', '")}' found"
return false
end
s.options[:flavor] = self.compute.list_flavors_detail.body["flavors"].detect{|f| f["name"] == s.options[:flavor]}["id"]
out << "Creating server with name '#{s.chef_node_name}', image '#{s.options[:image]}', flavor '#{s.options[:flavor]}', key '#{s.key}' and networks '#{networks.map{|n| n["name"]}.join("', '")}'...\n\n"
compute = self.compute
begin
o_server = compute.create_server(s.chef_node_name, s.options[:image], s.options[:flavor],
"nics" => networks.map{|n| {"net_id" => n["id"]}},
"security_groups" => s.options[:groups],
"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/
out << "\nERROR: Bad request (400): Invalid flavor id specified: #{s.options[:flavor]}"
elsif response['badRequest']['message'] =~ /Invalid imageRef/
out << "\nERROR: Bad request (400): Invalid image specified: #{s.options[:image]}"
2014-05-08 15:34:26 +04:00
else
out << "\nERROR: 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
else
out << "\nERROR: 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
rescue Excon::Errors::InternalServerError => ise
out << "\nError: openstack internal server error " + ise.message
out << "\n"
return false
rescue => e2
out << "\nError: Unknown error: " + e2.message
out << "\n"
return false
end
sbody = o_server.body
s.id = sbody["server"]["id"]
out << "\nInstance Name: #{s.chef_node_name}"
out << "\nInstance ID: #{s.id}\n"
out << "\nWaiting for server..."
details, status = nil, nil
until status == "ACTIVE"
sleep(1)
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\nServer returns status 'ERROR'"
out << details["server"]
return false
2014-05-08 15:34:26 +04:00
end
end
network = networks[0]["name"]
s.private_ip = details["server"]["addresses"][network][0]["addr"]
2015-08-04 13:20:35 +03:00
out << " done\n\n"
out << s.info
true
end
2014-05-08 15:34:26 +04:00
def create_default_chef_node_name s
"#{self.ssh_key}-#{s.project}-#{s.deploy_env}-#{Time.now.to_i}"
end
2014-06-11 12:13:02 +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
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
return nil
end
2014-05-08 15:34:26 +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
return nil
end
2014-05-08 15:34:26 +04:00
2014-06-20 12:59:17 +04:00
def compute
@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
def create_stack(stack)
begin
2015-07-13 20:26:13 +03:00
response = orchestration.create_stack(
stack_name: stack.id,
template: stack.template_body,
tenant_id: connection_options[:openstack_tenant],
parameters: stack.parameters
2015-07-13 20:26:13 +03:00
)
response[:body]['stack']['id']
rescue Excon::Errors::Conflict => e
raise ProviderErrors::NameConflict
end
2015-02-12 13:01:05 +03:00
end
def delete_stack(stack)
2015-07-15 18:37:27 +03:00
fog_stack(stack).destroy
end
2015-07-14 16:51:40 +03:00
def stack_details(stack)
2015-07-30 02:14:45 +03:00
fog_stack(stack).details
2015-07-15 18:37:27 +03:00
end
def stack_resources(stack)
fog_stack(stack).resources
2015-07-14 16:51:40 +03:00
end
def stack_resource(stack, resource_id)
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)
end
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
res[g["name"]]["rules"] = rules
2014-05-08 15:34:26 +04:00
end
res
end
2014-05-08 15:34:26 +04:00
2015-02-12 13:01:05 +03:00
def orchestration
@connection ||= Fog::Orchestration.new(connection_options)
2015-02-12 13:01:05 +03:00
end
2015-07-15 18:37:27 +03:00
def fog_stack(stack)
orchestration.stacks.get(stack.id, stack.cloud_stack_id)
end
2014-05-08 15:34:26 +04:00
end
end