210 lines
7.1 KiB
Ruby
210 lines
7.1 KiB
Ruby
## This is kind of a hack, to ensure that the cookbook can be
|
|
# loaded. On first load, nokogiri may not be present. It is
|
|
# installed at load time by recipes/default.rb, so that at run
|
|
# time nokogiri will be present.
|
|
#
|
|
require 'forwardable'
|
|
|
|
# rubocop:disable Metrics/ClassLength
|
|
module SMFManifest
|
|
# XMLBuilder manages the translation of the SMF Chef resource attributes into
|
|
# XML that can be parsed by `svccfg import`.
|
|
#
|
|
# SMFManifest::XMLBuilder.new(resource, node).to_xml
|
|
#
|
|
class XMLBuilder
|
|
# allow delegation
|
|
extend Forwardable
|
|
|
|
attr_reader :resource, :node
|
|
|
|
# delegate methods to :resource
|
|
def_delegators :resource, :name, :authorization_name, :dependencies, :duration, :environment, :group, :ignore,
|
|
:include_default_dependencies, :locale, :manifest_type, :project, :property_groups,
|
|
:service_path, :stability, :working_directory
|
|
|
|
public
|
|
|
|
def initialize(smf_resource, node)
|
|
@resource = smf_resource
|
|
@node = node
|
|
end
|
|
|
|
def to_xml
|
|
@xml_output ||= xml_output
|
|
end
|
|
|
|
protected
|
|
|
|
## methods that need to be called from within the context
|
|
# of the Nokogiri builder block need to be protected, rather
|
|
# than private.
|
|
|
|
def commands
|
|
@commands ||= {
|
|
'start' => resource.start_command,
|
|
'stop' => resource.stop_command,
|
|
'restart' => resource.restart_command,
|
|
'refresh' => resource.refresh_command
|
|
}
|
|
end
|
|
|
|
def timeout
|
|
@timeouts ||= {
|
|
'start' => resource.start_timeout,
|
|
'stop' => resource.stop_timeout,
|
|
'restart' => resource.restart_timeout,
|
|
'refresh' => resource.refresh_timeout
|
|
}
|
|
end
|
|
|
|
def default_dependencies
|
|
if node.platform == 'solaris2' && node.platform_version == '5.11'
|
|
[
|
|
{ 'name' => 'milestone', 'value' => '/milestone/config' },
|
|
{ 'name' => 'fs-local', 'value' => '/system/filesystem/local' },
|
|
{ 'name' => 'name-services', 'value' => '/milestone/name-services' },
|
|
{ 'name' => 'network', 'value' => '/milestone/network' }
|
|
]
|
|
else
|
|
[
|
|
{ 'name' => 'milestone', 'value' => '/milestone/sysconfig' },
|
|
{ 'name' => 'fs-local', 'value' => '/system/filesystem/local' },
|
|
{ 'name' => 'name-services', 'value' => '/milestone/name-services' },
|
|
{ 'name' => 'network', 'value' => '/milestone/network' }
|
|
]
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def xml_output
|
|
xml_builder = ::Builder::XmlMarkup.new(indent: 2)
|
|
xml_builder.instruct!
|
|
xml_builder.declare! :DOCTYPE, :service_bundle, :SYSTEM, '/usr/share/lib/xml/dtd/service_bundle.dtd.1'
|
|
xml_builder.service_bundle('name' => name, 'type' => 'manifest') do |xml|
|
|
xml.service('name' => service_fmri, 'type' => 'service', 'version' => '1') do |service|
|
|
service.create_default_instance('enabled' => 'false')
|
|
service.single_instance
|
|
|
|
if include_default_dependencies
|
|
default_dependencies.each do |dependency|
|
|
service.dependency('name' => dependency['name'],
|
|
'grouping' => 'require_all',
|
|
'restart_on' => 'none',
|
|
'type' => 'service') do |dep|
|
|
dep.service_fmri('value' => "svc:#{dependency['value']}")
|
|
end
|
|
end
|
|
end
|
|
|
|
dependencies.each do |dependency|
|
|
service.dependency('name' => dependency['name'],
|
|
'grouping' => dependency['grouping'],
|
|
'restart_on' => dependency['restart_on'],
|
|
'type' => dependency['type']) do |dep|
|
|
dependency['fmris'].each do |service_fmri|
|
|
dep.service_fmri('value' => service_fmri)
|
|
end
|
|
end
|
|
end
|
|
|
|
service.method_context(exec_context) do |context|
|
|
context.method_credential(credentials) if user != 'root'
|
|
|
|
if environment
|
|
context.method_environment do |env|
|
|
environment.each_pair do |var, value|
|
|
env.envvar('name' => var, 'value' => value)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
commands.each_pair do |type, command|
|
|
if command
|
|
service.exec_method('type' => 'method', 'name' => type, 'exec' => command, 'timeout_seconds' => timeout[type])
|
|
end
|
|
end
|
|
|
|
service.property_group('name' => 'general', 'type' => 'framework') do |group|
|
|
group.propval('name' => 'action_authorization',
|
|
'type' => 'astring',
|
|
'value' => "solaris.smf.manage.#{authorization_name}")
|
|
group.propval('name' => 'value_authorization',
|
|
'type' => 'astring',
|
|
'value' => "solaris.smf.value.#{authorization_name}")
|
|
end
|
|
|
|
if sets_duration? || ignores_faults?
|
|
service.property_group('name' => 'startd', 'type' => 'framework') do |group|
|
|
group.propval('name' => 'duration', 'type' => 'astring', 'value' => duration) if sets_duration?
|
|
group.propval('name' => 'ignore_error', 'type' => 'astring', 'value' => ignore.join(',')) if ignores_faults?
|
|
end
|
|
end
|
|
|
|
property_groups.each_pair do |name, properties|
|
|
service.property_group('name' => name, 'type' => properties.delete('type') { |_type| 'application' }) do |group|
|
|
properties.each_pair do |key, value|
|
|
group.propval('name' => key, 'value' => value, 'type' => check_type(value))
|
|
end
|
|
end
|
|
end
|
|
|
|
service.stability('value' => stability)
|
|
|
|
service.template do |template|
|
|
template.common_name do |common_name|
|
|
common_name.loctext(name, 'xml:lang' => locale)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
xml_builder.target!
|
|
end
|
|
|
|
def credentials
|
|
creds = { 'user' => user, 'privileges' => resource.privilege_list }
|
|
creds.merge!('group' => group) unless group.nil?
|
|
creds
|
|
end
|
|
|
|
def user
|
|
resource.user || resource.credentials_user || 'root'
|
|
end
|
|
|
|
def exec_context
|
|
context = {}
|
|
context['working_directory'] = working_directory unless working_directory.nil?
|
|
context['project'] = project unless project.nil?
|
|
context
|
|
end
|
|
|
|
def check_type(value)
|
|
if value == value.to_i
|
|
'integer'
|
|
else
|
|
'astring'
|
|
end
|
|
end
|
|
|
|
def ignores_faults?
|
|
!ignore.nil?
|
|
end
|
|
|
|
def sets_duration?
|
|
duration != 'contract'
|
|
end
|
|
|
|
# resource.fmri is set in the SMF :install action of the default provider.
|
|
# If there is already a service with a name that is matched by our resource.name
|
|
# then we grab the FMRI (fault management resource identifier) from the system.
|
|
# If a service is not found, we set this to our own FMRI.
|
|
def service_fmri
|
|
resource.fmri.nil? || resource.fmri.empty? ? "#{manifest_type}/management/#{name}" : resource.fmri.gsub(/^\//, '')
|
|
end
|
|
end
|
|
end
|
|
# rubocop:enable Metrics/ClassLength
|