2 * ============LICENSE_START=======================================================
3 * ONAP : ccsdk features
4 * ================================================================================
5 * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.database;
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;
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.SearchHit;
37 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchResult;
38 import org.onap.ccsdk.features.sdnr.wt.common.database.config.HostInfo;
39 import org.onap.ccsdk.features.sdnr.wt.common.database.data.AliasesEntry;
40 import org.onap.ccsdk.features.sdnr.wt.common.database.data.AliasesEntryList;
41 import org.onap.ccsdk.features.sdnr.wt.common.database.data.DatabaseVersion;
42 import org.onap.ccsdk.features.sdnr.wt.common.database.data.IndicesEntry;
43 import org.onap.ccsdk.features.sdnr.wt.common.database.data.IndicesEntryList;
44 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.CreateAliasRequest;
45 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.CreateIndexRequest;
46 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.DeleteAliasRequest;
47 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.DeleteIndexRequest;
48 import org.onap.ccsdk.features.sdnr.wt.common.database.responses.AcknowledgedResponse;
49 import org.onap.ccsdk.features.sdnr.wt.common.database.responses.GetInfoResponse;
50 import org.onap.ccsdk.features.sdnr.wt.common.database.responses.ListAliasesResponse;
51 import org.onap.ccsdk.features.sdnr.wt.common.database.responses.ListIndicesResponse;
52 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.SdnrDbType;
53 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.DataMigrationProviderService;
54 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.ReleaseInformation;
55 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ComponentData;
56 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ComponentName;
57 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.DataContainer;
58 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.DataMigrationReport;
59 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.Release;
60 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ReleaseGroup;
61 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.SearchHitConverter;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
66 public class ElasticsearchDataMigrationProvider implements DataMigrationProviderService {
69 private static final Logger LOG = LoggerFactory.getLogger(ElasticsearchDataMigrationProvider.class);
70 private static final String LOG_DELETING_INDEX = "deleting index {}";
71 private final HtDatabaseClient dbClient;
73 public ElasticsearchDataMigrationProvider(String url, String username, String password, boolean trustAll,
74 long timeoutms) throws Exception {
75 dbClient = HtDatabaseClient.getClient(new HostInfo[] {HostInfo.parse(url)}, username, password, trustAll, true,
80 public DataMigrationReport importData(String filename, boolean dryrun) throws Exception {
81 return this.importData(filename, dryrun, Release.CURRENT_RELEASE);
85 public DataMigrationReport importData(String filename, boolean dryrun, Release forRelease) throws Exception {
86 DataMigrationReport report = new DataMigrationReport();
87 File file = new File(filename);
90 report.error("file %s not found", filename);
93 throw new FileNotFoundException(filename);
95 DataContainer container = null;
97 container = DataContainer.load(file);
98 } catch (Exception e) {
100 report.error("problem loading file %s: %s", filename, e.getMessage());
103 throw new Exception("problem loading file " + filename, e);
105 ReleaseInformation ri = ReleaseInformation.getInstance(forRelease);
106 SearchHitConverter converter;
107 Set<ComponentName> components = ri.getComponents();
108 //for all db components of dest architecture
109 for (ComponentName component : components) {
110 //convert to ComponentData for current release with existing ComponentData of the container
111 converter = SearchHitConverter.Factory.getInstance(container.getRelease(), forRelease, component);
112 if (converter == null) {
115 ComponentData data = converter.convert(container);
117 String indexName = ri.getAlias(component);
118 String dataTypeName = ri.getDataType(component);
120 report.log("write %d entries into %s/%s", data.size(), indexName, dataTypeName);
122 LOG.debug("write {} entries into {}/{}", data.size(), indexName, dataTypeName);
124 for (SearchHit item : data) {
126 String id = this.dbClient.doWriteRaw(indexName, dataTypeName, item.getId(),
127 item.getSourceAsString(), true);
128 if (!item.getId().equals(id)) {
129 LOG.warn("entry for {} with original id {} was written with another id {}",
130 component.getValue(), item.getId(), id);
136 report.error("unable to convert data for " + component.getValue() + " from version "
137 + container.getRelease().getValue() + " to " + forRelease.getValue() + "\n");
139 LOG.warn("unable to convert data for {} from version {} to {}", component.getValue(),
140 container.getRelease().getValue(), forRelease.getValue());
144 LOG.info("import of {} completed", filename);
146 report.log("import of %s completed", filename);
148 report.setCompleted(true);
154 * export data if file exists .1 (.n) will be created
158 public DataMigrationReport exportData(String filename) {
159 DataMigrationReport report = new DataMigrationReport();
161 DataContainer container = new DataContainer();
163 filename = this.checkFilenameForWrite(filename);
164 LOG.info("output will be written to {}", filename);
166 Release dbRelease = this.autoDetectRelease();
167 if (dbRelease == null) {
168 report.error("unbable to detect db release. is database initialized?");
171 ReleaseInformation ri = ReleaseInformation.getInstance(dbRelease);
172 boolean componentsSucceeded = true;
173 for (ComponentName c : ri.getComponents()) {
174 ComponentData data = new ComponentData(c);
175 SearchResult<SearchHit> result = this.dbClient.doReadAllJsonData(ri.getAlias(c), ri.getDataType(c), false);
176 data.addAll(result.getHits());
177 container.addComponent(c, data);
180 Files.write(new File(filename).toPath(), Arrays.asList(container.toJSON()), StandardCharsets.UTF_8);
181 report.setCompleted(componentsSucceeded);
182 } catch (IOException e) {
183 LOG.warn("problem writing data to {}: {}", filename, e);
188 private String checkFilenameForWrite(String filename) {
189 File f = new File(filename);
193 return this.checkFilenameForWrite(filename, 0);
196 private String checkFilenameForWrite(String filename, int apdx) {
197 File f = new File(String.format("$s.$d", filename, apdx));
201 return this.checkFilenameForWrite(filename, apdx + 1);
205 public Release getCurrentVersion() {
206 return Release.CURRENT_RELEASE;
211 public Release autoDetectRelease() {
212 DatabaseVersion dbVersion = this.readActualVersion();
213 AliasesEntryList aliases = this.readAliases();
214 IndicesEntryList indices = this.readIndices();
215 if (indices == null) {
218 List<Release> foundReleases = new ArrayList<>();
219 //if there are active aliases reduce indices to the active ones
220 if (aliases != null && !aliases.isEmpty()) {
221 indices = indices.subList(aliases.getLinkedIndices());
223 for (Release r : Release.values()) {
224 if (r.isDbInRange(dbVersion, SdnrDbType.ELASTICSEARCH)) {
225 ReleaseInformation ri = ReleaseInformation.getInstance(r);
226 if (ri != null && ri.containsIndices(indices)) {
227 foundReleases.add(r);
231 if (foundReleases.size() == 1) {
232 return foundReleases.get(0);
234 LOG.error("detect {} releases: {}. unable to detect for which one to do sth.", foundReleases.size(),
239 private DatabaseVersion readActualVersion() {
241 GetInfoResponse response = this.dbClient.getInfo();
242 return response.getVersion();
243 } catch (Exception e) {
244 LOG.warn(e.getMessage());
249 private AliasesEntryList readAliases() {
250 AliasesEntryList entries = null;
252 ListAliasesResponse response = this.dbClient.getAliases();
253 entries = response.getEntries();
254 } catch (ParseException | IOException e) {
255 LOG.error(e.getMessage());
260 private IndicesEntryList readIndices() {
261 IndicesEntryList entries = null;
263 ListIndicesResponse response = this.dbClient.getIndices();
264 entries = response.getEntries();
265 } catch (ParseException | IOException e) {
266 LOG.error(e.getMessage());
273 public boolean initDatabase(Release release, int numShards, int numReplicas, String dbPrefix, boolean forceRecreate,
276 this.dbClient.waitForYellowStatus(timeoutms);
278 DatabaseVersion dbVersion = this.readActualVersion();
279 if (dbVersion == null) {
282 LOG.info("detected database version {}", dbVersion);
283 if (release == null) {
284 release = ReleaseGroup.CURRENT_RELEASE.getLatestCompatibleRelease(dbVersion, SdnrDbType.ELASTICSEARCH);
285 if (release == null) {
286 LOG.warn("unable to autodetect release for this database version for release {}",
287 ReleaseGroup.CURRENT_RELEASE.name());
290 LOG.info("autodetect release {}", release);
292 if (!release.isDbInRange(dbVersion, SdnrDbType.ELASTICSEARCH)) {
293 LOG.warn("db version {} maybe not compatible with release {}", dbVersion, release);
297 this.clearDatabase(release, dbPrefix, 0);
299 ReleaseInformation ri = ReleaseInformation.getInstance(release);
300 AliasesEntryList aliases = this.readAliases();
301 IndicesEntryList indices = this.readIndices();
302 if (aliases == null || indices == null) {
305 AcknowledgedResponse response = null;
306 if (!ri.runPreInitCommands(this.dbClient)) {
309 for (ComponentName component : ri.getComponents()) {
311 if (ri.hasOwnDbIndex(component)) {
312 //check if index already exists
313 String indexName = ri.getIndex(component, dbPrefix);
314 String aliasName = ri.getAlias(component, dbPrefix);
315 if (indices.findByIndex(indexName) == null) {
316 LOG.info("creating index for {}", component);
317 CreateIndexRequest request = new CreateIndexRequest(ri.getIndex(component, dbPrefix));
318 request.mappings(new JSONObject(ri.getDatabaseMapping(component)));
319 request.settings(new JSONObject(ri.getDatabaseSettings(component, numShards, numReplicas)));
320 response = this.dbClient.createIndex(request);
321 LOG.info(response.isAcknowledged() ? "succeeded" : "failed");
323 LOG.info("index {} for {} already exists", indexName, component);
325 //check if alias already exists
326 if (aliases.findByAlias(aliasName) == null) {
327 LOG.info("creating alias for {}", component);
328 response = this.dbClient.createAlias(new CreateAliasRequest(indexName, aliasName));
329 LOG.info(response.isAcknowledged() ? "succeeded" : "failed");
331 LOG.info("alias {} for index {} for {} already exists", aliasName, indexName, component);
334 } catch (IOException e) {
335 LOG.error(e.getMessage());
339 if (!ri.runPostInitCommands(this.dbClient)) {
346 public boolean clearDatabase(Release release, String dbPrefix, long timeoutms) {
349 this.dbClient.waitForYellowStatus(timeoutms);
352 AliasesEntryList entries = this.readAliases();
353 IndicesEntryList entries2 = this.readIndices();
354 if (entries == null) {
357 if (release == null) {
358 DatabaseVersion dbVersion = this.readActualVersion();
359 if (dbVersion == null) {
362 LOG.info("detected database version {}", dbVersion);
363 release = ReleaseGroup.CURRENT_RELEASE.getLatestCompatibleRelease(dbVersion, SdnrDbType.ELASTICSEARCH);
364 if (release == null) {
365 LOG.warn("unable to autodetect release for this database version for release {}",
366 ReleaseGroup.CURRENT_RELEASE.name());
369 LOG.info("autodetect release {}", release);
371 ReleaseInformation ri = ReleaseInformation.getInstance(release);
372 AcknowledgedResponse response;
373 if (entries.isEmpty()) {
374 LOG.info("no aliases to clear");
376 //check for every component of release if alias exists
377 for (ComponentName component : ri.getComponents()) {
378 String aliasToDelete = ri.getAlias(component, dbPrefix);
379 AliasesEntry entryToDelete = entries.findByAlias(aliasToDelete);
380 if (entryToDelete != null) {
382 LOG.info("deleting alias {} for index {}", entryToDelete.getAlias(), entryToDelete.getIndex());
383 response = this.dbClient.deleteAlias(
384 new DeleteAliasRequest(entryToDelete.getIndex(), entryToDelete.getAlias()));
385 LOG.info(response.isResponseSucceeded() ? "succeeded" : "failed");
386 } catch (IOException e) {
387 LOG.error(e.getMessage());
391 //try to find malformed typed index with alias name
392 IndicesEntry entry2ToDelete = entries2 == null ? null : entries2.findByIndex(aliasToDelete);
393 if (entry2ToDelete != null) {
395 LOG.info(LOG_DELETING_INDEX, entry2ToDelete.getName());
396 response = this.dbClient.deleteIndex(new DeleteIndexRequest(entry2ToDelete.getName()));
397 LOG.info(response.isResponseSucceeded() ? "succeeded" : "failed");
398 } catch (IOException e) {
399 LOG.error(e.getMessage());
406 if (entries2 == null) {
409 if (entries2.isEmpty()) {
410 LOG.info("no indices to clear");
412 //check for every component of release if index exists
413 for (ComponentName component : ri.getComponents()) {
414 String indexToDelete = ri.getIndex(component, dbPrefix);
415 IndicesEntry entryToDelete = entries2.findByIndex(indexToDelete);
416 if (entryToDelete != null) {
418 LOG.info(LOG_DELETING_INDEX, entryToDelete.getName());
419 response = this.dbClient.deleteIndex(new DeleteIndexRequest(entryToDelete.getName()));
420 LOG.info(response.isResponseSucceeded() ? "succeeded" : "failed");
421 } catch (IOException e) {
422 LOG.error(e.getMessage());
436 public boolean clearCompleteDatabase(long timeoutms) {
438 this.dbClient.waitForYellowStatus(timeoutms);
440 //check aliases and indices
441 AliasesEntryList aliases = this.readAliases();
442 IndicesEntryList indices = this.readIndices();
443 if (aliases == null || indices == null) {
446 for (AliasesEntry alias : aliases) {
448 LOG.info("deleting alias {} for index {}", alias.getAlias(), alias.getIndex());
449 this.dbClient.deleteAlias(new DeleteAliasRequest(alias.getIndex(), alias.getAlias()));
450 } catch (IOException e) {
451 LOG.error("problem deleting alias {}: {}", alias.getAlias(), e);
455 for (IndicesEntry index : indices) {
457 LOG.info(LOG_DELETING_INDEX, index.getName());
458 this.dbClient.deleteIndex(new DeleteIndexRequest(index.getName()));
459 } catch (IOException e) {
460 LOG.error("problem deleting index {}: {}", index.getName(), e);