vagrant-azure 1.0.5 → 1.1.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.
- checksums.yaml +4 -4
- data/.gitignore +19 -15
- data/CHANGELOG.md +24 -24
- data/Gemfile +20 -15
- data/LICENSE +4 -4
- data/README.md +189 -125
- data/Rakefile +15 -14
- data/lib/vagrant-azure.rb +31 -33
- data/lib/vagrant-azure/action.rb +267 -243
- data/lib/vagrant-azure/action/connect_azure.rb +49 -46
- data/lib/vagrant-azure/action/os_type.rb +34 -0
- data/lib/vagrant-azure/action/powershell_run.rb +28 -0
- data/lib/vagrant-azure/action/provision.rb +42 -49
- data/lib/vagrant-azure/action/rdp.rb +63 -62
- data/lib/vagrant-azure/action/read_ssh_info.rb +54 -51
- data/lib/vagrant-azure/action/read_state.rb +47 -46
- data/lib/vagrant-azure/action/read_winrm_info.rb +57 -0
- data/lib/vagrant-azure/action/restart_vm.rb +28 -27
- data/lib/vagrant-azure/action/run_instance.rb +123 -115
- data/lib/vagrant-azure/action/start_instance.rb +35 -35
- data/lib/vagrant-azure/action/stop_instance.rb +42 -38
- data/lib/vagrant-azure/action/sync_folders.rb +64 -63
- data/lib/vagrant-azure/action/terminate_instance.rb +34 -34
- data/lib/vagrant-azure/action/vagrant_azure_service.rb +44 -43
- data/lib/vagrant-azure/action/wait_for_communicate.rb +39 -38
- data/lib/vagrant-azure/action/wait_for_state.rb +50 -49
- data/lib/vagrant-azure/capabilities/winrm.rb +12 -0
- data/lib/vagrant-azure/command/powershell.rb +43 -0
- data/lib/vagrant-azure/command/rdp.rb +24 -0
- data/lib/vagrant-azure/config.rb +158 -147
- data/lib/vagrant-azure/driver.rb +48 -84
- data/lib/vagrant-azure/errors.rb +28 -27
- data/lib/vagrant-azure/monkey_patch/azure.rb +46 -0
- data/lib/vagrant-azure/monkey_patch/winrm.rb +77 -0
- data/lib/vagrant-azure/plugin.rb +102 -91
- data/lib/vagrant-azure/provider.rb +74 -70
- data/lib/vagrant-azure/provisioner/chef-solo.rb +178 -177
- data/lib/vagrant-azure/provisioner/puppet.rb +116 -115
- data/lib/vagrant-azure/version.rb +11 -10
- data/locales/en.yml +37 -37
- data/templates/provisioners/chef-solo/solo.erb +51 -51
- data/vagrant-azure.gemspec +59 -58
- metadata +48 -38
- data/lib/vagrant-azure/command/rdp/command.rb +0 -21
- data/lib/vagrant-azure/communication/powershell.rb +0 -41
- data/lib/vagrant-azure/monkey_patch/machine.rb +0 -22
- data/lib/vagrant-azure/provisioner/shell.rb +0 -83
- data/lib/vagrant-azure/scripts/check_winrm.ps1 +0 -47
- data/lib/vagrant-azure/scripts/export_vm.ps1 +0 -31
- data/lib/vagrant-azure/scripts/file_sync.ps1 +0 -145
- data/lib/vagrant-azure/scripts/host_info.ps1 +0 -25
- data/lib/vagrant-azure/scripts/hyperv_manager.ps1 +0 -36
- data/lib/vagrant-azure/scripts/run_in_remote.ps1 +0 -32
- data/lib/vagrant-azure/scripts/upload_file.ps1 +0 -95
- data/lib/vagrant-azure/scripts/utils/create_session.ps1 +0 -34
- data/lib/vagrant-azure/scripts/utils/write_messages.ps1 +0 -18
data/Rakefile
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
#--------------------------------------------------------------------------
|
2
|
-
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
-
# All Rights Reserved.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require '
|
8
|
-
|
9
|
-
|
10
|
-
$
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
1
|
+
#--------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache License, Version 2.0.
|
4
|
+
# See License.txt in the project root for license information.
|
5
|
+
#--------------------------------------------------------------------------
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'bundler/setup'
|
9
|
+
|
10
|
+
$stdout.sync = true
|
11
|
+
$stderr.sync = true
|
12
|
+
|
13
|
+
Dir.chdir(File.expand_path('../', __FILE__))
|
14
|
+
|
15
|
+
Bundler::GemHelper.install_tasks
|
data/lib/vagrant-azure.rb
CHANGED
@@ -1,33 +1,31 @@
|
|
1
|
-
#--------------------------------------------------------------------------
|
2
|
-
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
-
# All Rights Reserved.
|
4
|
-
|
5
|
-
|
6
|
-
require '
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
autoload :
|
13
|
-
autoload :
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
require lib_path.join('
|
19
|
-
require lib_path.join('
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
1
|
+
#--------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache License, Version 2.0.
|
4
|
+
# See License.txt in the project root for license information.
|
5
|
+
#--------------------------------------------------------------------------
|
6
|
+
require 'pathname'
|
7
|
+
require 'vagrant-azure/plugin'
|
8
|
+
|
9
|
+
module VagrantPlugins
|
10
|
+
module WinAzure
|
11
|
+
lib_path = Pathname.new(File.expand_path('../vagrant-azure', __FILE__))
|
12
|
+
autoload :Action, lib_path.join('action')
|
13
|
+
autoload :Errors, lib_path.join('errors')
|
14
|
+
autoload :Driver, lib_path.join('driver')
|
15
|
+
|
16
|
+
Vagrant.plugin('2').manager.communicators[:winrm]
|
17
|
+
require 'kconv'
|
18
|
+
require lib_path.join('monkey_patch/azure')
|
19
|
+
require lib_path.join('monkey_patch/winrm')
|
20
|
+
|
21
|
+
CLOUD_SERVICE_SEMAPHORE = Mutex.new
|
22
|
+
|
23
|
+
|
24
|
+
# This returns the path to the source of this plugin.
|
25
|
+
#
|
26
|
+
# @return [Pathname]
|
27
|
+
def self.source_root
|
28
|
+
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/vagrant-azure/action.rb
CHANGED
@@ -1,243 +1,267 @@
|
|
1
|
-
#--------------------------------------------------------------------------
|
2
|
-
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
-
# All Rights Reserved.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
b.use
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
b2.use
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
b2.use
|
46
|
-
b2.use
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
b.use
|
63
|
-
b.use
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
b.use
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def self.
|
144
|
-
Vagrant::Action::Builder.new.tap do |b|
|
145
|
-
b.use
|
146
|
-
b.use
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
b.use
|
203
|
-
b.use
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
end
|
1
|
+
#--------------------------------------------------------------------------
|
2
|
+
# Copyright (c) Microsoft Open Technologies, Inc.
|
3
|
+
# All Rights Reserved. Licensed under the Apache License, Version 2.0.
|
4
|
+
# See License.txt in the project root for license information.
|
5
|
+
#--------------------------------------------------------------------------
|
6
|
+
require 'pathname'
|
7
|
+
|
8
|
+
require 'vagrant/action/builder'
|
9
|
+
|
10
|
+
module VagrantPlugins
|
11
|
+
module WinAzure
|
12
|
+
module Action
|
13
|
+
# Include the built-in modules so we can use them as top-level things.
|
14
|
+
include Vagrant::Action::Builtin
|
15
|
+
|
16
|
+
# This action is called to halt the remote machine.
|
17
|
+
def self.action_halt
|
18
|
+
Vagrant::Action::Builder.new.tap do |b|
|
19
|
+
b.use ConfigValidate
|
20
|
+
b.use Call, IsState, :NotCreated do |env, b2|
|
21
|
+
if env[:result]
|
22
|
+
b2.use Message, I18n.t('vagrant_azure.not_created')
|
23
|
+
next
|
24
|
+
end
|
25
|
+
|
26
|
+
b2.use ConnectAzure
|
27
|
+
b2.use StopInstance
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# This action is called to terminate the remote machine.
|
33
|
+
def self.action_destroy
|
34
|
+
Vagrant::Action::Builder.new.tap do |b|
|
35
|
+
b.use Call, DestroyConfirm do |env, b2|
|
36
|
+
if env[:result]
|
37
|
+
b2.use ConfigValidate
|
38
|
+
b2.use Call, IsState, :NotCreated do |env2, b3|
|
39
|
+
if env2[:result]
|
40
|
+
b3.use Message, I18n.t('vagrant_azure.not_created')
|
41
|
+
next
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
b2.use ConnectAzure
|
46
|
+
b2.use TerminateInstance
|
47
|
+
b2.use ProvisionerCleanup if defined?(ProvisionerCleanup)
|
48
|
+
else
|
49
|
+
env[:machine].id =~ /@/
|
50
|
+
b2.use Message, I18n.t(
|
51
|
+
'vagrant_azure.will_not_destroy',
|
52
|
+
:name => $`
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# This action is called when `vagrant provision` is called.
|
60
|
+
def self.action_provision
|
61
|
+
Vagrant::Action::Builder.new.tap do |b|
|
62
|
+
b.use ConnectAzure
|
63
|
+
b.use ConfigValidate
|
64
|
+
b.use OSType
|
65
|
+
b.use ReadWinrmInfo
|
66
|
+
b.use Call, IsState, :NotCreated do |env, b2|
|
67
|
+
if env[:result]
|
68
|
+
b2.use Message, I18n.t('vagrant_azure.not_created')
|
69
|
+
next
|
70
|
+
end
|
71
|
+
|
72
|
+
b2.use Provision
|
73
|
+
b2.use SyncFolders
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# This action is called to read the SSH info of the machine. The
|
79
|
+
# resulting state is expected to be put into the `:machine_ssh_info` key.
|
80
|
+
def self.action_read_ssh_info
|
81
|
+
Vagrant::Action::Builder.new.tap do |b|
|
82
|
+
b.use ConfigValidate
|
83
|
+
b.use ConnectAzure
|
84
|
+
b.use ReadSSHInfo, 22
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.action_read_rdp_info
|
89
|
+
Vagrant::Action::Builder.new.tap do |b|
|
90
|
+
b.use ConfigValidate
|
91
|
+
b.use ConnectAzure
|
92
|
+
b.use ReadSSHInfo, 3389
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.action_read_winrm_info
|
97
|
+
Vagrant::Action::Builder.new.tap do |b|
|
98
|
+
b.use ConfigValidate
|
99
|
+
b.use ConnectAzure
|
100
|
+
b.use OSType
|
101
|
+
b.use ReadWinrmInfo
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.action_read_state
|
106
|
+
Vagrant::Action::Builder.new.tap do |b|
|
107
|
+
b.use ConfigValidate
|
108
|
+
b.use ConnectAzure
|
109
|
+
b.use ReadState
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# This action is called to SSH into the machine
|
114
|
+
def self.action_ssh
|
115
|
+
Vagrant::Action::Builder.new.tap do |b|
|
116
|
+
b.use ConnectAzure
|
117
|
+
b.use ConfigValidate
|
118
|
+
b.use Call, IsState, :NotCreated do |env, b2|
|
119
|
+
if env[:result]
|
120
|
+
b2.use Message, I18n.t('vagrant_azure.not_created')
|
121
|
+
next
|
122
|
+
end
|
123
|
+
|
124
|
+
b2.use SSHExec
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.action_powershell_run
|
130
|
+
Vagrant::Action::Builder.new.tap do |b|
|
131
|
+
b.use action_read_winrm_info
|
132
|
+
b.use Call, IsState, :NotCreated do |env, b2|
|
133
|
+
if env[:result]
|
134
|
+
b2.use Message, I18n.t('vagrant_azure.not_created')
|
135
|
+
next
|
136
|
+
end
|
137
|
+
|
138
|
+
b2.use PowerShellRun
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.action_rdp
|
144
|
+
Vagrant::Action::Builder.new.tap do |b|
|
145
|
+
b.use ConnectAzure
|
146
|
+
b.use ConfigValidate
|
147
|
+
b.use Call, IsState, :NotCreated do |env1, b1|
|
148
|
+
if env1[:result]
|
149
|
+
b1.use Message, I18n.t('vagrant_azure.not_created')
|
150
|
+
next
|
151
|
+
end
|
152
|
+
|
153
|
+
b1.use Call, IsState, :ReadyRole do |env2, b2|
|
154
|
+
if !env2[:result]
|
155
|
+
b2.use Message, I18n.t('vagrant_azure.rdp_not_ready')
|
156
|
+
next
|
157
|
+
end
|
158
|
+
|
159
|
+
b2.use Rdp
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.action_ssh_run
|
166
|
+
Vagrant::Action::Builder.new.tap do |b|
|
167
|
+
b.use ConnectAzure
|
168
|
+
b.use ConfigValidate
|
169
|
+
b.use Call, IsState, :NotCreated do |env, b2|
|
170
|
+
if env[:result]
|
171
|
+
b2.use Message, I18n.t('vagrant_azure.not_created')
|
172
|
+
next
|
173
|
+
end
|
174
|
+
|
175
|
+
b2.use SSHRun
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.action_prepare_boot
|
181
|
+
Vagrant::Action::Builder.new.tap do |b|
|
182
|
+
b.use Call, WaitForState, :ReadyRole do |env, b1|
|
183
|
+
if env[:result]
|
184
|
+
env[:machine].id =~ /@/
|
185
|
+
b1.use Message, I18n.t(
|
186
|
+
'vagrant_azure.vm_started', :name => $`
|
187
|
+
)
|
188
|
+
b1.use action_read_winrm_info
|
189
|
+
b1.use WaitForCommunicate
|
190
|
+
b1.use action_provision
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# This action is called to bring the box up from nothing
|
197
|
+
def self.action_up
|
198
|
+
Vagrant::Action::Builder.new.tap do |b|
|
199
|
+
b.use HandleBox
|
200
|
+
b.use ConfigValidate
|
201
|
+
b.use ConnectAzure
|
202
|
+
b.use OSType
|
203
|
+
b.use Call, IsState, :NotCreated do |env1, b1|
|
204
|
+
if !env1[:result]
|
205
|
+
b1.use Call, IsState, :StoppedDeallocated do |env2, b2|
|
206
|
+
if env2[:result]
|
207
|
+
b2.use StartInstance # start this instance again
|
208
|
+
b2.use action_prepare_boot
|
209
|
+
else
|
210
|
+
b2.use Message, I18n.t(
|
211
|
+
'vagrant_azure.already_status', :status => 'created'
|
212
|
+
)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
else
|
216
|
+
b1.use RunInstance # Launch a new instance
|
217
|
+
b1.use action_prepare_boot
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.action_reload
|
224
|
+
Vagrant::Action::Builder.new.tap do |b|
|
225
|
+
b.use ConfigValidate
|
226
|
+
b.use ConnectAzure
|
227
|
+
b.use Call, IsState, :NotCreated do |env, b2|
|
228
|
+
if env[:result]
|
229
|
+
b2.use Message, I18n.t('vagrant_azure.not_created')
|
230
|
+
next
|
231
|
+
end
|
232
|
+
|
233
|
+
b2.use action_halt
|
234
|
+
b2.use Call, WaitForState, :StoppedDeallocated do |env2, b3|
|
235
|
+
if env2[:result]
|
236
|
+
env2[:machine].id =~ /@/
|
237
|
+
b3.use Message, I18n.t('vagrant_azure.vm_stopped', name: $`)
|
238
|
+
b3.use action_up
|
239
|
+
else
|
240
|
+
b3.use Message, 'Not able to stop the machine. Please retry.'
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# The autoload farm
|
248
|
+
action_root = Pathname.new(File.expand_path('../action', __FILE__))
|
249
|
+
autoload :ConnectAzure, action_root.join('connect_azure')
|
250
|
+
autoload :Rdp, action_root.join('rdp')
|
251
|
+
autoload :ReadSSHInfo, action_root.join('read_ssh_info')
|
252
|
+
autoload :ReadWinrmInfo, action_root.join('read_winrm_info')
|
253
|
+
autoload :PowerShellRun, action_root.join('powershell_run')
|
254
|
+
autoload :Provision, action_root.join('provision')
|
255
|
+
autoload :OSType, action_root.join('os_type')
|
256
|
+
autoload :ReadState, action_root.join('read_state')
|
257
|
+
autoload :RestartVM, action_root.join('restart_vm')
|
258
|
+
autoload :RunInstance, action_root.join('run_instance')
|
259
|
+
autoload :StartInstance, action_root.join('start_instance')
|
260
|
+
autoload :StopInstance, action_root.join('stop_instance')
|
261
|
+
autoload :SyncFolders, action_root.join('sync_folders')
|
262
|
+
autoload :TerminateInstance, action_root.join('terminate_instance')
|
263
|
+
autoload :WaitForState, action_root.join('wait_for_state')
|
264
|
+
autoload :WaitForCommunicate, action_root.join('wait_for_communicate')
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|