dabe0047f7388a58a636b82970f34898804a1485
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.setup;
23
24 import java.io.File;
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.nio.charset.StandardCharsets;
28 import java.nio.file.Files;
29 import java.text.ParseException;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.Set;
34 import org.json.JSONObject;
35 import org.onap.ccsdk.features.sdnr.wt.common.database.HtDatabaseClient;
36 import org.onap.ccsdk.features.sdnr.wt.common.database.Portstatus;
37 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchHit;
38 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchResult;
39 import org.onap.ccsdk.features.sdnr.wt.common.database.config.HostInfo;
40 import org.onap.ccsdk.features.sdnr.wt.common.database.data.AliasesEntry;
41 import org.onap.ccsdk.features.sdnr.wt.common.database.data.AliasesEntryList;
42 import org.onap.ccsdk.features.sdnr.wt.common.database.data.EsVersion;
43 import org.onap.ccsdk.features.sdnr.wt.common.database.data.IndicesEntry;
44 import org.onap.ccsdk.features.sdnr.wt.common.database.data.IndicesEntryList;
45 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.CreateAliasRequest;
46 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.CreateIndexRequest;
47 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.DeleteAliasRequest;
48 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.DeleteIndexRequest;
49 import org.onap.ccsdk.features.sdnr.wt.common.database.responses.AcknowledgedResponse;
50 import org.onap.ccsdk.features.sdnr.wt.common.database.responses.GetInfoResponse;
51 import org.onap.ccsdk.features.sdnr.wt.common.database.responses.ListAliasesResponse;
52 import org.onap.ccsdk.features.sdnr.wt.common.database.responses.ListIndicesResponse;
53 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ComponentData;
54 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ComponentName;
55 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.DataContainer;
56 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.DataMigrationReport;
57 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.Release;
58 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.SearchHitConverter;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 public class DataMigrationProviderImpl implements DataMigrationProviderService {
63
64
65     private static final Logger LOG = LoggerFactory.getLogger(DataMigrationProviderImpl.class);
66     private final HtDatabaseClient dbClient;
67
68     public DataMigrationProviderImpl(HostInfo[] hosts, String username, String password, boolean trustAll,
69             long timeoutms) {
70
71         if (timeoutms > 0) {
72             Portstatus.waitSecondsTillAvailable(timeoutms / 1000, hosts);
73         }
74         try {
75             this.dbClient = HtDatabaseClient.getClient(hosts, username, password, trustAll);
76         } catch (Exception e) {
77             throw new IllegalArgumentException("Can not reach database with parameters.",e);
78         }
79     }
80
81     @Override
82     public DataMigrationReport importData(String filename, boolean dryrun) throws Exception {
83         return this.importData(filename, dryrun, Release.CURRENT_RELEASE);
84     }
85
86     public DataMigrationReport importData(String filename, boolean dryrun, Release forRelease) throws Exception {
87         DataMigrationReport report = new DataMigrationReport();
88         File file = new File(filename);
89         if (!file.exists()) {
90             if (dryrun) {
91                 report.error("file %s not found", filename);
92                 return report;
93             }
94             throw new FileNotFoundException(filename);
95         }
96         DataContainer container = null;
97         try {
98             container = DataContainer.load(file);
99         } catch (Exception e) {
100             if (dryrun) {
101                 report.error("problem loading file %s: %s", filename, e.getMessage());
102                 return report;
103             }
104             throw new Exception("problem loading file " + filename, e);
105         }
106         ReleaseInformation ri = ReleaseInformation.getInstance(forRelease);
107         SearchHitConverter converter;
108         Set<ComponentName> components = ri.getComponents();
109         //for all db components of dest architecture
110         for (ComponentName component : components) {
111             //convert to ComponentData for current release with existing ComponentData of the container
112             converter = SearchHitConverter.Factory.getInstance(container.getRelease(), forRelease, component);
113             if (converter == null) {
114                 continue;
115             }
116             ComponentData data = converter.convert(container);
117             if (data != null) {
118                 String indexName = ri.getAlias(component);
119                 String dataTypeName = ri.getDataType(component);
120                 if (dryrun) {
121                     report.log("write %d entries into %s/%s", data.size(), indexName, dataTypeName);
122                 } else {
123                     LOG.debug("write {} entries into {}/{}", data.size(), indexName, dataTypeName);
124                 }
125                 for (SearchHit item : data) {
126                     if (!dryrun) {
127                         String id = this.dbClient.doWriteRaw(indexName, dataTypeName, item.getId(),
128                                 item.getSourceAsString());
129                         if (!item.getId().equals(id)) {
130                             LOG.warn("entry for {} with original id {} was written with another id {}",
131                                     component.getValue(), item.getId(), id);
132                         }
133                     }
134                 }
135             } else {
136                 if (dryrun) {
137                     report.error("unable to convert data for " + component.getValue() + " from version "
138                             + container.getRelease().getValue() + " to " + forRelease.getValue() + "\n");
139                 } else {
140                     LOG.warn("unable to convert data for {} from version {} to {}", component.getValue(),
141                             container.getRelease().getValue(), forRelease.getValue());
142                 }
143             }
144         }
145         LOG.info("import of {} completed", filename);
146         if (dryrun) {
147             report.log("import of %s completed", filename);
148         }
149         report.setCompleted(true);
150         return report;
151     }
152
153
154     /**
155      * export data if file exists .1 (.n) will be created
156      *
157      */
158     @Override
159     public DataMigrationReport exportData(String filename) {
160         DataMigrationReport report = new DataMigrationReport();
161
162         DataContainer container = new DataContainer();
163
164         filename = this.checkFilenameForWrite(filename);
165         LOG.info("output will be written to {}", filename);
166         //autodetect version
167         Release dbRelease = this.autoDetectRelease();
168         if (dbRelease == null) {
169             report.error("unbable to detect db release. is database initialized?");
170             return report;
171         }
172         ReleaseInformation ri = ReleaseInformation.getInstance(dbRelease);
173         boolean componentsSucceeded = true;
174         for (ComponentName c : ri.getComponents()) {
175             ComponentData data = new ComponentData(c);
176             SearchResult<SearchHit> result = this.dbClient.doReadAllJsonData(ri.getAlias(c), ri.getDataType(c), false);
177             data.addAll(result.getHits());
178             container.addComponent(c, data);
179         }
180         try {
181             Files.write(new File(filename).toPath(), Arrays.asList(container.toJSON()), StandardCharsets.UTF_8);
182             report.setCompleted(componentsSucceeded);
183         } catch (IOException e) {
184             LOG.warn("problem writing data to {}: {}", filename, e);
185         }
186         return report;
187     }
188
189     private String checkFilenameForWrite(String filename) {
190         File f = new File(filename);
191         if (!f.exists()) {
192             return filename;
193         }
194         return this.checkFilenameForWrite(filename, 0);
195     }
196
197     private String checkFilenameForWrite(String filename, int apdx) {
198         File f = new File(String.format("$s.$d", filename, apdx));
199         if (!f.exists()) {
200             return filename;
201         }
202         return this.checkFilenameForWrite(filename, apdx + 1);
203     }
204
205     @Override
206     public Release getCurrentVersion() {
207         return Release.CURRENT_RELEASE;
208     }
209
210
211     public Release autoDetectRelease() {
212         EsVersion dbVersion = this.readActualVersion();
213         AliasesEntryList aliases = this.readAliases();
214         IndicesEntryList indices = this.readIndices();
215         if (indices == null) {
216             return null;
217         }
218         List<Release> foundReleases = new ArrayList<>();
219         //if there are active aliases reduce indices to the active ones
220         if (aliases != null && aliases.size() > 0) {
221             indices = indices.subList(aliases.getLinkedIndices());
222         }
223         for (Release r : Release.values()) {
224             if (r.isDbInRange(dbVersion)) {
225                 ReleaseInformation ri = ReleaseInformation.getInstance(r);
226                 if (ri != null && ri.containsIndices(indices)) {
227                     foundReleases.add(r);
228                 }
229             }
230         }
231         if (foundReleases.size() == 1) {
232             return foundReleases.get(0);
233         }
234         LOG.error("detect {} releases: {}. unable to detect for which one to do sth.", foundReleases.size(),
235                 foundReleases);
236         return null;
237     }
238
239     private EsVersion readActualVersion() {
240         try {
241             GetInfoResponse response = this.dbClient.getInfo();
242             return response.getVersion();
243         } catch (Exception e) {
244             LOG.warn(e.getMessage());
245         }
246         return null;
247     }
248
249     private AliasesEntryList readAliases() {
250         AliasesEntryList entries = null;
251         try {
252             ListAliasesResponse response = this.dbClient.getAliases();
253             entries = response.getEntries();
254         } catch (ParseException | IOException e) {
255             LOG.error(e.getMessage());
256         }
257         return entries;
258     }
259
260     private IndicesEntryList readIndices() {
261         IndicesEntryList entries = null;
262         try {
263             ListIndicesResponse response = this.dbClient.getIndices();
264             entries = response.getEntries();
265         } catch (ParseException | IOException e) {
266             LOG.error(e.getMessage());
267         }
268         return entries;
269     }
270
271     @Override
272     public boolean initDatabase(Release release, int numShards, int numReplicas, String dbPrefix, boolean forceRecreate,
273             long timeoutms) {
274         if (timeoutms > 0) {
275             this.dbClient.waitForYellowStatus(timeoutms);
276         }
277         EsVersion dbVersion = this.readActualVersion();
278         if (dbVersion == null) {
279             return false;
280         }
281         if (!release.isDbInRange(dbVersion)) {
282             LOG.warn("db version {} maybe not compatible with release {}", dbVersion, release);
283             return false;
284         }
285         if (forceRecreate) {
286             this.clearDatabase(release, dbPrefix, 0);
287         }
288         ReleaseInformation ri = ReleaseInformation.getInstance(release);
289         AliasesEntryList aliases = this.readAliases();
290         IndicesEntryList indices = this.readIndices();
291         if (aliases == null || indices == null) {
292             return false;
293         }
294         AcknowledgedResponse response = null;
295         if (!ri.runPreInitCommands(this.dbClient)) {
296             return false;
297         }
298         for (ComponentName component : ri.getComponents()) {
299             try {
300                 if (ri.hasOwnDbIndex(component)) {
301                     //check if index already exists
302                     String indexName = ri.getIndex(component, dbPrefix);
303                     String aliasName = ri.getAlias(component, dbPrefix);
304                     if (indices.findByIndex(indexName) == null) {
305                         LOG.info("creating index for {}", component);
306                         CreateIndexRequest request = new CreateIndexRequest(ri.getIndex(component, dbPrefix));
307                         request.mappings(new JSONObject(ri.getDatabaseMapping(component)));
308                         request.settings(new JSONObject(ri.getDatabaseSettings(component, numShards, numReplicas)));
309                         response = this.dbClient.createIndex(request);
310                         LOG.info(response.isAcknowledged() ? "succeeded" : "failed");
311                     } else {
312                         LOG.info("index {} for {} already exists", indexName, component);
313                     }
314                     //check if alias already exists
315                     if (aliases.findByAlias(aliasName) == null) {
316                         LOG.info("creating alias for {}", component);
317                         response = this.dbClient.createAlias(new CreateAliasRequest(indexName, aliasName));
318                         LOG.info(response.isAcknowledged() ? "succeeded" : "failed");
319                     } else {
320                         LOG.info("alias {} for index {} for {} already exists", aliasName, indexName, component);
321                     }
322                 }
323             } catch (IOException e) {
324                 LOG.error(e.getMessage());
325                 return false;
326             }
327         }
328         if (!ri.runPostInitCommands(this.dbClient)) {
329             return false;
330         }
331         return true;
332     }
333
334     @Override
335     public boolean clearDatabase(Release release, String dbPrefix, long timeoutms) {
336
337         if (timeoutms > 0) {
338             this.dbClient.waitForYellowStatus(timeoutms);
339         }
340         //check aliases
341         AliasesEntryList entries = this.readAliases();
342         if (entries == null) {
343             return false;
344         }
345         ReleaseInformation ri = ReleaseInformation.getInstance(release);
346         AcknowledgedResponse response;
347         if (entries.size() <= 0) {
348             LOG.info("no aliases to clear");
349         } else {
350             //check for every component of release if alias exists
351             for (ComponentName component : ri.getComponents()) {
352                 String aliasToDelete = ri.getAlias(component, dbPrefix);
353                 AliasesEntry entryToDelete = entries.findByAlias(aliasToDelete);
354                 if (entryToDelete != null) {
355                     try {
356                         LOG.info("deleting alias {} for index {}", entryToDelete.getAlias(), entryToDelete.getIndex());
357                         response = this.dbClient.deleteAlias(
358                                 new DeleteAliasRequest(entryToDelete.getIndex(), entryToDelete.getAlias()));
359                         LOG.info(response.isResponseSucceeded() ? "succeeded" : "failed");
360                     } catch (IOException e) {
361                         LOG.error(e.getMessage());
362                         return false;
363                     }
364                 }
365             }
366         }
367         IndicesEntryList entries2 = this.readIndices();
368         if (entries2 == null) {
369             return false;
370         }
371         if (entries2.size() <= 0) {
372             LOG.info("no indices to clear");
373         } else {
374             //check for every component of release if index exists
375             for (ComponentName component : ri.getComponents()) {
376                 String indexToDelete = ri.getIndex(component, dbPrefix);
377                 IndicesEntry entryToDelete = entries2.findByIndex(indexToDelete);
378                 if (entryToDelete != null) {
379                     try {
380                         LOG.info("deleting index {}", entryToDelete.getName());
381                         response = this.dbClient.deleteIndex(new DeleteIndexRequest(entryToDelete.getName()));
382                         LOG.info(response.isResponseSucceeded() ? "succeeded" : "failed");
383                     } catch (IOException e) {
384                         LOG.error(e.getMessage());
385                         return false;
386                     }
387                 }
388             }
389         }
390
391         return true;
392     }
393
394     /**
395      * @param timeoutms
396      * @return
397      */
398     public boolean clearCompleteDatabase(long timeoutms) {
399         if (timeoutms > 0) {
400             this.dbClient.waitForYellowStatus(timeoutms);
401         }
402         //check aliases and indices
403         AliasesEntryList aliases = this.readAliases();
404         IndicesEntryList indices = this.readIndices();
405         if (aliases == null || indices == null) {
406             return false;
407         }
408         for (AliasesEntry alias : aliases) {
409             try {
410                 LOG.info("deleting alias {} for index {}", alias.getAlias(), alias.getIndex());
411                 this.dbClient.deleteAlias(new DeleteAliasRequest(alias.getIndex(), alias.getAlias()));
412             } catch (IOException e) {
413                 LOG.error("problem deleting alias {}: {}", alias.getAlias(), e);
414                 return false;
415             }
416         }
417         for (IndicesEntry index : indices) {
418             try {
419                 LOG.info("deleting index {}", index.getName());
420                 this.dbClient.deleteIndex(new DeleteIndexRequest(index.getName()));
421             } catch (IOException e) {
422                 LOG.error("problem deleting index {}: {}", index.getName(), e);
423                 return false;
424             }
425         }
426         return true;
427     }
428
429 }