24
24
25
25
import javax .inject .Inject ;
26
26
27
+ import com .cloud .storage .VMTemplateStorageResourceAssoc ;
28
+ import com .cloud .storage .download .DownloadListener ;
29
+ import com .cloud .utils .exception .CloudRuntimeException ;
27
30
import org .apache .cloudstack .engine .subsystem .api .storage .CopyCommandResult ;
28
31
import org .apache .cloudstack .engine .subsystem .api .storage .DataMotionService ;
29
32
import org .apache .cloudstack .engine .subsystem .api .storage .DataObject ;
@@ -118,26 +121,21 @@ public AsyncCallFuture<DataObjectResult> migrateData(DataObject srcDataObject, D
118
121
}
119
122
} else if (srcDataObject instanceof TemplateInfo && templateChain != null && templateChain .containsKey (srcDataObject )) {
120
123
for (TemplateInfo templateInfo : templateChain .get (srcDataObject ).first ()) {
124
+ if (templateIsOnDestination (templateInfo , destDatastore )) {
125
+ res .setResult ("Template already exists on destination." );
126
+ res .setSuccess (true );
127
+ logger .debug ("Deleting template {} from source data store [{}]." , srcDataObject .getTO ().toString (),
128
+ srcDataObject .getDataStore ().getTO ().toString ());
129
+ srcDataObject .getDataStore ().delete (srcDataObject );
130
+ future .complete (res );
131
+ continue ;
132
+ }
121
133
destDataObject = destDatastore .create (templateInfo );
122
134
templateInfo .processEvent (ObjectInDataStoreStateMachine .Event .MigrateDataRequested );
123
135
destDataObject .processEvent (ObjectInDataStoreStateMachine .Event .MigrateDataRequested );
124
136
migrateJob (future , templateInfo , destDataObject , destDatastore );
125
137
}
126
- }
127
- else {
128
- // Check if template in destination store, if yes, do not proceed
129
- if (srcDataObject instanceof TemplateInfo ) {
130
- logger .debug ("Checking if template present at destination" );
131
- TemplateDataStoreVO templateStoreVO = templateStoreDao .findByStoreTemplate (destDatastore .getId (), srcDataObject .getId ());
132
- if (templateStoreVO != null ) {
133
- String msg = "Template already exists in destination store" ;
134
- logger .debug (msg );
135
- res .setResult (msg );
136
- res .setSuccess (true );
137
- future .complete (res );
138
- return future ;
139
- }
140
- }
138
+ } else {
141
139
destDataObject = destDatastore .create (srcDataObject );
142
140
srcDataObject .processEvent (ObjectInDataStoreStateMachine .Event .MigrateDataRequested );
143
141
destDataObject .processEvent (ObjectInDataStoreStateMachine .Event .MigrateDataRequested );
@@ -160,6 +158,69 @@ public AsyncCallFuture<DataObjectResult> migrateData(DataObject srcDataObject, D
160
158
return future ;
161
159
}
162
160
161
+ /**
162
+ * Returns a boolean indicating whether a template is ready on the provided data store. If the template is being downloaded,
163
+ * waits until the download finishes.
164
+ * @param srcDataObject the template.
165
+ * @param destDatastore the data store.
166
+ */
167
+ protected boolean templateIsOnDestination (DataObject srcDataObject , DataStore destDatastore ) {
168
+ if (!(srcDataObject instanceof TemplateInfo )) {
169
+ return false ;
170
+ }
171
+
172
+ String templateAsString = srcDataObject .getTO ().toString ();
173
+ String destDatastoreAsString = destDatastore .getTO ().toString ();
174
+ TemplateDataStoreVO templateStoreVO ;
175
+
176
+ long timer = getTemplateDownloadTimeout ();
177
+ long msToSleep = 10000L ;
178
+ int previousDownloadPercentage = -1 ;
179
+
180
+ while (true ) {
181
+ templateStoreVO = templateStoreDao .findByStoreTemplate (destDatastore .getId (), srcDataObject .getId ());
182
+ if (templateStoreVO == null ) {
183
+ logger .debug ("{} is not present at destination [{}]." , templateAsString , destDatastoreAsString );
184
+ return false ;
185
+ }
186
+ VMTemplateStorageResourceAssoc .Status downloadState = templateStoreVO .getDownloadState ();
187
+ if (downloadState == null || !VMTemplateStorageResourceAssoc .PENDING_DOWNLOAD_STATES .contains (downloadState )) {
188
+ break ;
189
+ }
190
+ if (previousDownloadPercentage == templateStoreVO .getDownloadPercent ()) {
191
+ timer -= msToSleep ;
192
+ } else {
193
+ timer = getTemplateDownloadTimeout ();
194
+ }
195
+ if (timer <= 0 ) {
196
+ throw new CloudRuntimeException (String .format ("Timeout while waiting for %s to be downloaded to image store [%s]. " +
197
+ "The download percentage has not changed for %d milliseconds." , templateAsString , destDatastoreAsString , getTemplateDownloadTimeout ()));
198
+ }
199
+ waitForTemplateDownload (msToSleep , templateAsString , destDatastoreAsString );
200
+ }
201
+
202
+ if (templateStoreVO .getState () == ObjectInDataStoreStateMachine .State .Ready ) {
203
+ logger .debug ("{} already exists on destination [{}]." , templateAsString , destDatastoreAsString );
204
+ return true ;
205
+ }
206
+ return false ;
207
+ }
208
+
209
+ protected long getTemplateDownloadTimeout () {
210
+ return DownloadListener .DOWNLOAD_TIMEOUT ;
211
+ }
212
+
213
+ protected void waitForTemplateDownload (long msToSleep , String templateAsString , String destDatastoreAsString ) {
214
+ logger .debug ("{} is being downloaded to destination [{}]; we will verify in {} milliseconds if the download has finished." ,
215
+ templateAsString , destDatastoreAsString , msToSleep );
216
+ try {
217
+ Thread .sleep (msToSleep );
218
+ } catch (InterruptedException e ) {
219
+ logger .warn ("[ignored] interrupted while waiting for template {} download to finish before trying to migrate it to data store [{}]." ,
220
+ templateAsString , destDatastoreAsString );
221
+ }
222
+ }
223
+
163
224
protected void migrateJob (AsyncCallFuture <DataObjectResult > future , DataObject srcDataObject , DataObject destDataObject , DataStore destDatastore ) throws ExecutionException , InterruptedException {
164
225
MigrateDataContext <DataObjectResult > context = new MigrateDataContext <DataObjectResult >(null , future , srcDataObject , destDataObject , destDatastore );
165
226
AsyncCallbackDispatcher <SecondaryStorageServiceImpl , CopyCommandResult > caller = AsyncCallbackDispatcher .create (this );
0 commit comments