55ba66ba6a6aaf2367ac97514d7d513fe0d99a16
[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.database;
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.sql.SQLException;
30 import java.text.ParseException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Set;
35 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchHit;
36 import org.onap.ccsdk.features.sdnr.wt.common.database.SearchResult;
37 import org.onap.ccsdk.features.sdnr.wt.common.database.data.AliasesEntry;
38 import org.onap.ccsdk.features.sdnr.wt.common.database.data.AliasesEntryList;
39 import org.onap.ccsdk.features.sdnr.wt.common.database.data.DatabaseVersion;
40 import org.onap.ccsdk.features.sdnr.wt.common.database.data.IndicesEntry;
41 import org.onap.ccsdk.features.sdnr.wt.common.database.data.IndicesEntryList;
42 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.SqlDBClient;
43 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.SdnrDbType;
44 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.DataMigrationProviderService;
45 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.ReleaseInformation;
46 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ComponentData;
47 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ComponentName;
48 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.DataContainer;
49 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.DataMigrationReport;
50 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.Release;
51 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ReleaseGroup;
52 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.SearchHitConverter;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 public class MariaDbDataMigrationProvider implements DataMigrationProviderService {
57
58
59     private static final Logger LOG = LoggerFactory.getLogger(MariaDbDataMigrationProvider.class);
60     private static final SdnrDbType DBTYPE = SdnrDbType.MARIADB;
61     private static final String LOG_DELETING_INDEX = "deleting index {}";
62     private final SqlDBClient dbClient;
63
64     public MariaDbDataMigrationProvider(String url, String username, String password, boolean trustAll,
65             long timeoutms) throws Exception {
66         dbClient = new SqlDBClient(url, username, password);
67     }
68
69     @Override
70     public DataMigrationReport importData(String filename, boolean dryrun) throws Exception {
71         return this.importData(filename, dryrun, Release.CURRENT_RELEASE);
72     }
73
74     @Override
75     public DataMigrationReport importData(String filename, boolean dryrun, Release forRelease) throws Exception {
76         DataMigrationReport report = new DataMigrationReport();
77         File file = new File(filename);
78         if (!file.exists()) {
79             if (dryrun) {
80                 report.error("file %s not found", filename);
81                 return report;
82             }
83             throw new FileNotFoundException(filename);
84         }
85         DataContainer container = null;
86         try {
87             container = DataContainer.load(file);
88         } catch (Exception e) {
89             if (dryrun) {
90                 report.error("problem loading file %s: %s", filename, e.getMessage());
91                 return report;
92             }
93             throw new Exception("problem loading file " + filename, e);
94         }
95         ReleaseInformation ri = ReleaseInformation.getInstance(forRelease);
96         SearchHitConverter converter;
97         Set<ComponentName> components = ri.getComponents();
98         //for all db components of dest architecture
99         for (ComponentName component : components) {
100             //convert to ComponentData for current release with existing ComponentData of the container
101             converter = SearchHitConverter.Factory.getInstance(container.getRelease(), forRelease, component);
102             if (converter == null) {
103                 continue;
104             }
105             ComponentData data = converter.convert(container);
106             if (data != null) {
107                 String indexName = ri.getAlias(component);
108                 String dataTypeName = ri.getDataType(component);
109                 if (dryrun) {
110                     report.log("write %d entries into %s/%s", data.size(), indexName, dataTypeName);
111                 } else {
112                     LOG.debug("write {} entries into {}/{}", data.size(), indexName, dataTypeName);
113                 }
114                 for (SearchHit item : data) {
115                     if (!dryrun) {
116                         String id = null;//this.dbClient.doWriteRaw(indexName, dataTypeName, item.getId(), item.getSourceAsString(), true);
117                         if (!item.getId().equals(id)) {
118                             LOG.warn("entry for {} with original id {} was written with another id {}",
119                                     component.getValue(), item.getId(), id);
120                         }
121                     }
122                 }
123             } else {
124                 if (dryrun) {
125                     report.error("unable to convert data for " + component.getValue() + " from version "
126                             + container.getRelease().getValue() + " to " + forRelease.getValue() + "\n");
127                 } else {
128                     LOG.warn("unable to convert data for {} from version {} to {}", component.getValue(),
129                             container.getRelease().getValue(), forRelease.getValue());
130                 }
131             }
132         }
133         LOG.info("import of {} completed", filename);
134         if (dryrun) {
135             report.log("import of %s completed", filename);
136         }
137         report.setCompleted(true);
138         return report;
139     }
140
141
142     /**
143      * export data if file exists .1 (.n) will be created
144      *
145      */
146     @Override
147     public DataMigrationReport exportData(String filename) {
148         DataMigrationReport report = new DataMigrationReport();
149
150         DataContainer container = new DataContainer();
151
152         filename = this.checkFilenameForWrite(filename);
153         LOG.info("output will be written to {}", filename);
154         //autodetect version
155         Release dbRelease = this.autoDetectRelease();
156         if (dbRelease == null) {
157             report.error("unbable to detect db release. is database initialized?");
158             return report;
159         }
160         ReleaseInformation ri = ReleaseInformation.getInstance(dbRelease);
161         boolean componentsSucceeded = true;
162         for (ComponentName c : ri.getComponents()) {
163             ComponentData data = new ComponentData(c);
164             SearchResult<SearchHit> result = null;//this.dbClient.doReadAllJsonData(ri.getAlias(c), ri.getDataType(c), false);
165             data.addAll(result.getHits());
166             container.addComponent(c, data);
167         }
168         try {
169             Files.write(new File(filename).toPath(), Arrays.asList(container.toJSON()), StandardCharsets.UTF_8);
170             report.setCompleted(componentsSucceeded);
171         } catch (IOException e) {
172             LOG.warn("problem writing data to {}: {}", filename, e);
173         }
174         return report;
175     }
176
177     private String checkFilenameForWrite(String filename) {
178         File f = new File(filename);
179         if (!f.exists()) {
180             return filename;
181         }
182         return this.checkFilenameForWrite(filename, 0);
183     }
184
185     private String checkFilenameForWrite(String filename, int apdx) {
186         File f = new File(String.format("$s.$d", filename, apdx));
187         if (!f.exists()) {
188             return filename;
189         }
190         return this.checkFilenameForWrite(filename, apdx + 1);
191     }
192
193     @Override
194     public Release getCurrentVersion() {
195         return Release.CURRENT_RELEASE;
196     }
197
198
199     @Override
200     public Release autoDetectRelease() {
201         DatabaseVersion dbVersion;
202         try {
203             dbVersion = this.dbClient.readActualVersion();
204         } catch (SQLException | ParseException e) {
205             LOG.error("unable to detect db version", e);
206             return null;
207         }
208         AliasesEntryList aliases = this.dbClient.readViews();
209         IndicesEntryList indices = this.dbClient.readTables();
210         if (indices == null) {
211             return null;
212         }
213         List<Release> foundReleases = new ArrayList<>();
214         //if there are active aliases reduce indices to the active ones
215         if (aliases != null && !aliases.isEmpty()) {
216             indices = indices.subList(aliases.getLinkedIndices());
217         }
218         for (Release r : Release.values()) {
219             if (r.isDbInRange(dbVersion, SdnrDbType.MARIADB)) {
220                 ReleaseInformation ri = ReleaseInformation.getInstance(r);
221                 if (ri != null && ri.containsIndices(indices)) {
222                     foundReleases.add(r);
223                 }
224             }
225         }
226         if (foundReleases.size() == 1) {
227             return foundReleases.get(0);
228         }
229         LOG.error("detect {} releases: {}. unable to detect for which one to do sth.", foundReleases.size(),
230                 foundReleases);
231         return null;
232     }
233
234     @Override
235     public boolean initDatabase(Release release, int numShards, int numReplicas, String dbPrefix, boolean forceRecreate,
236             long timeoutms) {
237         if (timeoutms > 0) {
238             this.dbClient.waitForYellowStatus(timeoutms);
239         }
240         DatabaseVersion dbVersion;
241         try {
242             dbVersion = this.dbClient.readActualVersion();
243         } catch (SQLException | ParseException e1) {
244             LOG.error("unable to detect db version", e1);
245             return false;
246         }
247         if (dbVersion == null) {
248             return false;
249         }
250         LOG.info("detected database version {}", dbVersion);
251         if (release == null) {
252             release = ReleaseGroup.CURRENT_RELEASE.getLatestCompatibleRelease(dbVersion, SdnrDbType.MARIADB);
253             if (release == null) {
254                 LOG.warn("unable to autodetect release for this database version for release {}",
255                         ReleaseGroup.CURRENT_RELEASE.name());
256                 return false;
257             }
258             LOG.info("autodetect release {}", release);
259         }
260         if (!release.isDbInRange(dbVersion, SdnrDbType.MARIADB)) {
261             LOG.warn("db version {} maybe not compatible with release {}", dbVersion, release);
262             return false;
263         }
264         if (forceRecreate) {
265             this.clearDatabase(release, dbPrefix, 0);
266         }
267         ReleaseInformation ri = ReleaseInformation.getInstance(release);
268         AliasesEntryList views = this.dbClient.readViews();
269         IndicesEntryList indices = this.dbClient.readTables();
270         if (views == null || indices == null) {
271             return false;
272         }
273         boolean response = false;
274         if (!ri.runPreInitCommands(this.dbClient)) {
275             return false;
276         }
277         for (ComponentName component : ri.getComponents()) {
278             try {
279                 if (ri.hasOwnDbIndex(component)) {
280                     //check if index already exists
281                     String tableName = ri.getIndex(component, dbPrefix);
282                     String viewName = ri.getAlias(component, dbPrefix);
283                     if (indices.findByIndex(tableName) == null) {
284                         LOG.info("creating index for {}", component);
285                         response = this.dbClient.createTable(ri.getIndex(component, dbPrefix), ri.getDatabaseMapping(component, DBTYPE));
286                         LOG.info(response ? "succeeded" : "failed");
287                     } else {
288                         LOG.info("index {} for {} already exists", tableName, component);
289                     }
290                     //check if alias already exists
291                     if (views.findByAlias(viewName) == null) {
292                         LOG.info("creating alias for {}", component);
293                         response = this.dbClient.createView(tableName, viewName);
294                         LOG.info(response ? "succeeded" : "failed");
295                     } else {
296                         LOG.info("view {} for table {} for {} already exists", viewName, tableName, component);
297                     }
298                 }
299             } catch (SQLException e) {
300                 LOG.error(e.getMessage());
301                 return false;
302             }
303         }
304         if (!ri.runPostInitCommands(this.dbClient)) {
305             return false;
306         }
307         return true;
308     }
309
310     @Override
311     public boolean clearDatabase(Release release, String dbPrefix, long timeoutms) {
312
313         if (timeoutms > 0) {
314             this.dbClient.waitForYellowStatus(timeoutms);
315         }
316         //check aliases
317         AliasesEntryList entries = this.dbClient.readViews();
318         IndicesEntryList entries2 = this.dbClient.readTables();
319         if (entries == null) {
320             return false;
321         }
322         if (release == null) {
323             DatabaseVersion dbVersion;
324             try {
325                 dbVersion = this.dbClient.readActualVersion();
326             } catch (SQLException | ParseException e) {
327                 LOG.error("unable to detect db version", e);
328                 return false;
329             }
330             LOG.info("detected database version {}", dbVersion);
331             release = ReleaseGroup.CURRENT_RELEASE.getLatestCompatibleRelease(dbVersion, SdnrDbType.MARIADB);
332             if (release == null) {
333                 LOG.warn("unable to autodetect release for this database version for release {}",
334                         ReleaseGroup.CURRENT_RELEASE.name());
335                 return false;
336             }
337             LOG.info("autodetect release {}", release);
338         }
339         ReleaseInformation ri = ReleaseInformation.getInstance(release);
340         boolean response;
341         if (entries.isEmpty()) {
342             LOG.info("no aliases to clear");
343         } else {
344             //check for every component of release if alias exists
345             for (ComponentName component : ri.getComponents()) {
346                 String aliasToDelete = ri.getAlias(component, dbPrefix);
347                 AliasesEntry entryToDelete = entries.findByAlias(aliasToDelete);
348                 if (entryToDelete != null) {
349                     try {
350                         LOG.info("deleting alias {} for index {}", entryToDelete.getAlias(), entryToDelete.getIndex());
351                         response = this.dbClient.deleteView(entryToDelete.getAlias());
352                         LOG.info(response ? "succeeded" : "failed");
353                     } catch (SQLException e) {
354                         LOG.error(e.getMessage());
355                         return false;
356                     }
357                 } else {
358                     //try to find malformed typed index with alias name
359                     IndicesEntry entry2ToDelete = entries2.findByIndex(aliasToDelete);
360                     if (entry2ToDelete != null) {
361                         try {
362                             LOG.info(LOG_DELETING_INDEX, entry2ToDelete.getName());
363                             response = this.dbClient.deleteTable(entry2ToDelete.getName());
364                             LOG.info(response ? "succeeded" : "failed");
365                         } catch (SQLException e) {
366                             LOG.error(e.getMessage());
367                             return false;
368                         }
369                     }
370                 }
371             }
372         }
373         if (entries2 == null) {
374             return false;
375         }
376         if (entries2.isEmpty()) {
377             LOG.info("no indices to clear");
378         } else {
379             //check for every component of release if index exists
380             for (ComponentName component : ri.getComponents()) {
381                 String indexToDelete = ri.getIndex(component, dbPrefix);
382                 IndicesEntry entryToDelete = entries2.findByIndex(indexToDelete);
383                 if (entryToDelete != null) {
384                     try {
385                         LOG.info(LOG_DELETING_INDEX, entryToDelete.getName());
386                         response = this.dbClient.deleteTable(entryToDelete.getName());
387                         LOG.info(response ? "succeeded" : "failed");
388                     } catch (SQLException e) {
389                         LOG.error(e.getMessage());
390                         return false;
391                     }
392                 }
393             }
394         }
395
396         return true;
397     }
398
399     /**
400      * @param timeoutms
401      * @return
402      */
403     public boolean clearCompleteDatabase(long timeoutms) {
404         if (timeoutms > 0) {
405             this.dbClient.waitForYellowStatus(timeoutms);
406         }
407         //check aliases and indices
408         AliasesEntryList aliases = this.dbClient.readViews();
409         IndicesEntryList indices = this.dbClient.readTables();
410         if (aliases == null || indices == null) {
411             return false;
412         }
413         for (AliasesEntry alias : aliases) {
414             try {
415                 LOG.info("deleting alias {} for index {}", alias.getAlias(), alias.getIndex());
416                 this.dbClient.deleteView(alias.getAlias());
417             } catch (SQLException e) {
418                 LOG.error("problem deleting alias {}: {}", alias.getAlias(), e);
419                 return false;
420             }
421         }
422         for (IndicesEntry index : indices) {
423             try {
424                 LOG.info(LOG_DELETING_INDEX, index.getName());
425                 this.dbClient.deleteTable(index.getName());
426             } catch (SQLException e) {
427                 LOG.error("problem deleting index {}: {}", index.getName(), e);
428                 return false;
429             }
430         }
431         return true;
432     }
433
434 }