modules/mu/cloud.rb in cloud-mu-3.1.5 vs modules/mu/cloud.rb in cloud-mu-3.1.6
- old
+ new
@@ -47,11 +47,11 @@
# Methods which a cloud resource implementation, e.g. Server, must implement
generic_class_methods = [:find, :cleanup, :validateConfig, :schema, :isGlobal?]
generic_instance_methods = [:create, :notify, :mu_name, :cloud_id, :config]
# Class methods which the base of a cloud implementation must implement
- generic_class_methods_toplevel = [:required_instance_methods, :myRegion, :listRegions, :listAZs, :hosted?, :hosted_config, :config_example, :writeDeploySecret, :listCredentials, :credConfig, :listInstanceTypes, :adminBucketName, :adminBucketUrl, :habitat]
+ generic_class_methods_toplevel = [:required_instance_methods, :myRegion, :listRegions, :listAZs, :hosted?, :hosted_config, :config_example, :writeDeploySecret, :listCredentials, :credConfig, :listInstanceTypes, :adminBucketName, :adminBucketUrl, :listHabitats, :habitat, :virtual?]
# Public attributes which will be available on all instantiated cloud resource objects
#
# +:config+: The fully-resolved {MU::Config} hash describing the object, aka the Basket of Kittens entry
#
@@ -527,11 +527,11 @@
response = open("#{base_url}/#{cloud}.yaml").read
images ||= {}
images.deep_merge!(YAML.load(response))
break
end
- rescue StandardError
+ rescue StandardError => e
if fail_hard
raise MuError, "Failed to fetch stock images from #{base_url}/#{cloud}.yaml (#{e.message})"
else
MU.log "Failed to fetch stock images from #{base_url}/#{cloud}.yaml (#{e.message})", MU::WARN if !quiet
end
@@ -642,22 +642,33 @@
@@resource_types
end
# Shorthand lookup for resource type names. Given any of the shorthand class name, configuration name (singular or plural), or full class name, return all four as a set.
# @param type [String]: A string that looks like our short or full class name or singular or plural configuration names.
+ # @param assert [Boolean]: Raise an exception if the type isn't valid
# @return [Array]: Class name (Symbol), singular config name (String), plural config name (String), full class name (Object)
- def self.getResourceNames(type)
- return [nil, nil, nil, nil, {}] if !type
+ def self.getResourceNames(type, assert = true)
+ if !type
+ if assert
+ raise MuError, "nil resource type requested in getResourceNames"
+ else
+ return [nil, nil, nil, nil, {}]
+ end
+ end
@@resource_types.each_pair { |name, cloudclass|
if name == type.to_sym or
cloudclass[:cfg_name] == type or
cloudclass[:cfg_plural] == type or
Object.const_get("MU").const_get("Cloud").const_get(name) == type
type = name
return [type.to_sym, cloudclass[:cfg_name], cloudclass[:cfg_plural], Object.const_get("MU").const_get("Cloud").const_get(name), cloudclass]
end
}
+ if assert
+ raise MuError, "Invalid resource type #{type} requested in getResourceNames"
+ end
+
[nil, nil, nil, nil, {}]
end
# Net::SSH exceptions seem to have their own behavior vis a vis threads,
# and our regular call stack gets circumvented when they're thrown. Cheat
@@ -682,10 +693,18 @@
# @return [Array<String>]
def self.supportedClouds
@@supportedCloudList
end
+ # Raise an exception if the cloud provider specified isn't valid
+ def self.assertSupportedCloud(cloud)
+ if cloud.nil? or !supportedClouds.include?(cloud.to_s)
+ raise MuError, "Cloud provider #{cloud} is not supported"
+ end
+ Object.const_get("MU").const_get("Cloud").const_get(cloud.to_s)
+ end
+
# List of known/supported Cloud providers for which we have at least one
# set of credentials configured.
# @return [Array<String>]
def self.availableClouds
available = []
@@ -699,10 +718,18 @@
}
available
end
+ # Raise an exception if the cloud provider specified isn't valid or we
+ # don't have any credentials configured for it.
+ def self.assertAvailableCloud(cloud)
+ if cloud.nil? or availableClouds.include?(cloud.to_s)
+ raise MuError, "Cloud provider #{cloud} is not available"
+ end
+ end
+
# Load the container class for each cloud we know about, and inject autoload
# code for each of its supported resource type classes.
failed = []
MU::Cloud.supportedClouds.each { |cloud|
require "mu/clouds/#{cloud.downcase}"
@@ -821,32 +848,32 @@
raise MuCloudResourceNotImplemented, "MU::Cloud::#{cloud} does not currently implement #{shortclass}, or implementation does not load correctly (#{e.message})"
end
@cloud_class_cache[cloud] = {} if !@cloud_class_cache.has_key?(cloud)
begin
cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
- myclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(type)
- @@resource_types[type.to_sym][:class].each { |class_method|
+ myclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(shortclass)
+ @@resource_types[shortclass.to_sym][:class].each { |class_method|
if !myclass.respond_to?(class_method) or myclass.method(class_method).owner.to_s != "#<Class:#{myclass}>"
- raise MuError, "MU::Cloud::#{cloud}::#{type} has not implemented required class method #{class_method}"
+ raise MuError, "MU::Cloud::#{cloud}::#{shortclass} has not implemented required class method #{class_method}"
end
}
- @@resource_types[type.to_sym][:instance].each { |instance_method|
+ @@resource_types[shortclass.to_sym][:instance].each { |instance_method|
if !myclass.public_instance_methods.include?(instance_method)
- raise MuCloudResourceNotImplemented, "MU::Cloud::#{cloud}::#{type} has not implemented required instance method #{instance_method}"
+ raise MuCloudResourceNotImplemented, "MU::Cloud::#{cloud}::#{shortclass} has not implemented required instance method #{instance_method}"
end
}
cloudclass.required_instance_methods.each { |instance_method|
if !myclass.public_instance_methods.include?(instance_method)
- MU.log "MU::Cloud::#{cloud}::#{type} has not implemented required instance method #{instance_method}, will declare as attr_accessor", MU::DEBUG
+ MU.log "MU::Cloud::#{cloud}::#{shortclass} has not implemented required instance method #{instance_method}, will declare as attr_accessor", MU::DEBUG
end
}
@cloud_class_cache[cloud][type] = myclass
return myclass
rescue NameError => e
@cloud_class_cache[cloud][type] = nil
- raise MuCloudResourceNotImplemented, "The '#{type}' resource is not supported in cloud #{cloud} (tried MU::#{cloud}::#{type})", e.backtrace
+ raise MuCloudResourceNotImplemented, "The '#{type}' resource is not supported in cloud #{cloud} (tried MU::Cloud::#{cloud}::#{shortclass})", e.backtrace
end
end
MU::Cloud.supportedClouds.each { |cloud|
Object.const_get("MU").const_get("Cloud").const_get(cloud).class_eval {
@@ -865,10 +892,12 @@
@@resource_types.keys.each { |name|
Object.const_get("MU").const_get("Cloud").const_get(name).class_eval {
attr_reader :cloudclass
attr_reader :cloudobj
+ attr_reader :credentials
+ attr_reader :config
attr_reader :destroyed
attr_reader :delayed_save
def self.shortname
name.sub(/.*?::([^:]+)$/, '\1')
@@ -919,18 +948,31 @@
# @param mommacat [MU::MommaCat]: The deploy to which we're being told we belong
# @param force [Boolean]: Set even if we already have a deploy object
# @return [String]: Our new +deploy_id+
def intoDeploy(mommacat, force: false)
if force or (!@deploy)
- MU.log "Inserting #{self} (#{self.object_id}) into #{mommacat.deploy_id}", MU::DEBUG
+ MU.log "Inserting #{self} [#{self.object_id}] into #{mommacat.deploy_id} as a #{@config['name']}", MU::DEBUG
+
@deploy = mommacat
+ @deploy.addKitten(@cloudclass.cfg_plural, @config['name'], self)
@deploy_id = @deploy.deploy_id
@cloudobj.intoDeploy(mommacat, force: force) if @cloudobj
end
@deploy_id
end
+ # Return the +virtual_name+ config field, if it is set.
+ # @param name [String]: If set, will only return a value if +virtual_name+ matches this string
+ # @return [String,nil]
+ def virtual_name(name = nil)
+ if @config and @config['virtual_name'] and
+ (!name or name == @config['virtual_name'])
+ return @config['virtual_name']
+ end
+ nil
+ end
+
# @param mommacat [MU::MommaCat]: The deployment containing this cloud resource
# @param mu_name [String]: Optional- specify the full Mu resource name of an existing resource to load, instead of creating a new one
# @param cloud_id [String]: Optional- specify the cloud provider's identifier for an existing resource to load, instead of creating a new one
# @param kitten_cfg [Hash]: The parse configuration for this object from {MU::Config}
def initialize(**args)
@@ -953,11 +995,10 @@
my_cloud = args[:kitten_cfg]['cloud'].to_s || MU::Config.defaultCloud
if my_cloud.nil? or !MU::Cloud.supportedClouds.include?(my_cloud)
raise MuError, "Can't instantiate a MU::Cloud object without a valid cloud (saw '#{my_cloud}')"
end
-
@cloudclass = MU::Cloud.loadCloudType(my_cloud, self.class.shortname)
@cloudparentclass = Object.const_get("MU").const_get("Cloud").const_get(my_cloud)
@cloudobj = @cloudclass.new(
mommacat: args[:mommacat],
kitten_cfg: args[:kitten_cfg],
@@ -979,11 +1020,10 @@
@deploy.addKitten(self.class.cfg_name, @config['name'], self)
elsif !@deploy.nil? and @cloudobj.mu_name.nil?
MU.log "#{self} in #{@deploy.deploy_id} didn't generate a mu_name after being loaded/initialized, dependencies on this resource will probably be confused!", MU::ERR, details: [caller, args.keys]
end
-
# We are actually a child object invoking this via super() from its
# own initialize(), so initialize all the attributes and instance
# variables we know to be universal.
else
@@ -2019,20 +2059,21 @@
rebootable_fails = 0
begin
loglevel = retries > 4 ? MU::NOTICE : MU::DEBUG
MU.log "Calling WinRM on #{@mu_name}", loglevel, details: opts
opts = {
- endpoint: 'https://'+@mu_name+':5986/wsman',
retry_limit: winrm_retries,
no_ssl_peer_verification: true, # XXX this should not be necessary; we get 'hostname "foo" does not match the server certificate' even when it clearly does match
ca_trust_path: "#{MU.mySSLDir}/Mu_CA.pem",
transport: :ssl,
operation_timeout: timeout,
}
- if retries % 2 == 0
+ if retries % 2 == 0 # NTLM password over https
+ opts[:endpoint] = 'https://'+canonical_ip+':5986/wsman'
opts[:user] = @config['windows_admin_username']
opts[:password] = getWindowsAdminPassword
- else
+ else # certificate auth over https
+ opts[:endpoint] = 'https://'+@mu_name+':5986/wsman'
opts[:client_cert] = "#{MU.mySSLDir}/#{@mu_name}-winrm.crt"
opts[:client_key] = "#{MU.mySSLDir}/#{@mu_name}-winrm.key"
end
conn = WinRM::Connection.new(opts)
conn.logger.level = :debug if retries > 2