right_link 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/actors/agent_manager.rb +88 -0
- data/actors/instance_scheduler.rb +321 -0
- data/actors/instance_services.rb +64 -0
- data/actors/instance_setup.rb +567 -0
- data/bin/cloud +25 -0
- data/bin/cook_runner +44 -0
- data/bin/deploy +120 -0
- data/bin/enroll +385 -0
- data/bin/rad +32 -0
- data/bin/rchk +29 -0
- data/bin/rnac +39 -0
- data/bin/rs_connect +33 -0
- data/bin/rs_log_level +31 -0
- data/bin/rs_ohai +28 -0
- data/bin/rs_reenroll +31 -0
- data/bin/rs_run_recipe +34 -0
- data/bin/rs_run_right_script +34 -0
- data/bin/rs_shutdown +33 -0
- data/bin/rs_tag +33 -0
- data/bin/rs_thunk +33 -0
- data/bin/rstat +31 -0
- data/bin/system +16 -0
- data/ext/Rakefile +18 -0
- data/init/config.yml +5 -0
- data/init/init.rb +79 -0
- data/lib/chef/ohai_setup.rb +51 -0
- data/lib/chef/plugins/cloud.rb +91 -0
- data/lib/chef/plugins/cloudstack.rb +23 -0
- data/lib/chef/plugins/ec2.rb +23 -0
- data/lib/chef/plugins/linux/block_device2.rb +24 -0
- data/lib/chef/plugins/rackspace.rb +23 -0
- data/lib/chef/plugins/rightscale.rb +125 -0
- data/lib/chef/plugins/windows/network.rb +114 -0
- data/lib/chef/plugins.rb +74 -0
- data/lib/chef/providers/dns_dnsmadeeasy_provider.rb +81 -0
- data/lib/chef/providers/dns_resource.rb +100 -0
- data/lib/chef/providers/executable_schedule_provider.rb +70 -0
- data/lib/chef/providers/executable_schedule_resource.rb +144 -0
- data/lib/chef/providers/remote_recipe_provider.rb +86 -0
- data/lib/chef/providers/remote_recipe_resource.rb +101 -0
- data/lib/chef/providers/right_link_tag_provider.rb +73 -0
- data/lib/chef/providers/right_link_tag_resource.rb +59 -0
- data/lib/chef/providers/right_script_provider.rb +190 -0
- data/lib/chef/providers/right_script_resource.rb +113 -0
- data/lib/chef/providers/rs_shutdown_provider.rb +75 -0
- data/lib/chef/providers/rs_shutdown_resource.rb +55 -0
- data/lib/chef/providers/server_collection_provider.rb +66 -0
- data/lib/chef/providers/server_collection_resource.rb +93 -0
- data/lib/chef/providers/windows/powershell_provider.rb +151 -0
- data/lib/chef/providers/windows/powershell_resource.rb +111 -0
- data/lib/chef/providers/windows/unsupported_provider.rb +51 -0
- data/lib/chef/right_providers.rb +55 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.csproj +104 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.dll-Help.xml +141 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Exceptions.cs +182 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionCommand.cs +178 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionRequest.cs +67 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionResponse.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueCommandBase.cs +142 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueRequestBase.cs +64 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueResponseBase.cs +69 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/JsonTransport.cs +110 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeClient.cs +158 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeServer.cs +142 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolConstants.cs +55 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolUtilities.cs +77 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ReadMe.txt +53 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeResponse.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceResponse.cs +40 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceResponse.cs +40 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueCommandBase.cs +293 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueRequestBase.cs +75 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueResponseBase.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Transport.cs +91 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet.sln +35 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Program.cs +374 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/TestChefNodeCmdlet.csproj +65 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Program.cs +136 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Properties/AssemblyInfo.cs +36 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/ReadMe.txt +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/TestNextActionCmdlet.csproj +68 -0
- data/lib/chef/windows/bin/Newtonsoft.Json.dll +0 -0
- data/lib/chef/windows/chef_node_server.rb +463 -0
- data/lib/chef/windows/dynamic_powershell_provider.rb +296 -0
- data/lib/chef/windows/pipe_server.rb +283 -0
- data/lib/chef/windows/powershell_host.rb +285 -0
- data/lib/chef/windows/powershell_pipe_server.rb +136 -0
- data/lib/chef/windows/powershell_provider_base.rb +92 -0
- data/lib/chef/windows/scripts/run_loop.ps1 +105 -0
- data/lib/clouds/cloud.rb +557 -0
- data/lib/clouds/cloud_factory.rb +250 -0
- data/lib/clouds/cloud_utilities.rb +244 -0
- data/lib/clouds/clouds/azure.rb +106 -0
- data/lib/clouds/clouds/cloudstack.rb +114 -0
- data/lib/clouds/clouds/ec2.rb +113 -0
- data/lib/clouds/clouds/eucalyptus.rb +46 -0
- data/lib/clouds/clouds/google.rb +102 -0
- data/lib/clouds/clouds/none.rb +76 -0
- data/lib/clouds/clouds/openstack.rb +30 -0
- data/lib/clouds/clouds/rackspace-ng.rb +54 -0
- data/lib/clouds/clouds/rackspace.rb +78 -0
- data/lib/clouds/clouds/softlayer.rb +91 -0
- data/lib/clouds/metadata_formatter.rb +108 -0
- data/lib/clouds/metadata_provider.rb +128 -0
- data/lib/clouds/metadata_source.rb +87 -0
- data/lib/clouds/metadata_sources/certificate_metadata_source.rb +207 -0
- data/lib/clouds/metadata_sources/config_drive_metadata_source.rb +129 -0
- data/lib/clouds/metadata_sources/file_metadata_source.rb +74 -0
- data/lib/clouds/metadata_sources/http_metadata_source.rb +277 -0
- data/lib/clouds/metadata_sources/selective_metadata_source.rb +122 -0
- data/lib/clouds/metadata_tree_climber.rb +144 -0
- data/lib/clouds/metadata_writer.rb +155 -0
- data/lib/clouds/metadata_writers/dictionary_metadata_writer.rb +72 -0
- data/lib/clouds/metadata_writers/ruby_metadata_writer.rb +76 -0
- data/lib/clouds/metadata_writers/shell_metadata_writer.rb +121 -0
- data/lib/clouds/register_clouds.rb +34 -0
- data/lib/clouds.rb +32 -0
- data/lib/gem_dependencies.rb +83 -0
- data/lib/git_hooks/commit-msg.rb +7 -0
- data/lib/instance/agent_config.rb +168 -0
- data/lib/instance/agent_watcher.rb +233 -0
- data/lib/instance/audit_cook_stub.rb +104 -0
- data/lib/instance/audit_proxy.rb +247 -0
- data/lib/instance/bundle_queue.rb +104 -0
- data/lib/instance/cook/agent_connection.rb +109 -0
- data/lib/instance/cook/audit_logger.rb +165 -0
- data/lib/instance/cook/audit_stub.rb +142 -0
- data/lib/instance/cook/ca-bundle.crt +2794 -0
- data/lib/instance/cook/chef_state.rb +211 -0
- data/lib/instance/cook/cook.rb +306 -0
- data/lib/instance/cook/cook_state.rb +298 -0
- data/lib/instance/cook/cookbook_path_mapping.rb +66 -0
- data/lib/instance/cook/cookbook_repo_retriever.rb +190 -0
- data/lib/instance/cook/executable_sequence.rb +765 -0
- data/lib/instance/cook/external_parameter_gatherer.rb +190 -0
- data/lib/instance/cook/repose_downloader.rb +349 -0
- data/lib/instance/cook/shutdown_request_proxy.rb +121 -0
- data/lib/instance/cook.rb +41 -0
- data/lib/instance/downloader.rb +208 -0
- data/lib/instance/duplicable.rb +67 -0
- data/lib/instance/exceptions.rb +49 -0
- data/lib/instance/executable_sequence_proxy.rb +278 -0
- data/lib/instance/instance_commands.rb +577 -0
- data/lib/instance/instance_state.rb +633 -0
- data/lib/instance/json_utilities.rb +102 -0
- data/lib/instance/login_manager.rb +533 -0
- data/lib/instance/login_user_manager.rb +522 -0
- data/lib/instance/message_encoder.rb +118 -0
- data/lib/instance/multi_thread_bundle_queue.rb +232 -0
- data/lib/instance/operation_context.rb +60 -0
- data/lib/instance/options_bag.rb +65 -0
- data/lib/instance/payload_formatter.rb +46 -0
- data/lib/instance/policy.rb +53 -0
- data/lib/instance/policy_audit.rb +100 -0
- data/lib/instance/policy_manager.rb +146 -0
- data/lib/instance/reenroll_manager.rb +104 -0
- data/lib/instance/right_scripts_cookbook.rb +181 -0
- data/lib/instance/shutdown_request.rb +221 -0
- data/lib/instance/single_thread_bundle_queue.rb +189 -0
- data/lib/instance/volume_management.rb +450 -0
- data/lib/instance.rb +50 -0
- data/lib/repo_conf_generators/apt_conf_generators.rb +106 -0
- data/lib/repo_conf_generators/gem_conf_generators.rb +80 -0
- data/lib/repo_conf_generators/rightscale_conf_generators.rb +254 -0
- data/lib/repo_conf_generators/rightscale_key.pub +17 -0
- data/lib/repo_conf_generators/yum_conf_generators.rb +225 -0
- data/lib/repo_conf_generators.rb +30 -0
- data/lib/run_shell.rb +28 -0
- data/scripts/agent_checker.rb +571 -0
- data/scripts/agent_controller.rb +247 -0
- data/scripts/agent_deployer.rb +148 -0
- data/scripts/bundle_runner.rb +336 -0
- data/scripts/cloud_controller.rb +176 -0
- data/scripts/log_level_manager.rb +142 -0
- data/scripts/ohai_runner.rb +33 -0
- data/scripts/reenroller.rb +193 -0
- data/scripts/server_importer.rb +293 -0
- data/scripts/shutdown_client.rb +183 -0
- data/scripts/system_configurator.rb +367 -0
- data/scripts/tagger.rb +381 -0
- data/scripts/thunker.rb +356 -0
- metadata +418 -0
@@ -0,0 +1,450 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2011 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
module RightScale
|
24
|
+
|
25
|
+
class VolumeManagement
|
26
|
+
# Delay enough time for attach/detach state in core to refresh
|
27
|
+
VOLUME_RETRY_SECONDS = 15 # minimum retry time as recommended in docs when volumes are changing by automation
|
28
|
+
|
29
|
+
# Max retries for attaching/detaching a given volume.
|
30
|
+
MAX_VOLUME_ATTEMPTS = 8 # 8 * 15 seconds = 2 minutes
|
31
|
+
|
32
|
+
class InvalidResponse < Exception; end
|
33
|
+
class UnexpectedState < Exception; end
|
34
|
+
class UnsupportedMountPoint < Exception; end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Provides helper methods for platforms which must manage volume attachments on the
|
38
|
+
# instance agent side
|
39
|
+
module VolumeManagementHelper
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
# Manages planned volumes by caching planned volume state and then ensuring
|
44
|
+
# volumes have been reattached in a predictable order for proper assignment
|
45
|
+
# of local drives.
|
46
|
+
#
|
47
|
+
# === Parameters
|
48
|
+
# block(Proc):: continuation callback for when volume management is complete.
|
49
|
+
#
|
50
|
+
# === Return
|
51
|
+
# result(Boolean):: true if successful
|
52
|
+
def manage_planned_volumes(&block)
|
53
|
+
# state may have changed since timer calling this method was added, so
|
54
|
+
# ensure we are still booting (and not stranded).
|
55
|
+
return if InstanceState.value == 'stranded'
|
56
|
+
|
57
|
+
# query for planned volume mappings belonging to instance.
|
58
|
+
last_mappings = InstanceState.planned_volume_state.mappings || []
|
59
|
+
payload = {:agent_identity => @agent_identity}
|
60
|
+
req = IdempotentRequest.new("/storage_valet/get_planned_volumes", payload, :retry_delay => VolumeManagement::VOLUME_RETRY_SECONDS)
|
61
|
+
req.callback do |res|
|
62
|
+
begin
|
63
|
+
mappings = merge_planned_volume_mappings(last_mappings, res)
|
64
|
+
InstanceState.planned_volume_state.mappings = mappings
|
65
|
+
if mappings.empty?
|
66
|
+
# no volumes requiring management.
|
67
|
+
@audit.append_info("This instance has no planned volumes.")
|
68
|
+
block.call if block
|
69
|
+
elsif (detachable_volume_count = mappings.count { |mapping| is_unmanaged_attached_volume?(mapping) }) >= 1
|
70
|
+
# must detach all 'attached' volumes if any are attached (or
|
71
|
+
# attaching) but not yet managed on the instance side. this is the
|
72
|
+
# only way to ensure they receive the correct device names.
|
73
|
+
mappings.each do |mapping|
|
74
|
+
if is_unmanaged_attached_volume?(mapping)
|
75
|
+
detach_planned_volume(mapping) do
|
76
|
+
detachable_volume_count -= 1
|
77
|
+
if 0 == detachable_volume_count
|
78
|
+
# add a timer to resume volume management later and pass the
|
79
|
+
# block for continuation afterward (unless detachment stranded).
|
80
|
+
Log.info("Waiting for volumes to detach for management purposes. "\
|
81
|
+
"Retrying in #{VolumeManagement::VOLUME_RETRY_SECONDS} seconds...")
|
82
|
+
EM.add_timer(VolumeManagement::VOLUME_RETRY_SECONDS) { manage_planned_volumes(&block) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
elsif mapping = mappings.find { |mapping| is_detaching_volume?(mapping) }
|
88
|
+
# we successfully requested detachment but status has not
|
89
|
+
# changed to reflect this yet.
|
90
|
+
Log.info("Waiting for volume #{mapping[:volume_id]} to fully detach. "\
|
91
|
+
"Retrying in #{VolumeManagement::VOLUME_RETRY_SECONDS} seconds...")
|
92
|
+
EM.add_timer(VolumeManagement::VOLUME_RETRY_SECONDS) { manage_planned_volumes(&block) }
|
93
|
+
elsif mapping = mappings.find { |mapping| is_managed_attaching_volume?(mapping) }
|
94
|
+
Log.info("Waiting for volume #{mapping[:volume_id]} to fully attach. Retrying in #{VolumeManagement::VOLUME_RETRY_SECONDS} seconds...")
|
95
|
+
EM.add_timer(VolumeManagement::VOLUME_RETRY_SECONDS) { manage_planned_volumes(&block) }
|
96
|
+
elsif mapping = mappings.find { |mapping| is_managed_attached_unassigned_volume?(mapping) }
|
97
|
+
manage_volume_device_assignment(mapping) do
|
98
|
+
unless InstanceState.value == 'stranded'
|
99
|
+
# we can move on to next volume 'immediately' if volume was
|
100
|
+
# successfully assigned its device name.
|
101
|
+
if mapping[:management_status] == 'assigned'
|
102
|
+
EM.next_tick { manage_planned_volumes(&block) }
|
103
|
+
else
|
104
|
+
Log.info("Waiting for volume #{mapping[:volume_id]} to initialize using \"#{mapping[:mount_points].first}\". "\
|
105
|
+
"Retrying in #{VolumeManagement::VOLUME_RETRY_SECONDS} seconds...")
|
106
|
+
EM.add_timer(VolumeManagement::VOLUME_RETRY_SECONDS) { manage_planned_volumes(&block) }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
elsif mapping = mappings.find { |mapping| is_detached_volume?(mapping) }
|
111
|
+
attach_planned_volume(mapping) do
|
112
|
+
unless InstanceState.value == 'stranded'
|
113
|
+
unless mapping[:attempts]
|
114
|
+
@audit.append_info("Attached volume #{mapping[:volume_id]} using \"#{mapping[:mount_points].first}\".")
|
115
|
+
Log.info("Waiting for volume #{mapping[:volume_id]} to appear using \"#{mapping[:mount_points].first}\". "\
|
116
|
+
"Retrying in #{VolumeManagement::VOLUME_RETRY_SECONDS} seconds...")
|
117
|
+
end
|
118
|
+
EM.add_timer(VolumeManagement::VOLUME_RETRY_SECONDS) { manage_planned_volumes(&block) }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
elsif mapping = mappings.find { |mapping| is_unmanageable_volume?(mapping) }
|
122
|
+
strand("State of volume #{mapping[:volume_id]} was unmanageable: #{mapping[:volume_status]}")
|
123
|
+
else
|
124
|
+
# all volumes are managed and have been assigned and so we can proceed.
|
125
|
+
block.call if block
|
126
|
+
end
|
127
|
+
rescue Exception => e
|
128
|
+
strand(e)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
req.errback do |res|
|
133
|
+
strand("Failed to retrieve planned volume mappings", res)
|
134
|
+
end
|
135
|
+
|
136
|
+
req.run
|
137
|
+
end
|
138
|
+
|
139
|
+
# Detaches the planned volume given by its mapping.
|
140
|
+
#
|
141
|
+
# === Parameters
|
142
|
+
# mapping(Hash):: details of planned volume
|
143
|
+
def detach_planned_volume(mapping)
|
144
|
+
payload = {:agent_identity => @agent_identity, :device_name => mapping[:device_name]}
|
145
|
+
Log.info("Detaching volume #{mapping[:volume_id]} for management purposes.")
|
146
|
+
req = IdempotentRequest.new("/storage_valet/detach_volume", payload, :retry_delay => VolumeManagement::VOLUME_RETRY_SECONDS)
|
147
|
+
|
148
|
+
req.callback do |res|
|
149
|
+
# don't set :volume_status here as that should only be queried
|
150
|
+
mapping[:management_status] = 'detached'
|
151
|
+
mapping[:attempts] = nil
|
152
|
+
yield if block_given?
|
153
|
+
end
|
154
|
+
|
155
|
+
req.errback do |res|
|
156
|
+
unless InstanceState.value == 'stranded'
|
157
|
+
# volume could already be detaching or have been deleted
|
158
|
+
# which we can't see because of latency; go around again
|
159
|
+
# and check state of volume later.
|
160
|
+
Log.error("Failed to detach volume #{mapping[:volume_id]}: #{res}")
|
161
|
+
mapping[:attempts] ||= 0
|
162
|
+
mapping[:attempts] += 1
|
163
|
+
# retry indefinitely so long as core api instructs us to retry or else fail after max attempts.
|
164
|
+
if mapping[:attempts] >= VolumeManagement::MAX_VOLUME_ATTEMPTS
|
165
|
+
strand("Exceeded maximum of #{VolumeManagement::MAX_VOLUME_ATTEMPTS} attempts detaching volume #{mapping[:volume_id]} with error: #{res}")
|
166
|
+
else
|
167
|
+
yield if block_given?
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
req.run
|
173
|
+
end
|
174
|
+
|
175
|
+
# Attaches the planned volume given by its mapping.
|
176
|
+
#
|
177
|
+
# === Parameters
|
178
|
+
# mapping(Hash):: details of planned volume
|
179
|
+
def attach_planned_volume(mapping)
|
180
|
+
# preserve the initial list of disks/volumes before attachment for comparison later.
|
181
|
+
vm = RightScale::Platform.volume_manager
|
182
|
+
InstanceState.planned_volume_state.disks ||= vm.disks
|
183
|
+
InstanceState.planned_volume_state.volumes ||= vm.volumes
|
184
|
+
|
185
|
+
# attach.
|
186
|
+
payload = {:agent_identity => @agent_identity, :volume_id => mapping[:volume_id], :device_name => mapping[:device_name]}
|
187
|
+
Log.info("Attaching volume #{mapping[:volume_id]}.")
|
188
|
+
req = IdempotentRequest.new("/storage_valet/attach_volume", payload, :retry_delay => VolumeManagement::VOLUME_RETRY_SECONDS)
|
189
|
+
|
190
|
+
req.callback do |res|
|
191
|
+
# don't set :volume_status here as that should only be queried
|
192
|
+
mapping[:management_status] = 'attached'
|
193
|
+
mapping[:attempts] = nil
|
194
|
+
yield if block_given?
|
195
|
+
end
|
196
|
+
|
197
|
+
req.errback do |res|
|
198
|
+
# volume could already be attaching or have been deleted
|
199
|
+
# which we can't see because of latency; go around again
|
200
|
+
# and check state of volume later.
|
201
|
+
Log.error("Failed to attach volume #{mapping[:volume_id]}: #{res}")
|
202
|
+
mapping[:attempts] ||= 0
|
203
|
+
mapping[:attempts] += 1
|
204
|
+
# retry indefinitely so long as core api instructs us to retry or else fail after max attempts.
|
205
|
+
if mapping[:attempts] >= VolumeManagement::MAX_VOLUME_ATTEMPTS
|
206
|
+
strand("Exceeded maximum of #{VolumeManagement::MAX_VOLUME_ATTEMPTS} attempts attaching volume #{mapping[:volume_id]} with error: #{res}")
|
207
|
+
else
|
208
|
+
yield if block_given?
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
req.run
|
213
|
+
end
|
214
|
+
|
215
|
+
# Manages device assignment for volumes with considerations for formatting
|
216
|
+
# blank attached volumes.
|
217
|
+
#
|
218
|
+
# === Parameters
|
219
|
+
# mapping(Hash):: details of planned volume
|
220
|
+
def manage_volume_device_assignment(mapping)
|
221
|
+
|
222
|
+
# only managed volumes should be in an attached state ready for assignment.
|
223
|
+
unless 'attached' == mapping[:management_status]
|
224
|
+
raise VolumeManagement::UnexpectedState.new("The volume #{mapping[:volume_id]} was in an unexpected managed state: #{mapping.inspect}")
|
225
|
+
end
|
226
|
+
|
227
|
+
# check for changes in disks.
|
228
|
+
last_disks = InstanceState.planned_volume_state.disks
|
229
|
+
last_volumes = InstanceState.planned_volume_state.volumes
|
230
|
+
vm = RightScale::Platform.volume_manager
|
231
|
+
current_disks = vm.disks
|
232
|
+
current_volumes = vm.volumes
|
233
|
+
|
234
|
+
# correctly managing device assignment requires expecting precise changes
|
235
|
+
# to disks and volumes. any deviation from this requires a retry.
|
236
|
+
succeeded = false
|
237
|
+
if new_disk = find_distinct_item(current_disks, last_disks, :index)
|
238
|
+
# if the new disk as no partitions, then we will format and assign device.
|
239
|
+
if vm.partitions(new_disk[:index]).empty?
|
240
|
+
# FIX: ignore multiple mount points for simplicity and only only create
|
241
|
+
# a single primary partition for the first mount point.
|
242
|
+
# if we had the UI for it, then the user would probably specify
|
243
|
+
# partition sizes as a percentage of disk size and associate those with
|
244
|
+
# mount points formatted optionally specifying file system, label, etc.
|
245
|
+
@audit.append_info("Creating primary partition and formatting \"#{mapping[:mount_points].first}\".")
|
246
|
+
vm.format_disk(new_disk[:index], mapping[:mount_points].first)
|
247
|
+
succeeded = true
|
248
|
+
else
|
249
|
+
# FIX: ignoring multiple existing partitiions on a disk (which should
|
250
|
+
# result in multiple new volumes appearing when the disk comes online)
|
251
|
+
# for simplicity until we have a UI supporting multiple mount points.
|
252
|
+
@audit.append_info("Preparing \"#{mapping[:mount_points].first}\" for use.")
|
253
|
+
new_volume = find_distinct_item(current_volumes, last_volumes, :device)
|
254
|
+
unless new_volume
|
255
|
+
vm.online_disk(new_disk[:index])
|
256
|
+
current_volumes = vm.volumes
|
257
|
+
new_volume = find_distinct_item(current_volumes, last_volumes, :device)
|
258
|
+
end
|
259
|
+
if new_volume
|
260
|
+
# prefer selection by existing device because it is more reliable in Windows 2003 case.
|
261
|
+
unless new_volume[:device] && (0 == new_volume[:device].casecmp(mapping[:mount_points].first))
|
262
|
+
device_or_index_to_select = new_volume[:device] || new_volume[:index]
|
263
|
+
vm.assign_device(device_or_index_to_select, mapping[:mount_points].first)
|
264
|
+
end
|
265
|
+
succeeded = true
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# retry only if still not assigned.
|
271
|
+
if succeeded
|
272
|
+
# volume is (finally!) assigned to correct device name.
|
273
|
+
mapping[:management_status] = 'assigned'
|
274
|
+
mapping[:attempts] = nil
|
275
|
+
|
276
|
+
# reset cached volumes/disks for next attempt (to attach), if any.
|
277
|
+
InstanceState.planned_volume_state.disks = nil
|
278
|
+
InstanceState.planned_volume_state.volumes = nil
|
279
|
+
|
280
|
+
# continue.
|
281
|
+
yield if block_given?
|
282
|
+
else
|
283
|
+
mapping[:attempts] ||= 0
|
284
|
+
mapping[:attempts] += 1
|
285
|
+
if mapping[:attempts] >= VolumeManagement::MAX_VOLUME_ATTEMPTS
|
286
|
+
strand("Exceeded maximum of #{VolumeManagement::MAX_VOLUME_ATTEMPTS} attempts waiting for volume #{mapping[:volume_id]} to be in a managable state.")
|
287
|
+
else
|
288
|
+
yield if block_given?
|
289
|
+
end
|
290
|
+
end
|
291
|
+
rescue Exception => e
|
292
|
+
strand(e)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Determines a single, unique item (hash) by given key in the current
|
296
|
+
# list which does not appear in the last list, if any. finds nothing if
|
297
|
+
# multiple new items appear. This is useful for inspecting lists of
|
298
|
+
# disks/volumes to determine when a new item appears.
|
299
|
+
#
|
300
|
+
# === Parameter
|
301
|
+
# current_list(Array):: current list
|
302
|
+
# last_list(Array):: last list
|
303
|
+
# key(Symbol):: key used to uniquely identify items
|
304
|
+
#
|
305
|
+
# === Return
|
306
|
+
# result(Hash):: item in current list which does not appear in last list or nil
|
307
|
+
def find_distinct_item(current_list, last_list, key)
|
308
|
+
if current_list.size == last_list.size + 1
|
309
|
+
unique_values = current_list.map { |item| item[key] } - last_list.map { |item| item[key] }
|
310
|
+
if unique_values.size == 1
|
311
|
+
unique_value = unique_values[0]
|
312
|
+
return current_list.find { |item| item[key] == unique_value }
|
313
|
+
end
|
314
|
+
end
|
315
|
+
return nil
|
316
|
+
end
|
317
|
+
|
318
|
+
# Determines if the given volume is in an attached (or attaching) state and
|
319
|
+
# not yet managed (in a managed state). Note that we can assign drive letters
|
320
|
+
# to 'attaching' volumes because they may appear locally before the repository
|
321
|
+
# can update their status to 'attached'.
|
322
|
+
#
|
323
|
+
# === Return
|
324
|
+
# result(Boolean):: true if volume is attached and unassigned
|
325
|
+
def is_unmanaged_attached_volume?(mapping)
|
326
|
+
case mapping[:volume_status]
|
327
|
+
when 'attached', 'attaching'
|
328
|
+
mapping[:management_status].nil?
|
329
|
+
else
|
330
|
+
false
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# Determines if the given volume is detaching regardless of whether detachment
|
335
|
+
# was managed (will adopt any detatching volumes).
|
336
|
+
#
|
337
|
+
# === Return
|
338
|
+
# result(Boolean):: true if volume is detaching
|
339
|
+
def is_detaching_volume?(mapping)
|
340
|
+
case mapping[:volume_status]
|
341
|
+
when 'detaching'
|
342
|
+
true
|
343
|
+
when 'attached', 'attaching'
|
344
|
+
# also detaching if we have successfully requested detachment but volume
|
345
|
+
# status does not yet reflect this change.
|
346
|
+
'detached' == mapping[:management_status]
|
347
|
+
else
|
348
|
+
false
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Determines if the given volume is detached regardless of whether detachment
|
353
|
+
# was managed (will adopt any detatched volumes).
|
354
|
+
#
|
355
|
+
# === Return
|
356
|
+
# result(Boolean):: true if volume is detaching
|
357
|
+
def is_detached_volume?(mapping)
|
358
|
+
# detached by volume status unless we have successfully requested attachment
|
359
|
+
# and volume status does not yet reflect this change. an unmanaged volume
|
360
|
+
# can also be detached.
|
361
|
+
return 'detached' == mapping[:volume_status] && 'attached' != mapping[:management_status]
|
362
|
+
end
|
363
|
+
|
364
|
+
# Determines if the given volume is attaching and managed (indicating that we
|
365
|
+
# requested attachment).
|
366
|
+
#
|
367
|
+
# === Return
|
368
|
+
# result(Boolean):: true if volume is managed and attaching
|
369
|
+
def is_managed_attaching_volume?(mapping)
|
370
|
+
return 'attached' == mapping[:management_status] && 'attached' != mapping[:volume_status]
|
371
|
+
end
|
372
|
+
|
373
|
+
# Determines if the given volume is attached and managed (indicating that we
|
374
|
+
# requested attachment) but not yet assigned its device name.
|
375
|
+
#
|
376
|
+
# === Return
|
377
|
+
# result(Boolean):: true if volume is managed, attached and unassigned
|
378
|
+
def is_managed_attached_unassigned_volume?(mapping)
|
379
|
+
return 'attached' == mapping[:volume_status] && 'attached' == mapping[:management_status]
|
380
|
+
end
|
381
|
+
|
382
|
+
# Determines if the given volume is in an unmanageable state (such as
|
383
|
+
# 'deleted').
|
384
|
+
#
|
385
|
+
# === Return
|
386
|
+
# result(Boolean):: true if volume is managed but unrecoverable
|
387
|
+
def is_unmanageable_volume?(mapping)
|
388
|
+
case mapping[:volume_status]
|
389
|
+
when 'attached', 'attaching', 'detached', 'detaching'
|
390
|
+
false
|
391
|
+
else
|
392
|
+
true
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# Merges mappings from query with any last known mappings which may have a
|
397
|
+
# locally persisted state which needs to be evaluated.
|
398
|
+
#
|
399
|
+
# === Parameters
|
400
|
+
# last_mappings(Array):: previously merged mappings or empty
|
401
|
+
# current_mappings(Array):: current unmerged mappings or empty
|
402
|
+
#
|
403
|
+
# === Returns
|
404
|
+
# results(Array):: array of hashes representing merged mappings
|
405
|
+
def merge_planned_volume_mappings(last_mappings, current_planned_volumes)
|
406
|
+
results = []
|
407
|
+
vm = RightScale::Platform.volume_manager
|
408
|
+
|
409
|
+
# merge latest mappings with last mappings, if any.
|
410
|
+
current_planned_volumes.each do |planned_volume|
|
411
|
+
raise VolumeManagement::InvalidResponse.new("Reponse for volume mapping was invalid: #{mapping.inspect}") unless planned_volume.is_valid?
|
412
|
+
if mount_point = planned_volume.mount_points.find { |mount_point| false == vm.is_attachable_volume_path?(mount_point) }
|
413
|
+
raise VolumeManagement::UnsupportedMountPoint.new("Cannot mount a volume using \"#{mount_point}\".")
|
414
|
+
end
|
415
|
+
|
416
|
+
mapping = {:volume_id => planned_volume.volume_id,
|
417
|
+
:device_name => planned_volume.device_name,
|
418
|
+
:volume_status => planned_volume.volume_status,
|
419
|
+
:mount_points => planned_volume.mount_points.dup}
|
420
|
+
if last_mapping = last_mappings.find { |last_mapping| last_mapping[:volume_id] == mapping[:volume_id] }
|
421
|
+
# if device name or mount point(s) have changed then we must start
|
422
|
+
# over (we can't prevent the user from doing this).
|
423
|
+
if last_mapping[:device_name] != mapping[:device_name] || last_mapping[:mount_points] != mapping[:mount_points]
|
424
|
+
last_mapping[:device_name] = mapping[:device_name]
|
425
|
+
last_mapping[:mount_points] = mapping[:mount_points].dup
|
426
|
+
last_mapping[:management_status] = nil
|
427
|
+
end
|
428
|
+
last_mapping[:volume_status] = mapping[:volume_status]
|
429
|
+
mapping = last_mapping
|
430
|
+
end
|
431
|
+
results << mapping
|
432
|
+
end
|
433
|
+
|
434
|
+
# preserve any last mappings which do not appear in current mappings by
|
435
|
+
# assuming that they are 'detached' to support a limitation of the initial
|
436
|
+
# query implementation.
|
437
|
+
last_mappings.each do |last_mapping|
|
438
|
+
mapping = results.find { |mapping| mapping[:volume_id] == last_mapping[:volume_id] }
|
439
|
+
unless mapping
|
440
|
+
last_mapping[:volume_status] = 'detached'
|
441
|
+
results << last_mapping
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
return results
|
446
|
+
end
|
447
|
+
|
448
|
+
end
|
449
|
+
|
450
|
+
end
|
data/lib/instance.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2011 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
INSTANCE_BASE_DIR = File.join(File.dirname(__FILE__), 'instance')
|
24
|
+
|
25
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'agent_config'))
|
26
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'audit_cook_stub'))
|
27
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'audit_proxy'))
|
28
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'bundle_queue'))
|
29
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'single_thread_bundle_queue'))
|
30
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'multi_thread_bundle_queue'))
|
31
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'cook', 'cook_state'))
|
32
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'downloader'))
|
33
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'duplicable'))
|
34
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'exceptions'))
|
35
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'executable_sequence_proxy'))
|
36
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'instance_commands'))
|
37
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'instance_state'))
|
38
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'login_user_manager'))
|
39
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'login_manager'))
|
40
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'message_encoder'))
|
41
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'operation_context'))
|
42
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'options_bag'))
|
43
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'payload_formatter'))
|
44
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'policy'))
|
45
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'policy_audit'))
|
46
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'policy_manager'))
|
47
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'reenroll_manager'))
|
48
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'right_scripts_cookbook'))
|
49
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'shutdown_request'))
|
50
|
+
require File.normalize_path(File.join(INSTANCE_BASE_DIR, 'volume_management'))
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'right_agent/exceptions'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2009-2011 RightScale Inc
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
# a copy of this software and associated documentation files (the
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
# the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
|
25
|
+
module Apt
|
26
|
+
|
27
|
+
module Ubuntu
|
28
|
+
SUPPORTED_REPOS = ['hardy', 'intrepid', 'jaunty', 'karmic', 'lucid', 'maverick', 'precise', 'quantal']
|
29
|
+
|
30
|
+
# The different generate classes will always generate an exception ("string") if there's anything that went wrong. If no exception, things went well.
|
31
|
+
SUPPORTED_REPOS.each do |c|
|
32
|
+
module_eval <<-EOS
|
33
|
+
class #{c.capitalize}
|
34
|
+
def self.generate(description, base_urls, frozen_date="latest")
|
35
|
+
opts = { :repo_filename => "rightscale",
|
36
|
+
:repo_name => "default",
|
37
|
+
:description => description,
|
38
|
+
:codename => '#{c}',
|
39
|
+
:base_urls => base_urls,
|
40
|
+
:frozen_date => frozen_date,
|
41
|
+
:enabled => true }
|
42
|
+
opts[:frozen_date] = frozen_date || "latest" # Optional frozen date
|
43
|
+
Apt::Ubuntu::abstract_generate(opts)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
EOS
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.path_to_sources_list
|
50
|
+
"/etc/apt/sources.list.d"
|
51
|
+
end
|
52
|
+
|
53
|
+
############## INTERNAL FUNCTIONS #######################################################
|
54
|
+
def self.abstract_generate(params)
|
55
|
+
return unless ::RightScale::Platform.linux? && ::RightScale::Platform.ubuntu?
|
56
|
+
|
57
|
+
opts = { :enabled => true, :frozen_date => "latest"}
|
58
|
+
opts.merge!(params)
|
59
|
+
raise ArgumentError.new("missing parameters to generate file!") unless opts[:repo_filename] &&
|
60
|
+
opts[:repo_name] &&
|
61
|
+
opts[:base_urls] &&
|
62
|
+
opts[:frozen_date] &&
|
63
|
+
opts[:enabled]
|
64
|
+
|
65
|
+
return unless opts[:enabled]
|
66
|
+
|
67
|
+
target = opts[:codename].downcase
|
68
|
+
codename = ::RightScale::Platform.codename.downcase
|
69
|
+
|
70
|
+
raise ::RightScale::Exceptions::PlatformError, "Unsupported Ubuntu release #{codename}" unless SUPPORTED_REPOS.include?(codename)
|
71
|
+
raise ::RightScale::Exceptions::PlatformError, "Wrong release; repo is for #{target}, we are #{codename}" unless target == codename
|
72
|
+
|
73
|
+
FileUtils.mkdir_p(Apt::Ubuntu::path_to_sources_list)
|
74
|
+
|
75
|
+
if opts[:frozen_date] != 'latest'
|
76
|
+
x = Date.parse(opts[:frozen_date]).to_s
|
77
|
+
x.gsub!(/-/,"/")
|
78
|
+
opts[:frozen_date] = x
|
79
|
+
end
|
80
|
+
|
81
|
+
mirror_list = opts[:base_urls].map do |bu|
|
82
|
+
bu +='/' unless bu[-1..-1] == '/' # ensure the base url is terminated with a '/'
|
83
|
+
bu + opts[:frozen_date]
|
84
|
+
end
|
85
|
+
config_body = ""
|
86
|
+
mirror_list.each do |mirror_url|
|
87
|
+
config_body += <<END
|
88
|
+
deb #{mirror_url} #{codename} main restricted multiverse universe
|
89
|
+
deb #{mirror_url} #{codename}-updates main restricted multiverse universe
|
90
|
+
deb #{mirror_url} #{codename}-security main restricted multiverse universe
|
91
|
+
|
92
|
+
END
|
93
|
+
end
|
94
|
+
|
95
|
+
target_filename = "#{Apt::Ubuntu::path_to_sources_list}/#{opts[:repo_filename]}.sources.list"
|
96
|
+
FileUtils.rm_f(target_filename) if File.exists?(target_filename)
|
97
|
+
File.open(target_filename,'w') { |f| f.write(config_body) }
|
98
|
+
FileUtils.mv("/etc/apt/sources.list", "/etc/apt/sources.list.ORIG") if File.exists?("/etc/apt/sources.list")
|
99
|
+
|
100
|
+
mirror_list
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Examples of usage...
|
106
|
+
#Apt::Ubuntu::Hardy.generate("Hardy", ["http://a.com/ubuntu_daily"], "20081010")
|