560dbb678b2f0418dc29109c8e67041f65a81353
[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.sql.SQLException;
26 import java.text.ParseException;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.stream.Collectors;
30 import org.onap.ccsdk.features.sdnr.wt.common.database.data.DatabaseVersion;
31 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.SqlDBClient;
32 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.SdnrDbType;
33 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.DataMigrationProviderService;
34 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.ReleaseInformation;
35 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ComponentName;
36 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.DataMigrationReport;
37 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.Release;
38 import org.onap.ccsdk.features.sdnr.wt.dataprovider.setup.data.ReleaseGroup;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 public class MariaDbDataMigrationProvider implements DataMigrationProviderService {
43
44
45     private static final Logger LOG = LoggerFactory.getLogger(MariaDbDataMigrationProvider.class);
46     private static final SdnrDbType DBTYPE = SdnrDbType.MARIADB;
47     private static final String LOG_DELETING_INDEX = "deleting index {}";
48     private final SqlDBClient dbClient;
49
50     public MariaDbDataMigrationProvider(String url, String username, String password, boolean trustAll,
51             long timeoutms) throws Exception {
52         dbClient = new SqlDBClient(url, username, password);
53     }
54
55     @Override
56     public DataMigrationReport importData(String filename, boolean dryrun) throws Exception {
57         return this.importData(filename, dryrun, Release.CURRENT_RELEASE);
58     }
59
60     @Override
61     public DataMigrationReport importData(String filename, boolean dryrun, Release forRelease) throws Exception {
62         throw new RuntimeException("not supported anymore");
63     }
64
65
66     /**
67      * export data if file exists .1 (.n) will be created
68      */
69     @Override
70     public DataMigrationReport exportData(String filename) {
71         throw new RuntimeException("not supported anymore");
72     }
73
74     private String checkFilenameForWrite(String filename) {
75         File f = new File(filename);
76         if (!f.exists()) {
77             return filename;
78         }
79         return this.checkFilenameForWrite(filename, 0);
80     }
81
82     private String checkFilenameForWrite(String filename, int apdx) {
83         File f = new File(String.format("$s.$d", filename, apdx));
84         if (!f.exists()) {
85             return filename;
86         }
87         return this.checkFilenameForWrite(filename, apdx + 1);
88     }
89
90     @Override
91     public Release getCurrentVersion() {
92         return Release.CURRENT_RELEASE;
93     }
94
95
96     @Override
97     public Release autoDetectRelease() {
98         DatabaseVersion dbVersion;
99         try {
100             dbVersion = this.dbClient.readActualVersion();
101         } catch (SQLException | ParseException e) {
102             LOG.error("unable to detect db version", e);
103             return null;
104         }
105         var views = this.dbClient.readViews();
106         var tables = this.dbClient.readTables();
107         if (tables == null) {
108             return null;
109         }
110         List<Release> foundReleases = new ArrayList<>();
111         //if there are active aliases reduce indices to the active ones
112         if (views != null && !views.isEmpty()) {
113             tables = tables.stream()
114                     .filter(e -> views.stream().anyMatch(v -> v.getTableReference().equals(e.getName())))
115                     .collect(Collectors.toUnmodifiableList());
116         }
117         for (Release r : Release.values()) {
118             if (r.isDbInRange(dbVersion)) {
119                 ReleaseInformation ri = ReleaseInformation.getInstance(r);
120                 if (ri != null && ri.containsIndices(tables)) {
121                     foundReleases.add(r);
122                 }
123             }
124         }
125         if (foundReleases.size() == 1) {
126             return foundReleases.get(0);
127         }
128         LOG.error("detect {} releases: {}. unable to detect for which one to do sth.", foundReleases.size(),
129                 foundReleases);
130         return null;
131     }
132
133     @Override
134     public boolean initDatabase(Release release, int numShards, int numReplicas, String dbPrefix, boolean forceRecreate,
135             long timeoutms) {
136         if (timeoutms > 0) {
137             this.dbClient.waitForYellowStatus(timeoutms);
138         }
139         DatabaseVersion dbVersion;
140         try {
141             dbVersion = this.dbClient.readActualVersion();
142         } catch (SQLException | ParseException e1) {
143             LOG.error("unable to detect db version", e1);
144             return false;
145         }
146         if (dbVersion == null) {
147             return false;
148         }
149         LOG.info("detected database version {}", dbVersion);
150         if (release == null) {
151             release = ReleaseGroup.CURRENT_RELEASE.getLatestCompatibleRelease(dbVersion);
152             if (release == null) {
153                 LOG.warn("unable to autodetect release for this database version for release {}",
154                         ReleaseGroup.CURRENT_RELEASE.name());
155                 return false;
156             }
157             LOG.info("autodetect release {}", release);
158         }
159         if (!release.isDbInRange(dbVersion)) {
160             LOG.warn("db version {} maybe not compatible with release {}", dbVersion, release);
161             return false;
162         }
163         if (forceRecreate) {
164             this.clearDatabase(release, dbPrefix, 0);
165         }
166         ReleaseInformation ri = ReleaseInformation.getInstance(release);
167         var views = this.dbClient.readViews();
168         var tables = this.dbClient.readTables();
169         if (views == null || tables == null) {
170             return false;
171         }
172         boolean response = false;
173         if (!ri.runPreInitCommands(this.dbClient)) {
174             return false;
175         }
176         for (ComponentName component : ri.getComponents()) {
177             try {
178                 if (ri.hasOwnDbIndex(component)) {
179                     //check if index already exists
180                     String tableName = ri.getIndex(component, dbPrefix);
181                     String viewName = ri.getAlias(component, dbPrefix);
182                     if (!tables.stream().anyMatch(e->e.getName().equals(tableName))) {
183                         LOG.info("creating index for {}", component);
184                         response = this.dbClient.createTable(ri.getIndex(component, dbPrefix),
185                                 ri.getDatabaseMapping(component, DBTYPE));
186                         LOG.info(response ? "succeeded" : "failed");
187                     } else {
188                         LOG.info("index {} for {} already exists", tableName, component);
189                     }
190                     //check if alias already exists
191                     if (!views.stream().anyMatch(e->e.getName().equals(viewName))) {
192                         LOG.info("creating alias for {}", component);
193                         response = this.dbClient.createView(tableName, viewName);
194                         LOG.info(response ? "succeeded" : "failed");
195                     } else {
196                         LOG.info("view {} for table {} for {} already exists", viewName, tableName, component);
197                     }
198                 }
199             } catch (SQLException e) {
200                 LOG.error(e.getMessage());
201                 return false;
202             }
203         }
204         if (!ri.runPostInitCommands(this.dbClient)) {
205             return false;
206         }
207         return true;
208     }
209
210     @Override
211     public boolean clearDatabase(Release release, String dbPrefix, long timeoutms) {
212
213         if (timeoutms > 0) {
214             this.dbClient.waitForYellowStatus(timeoutms);
215         }
216         //check aliases
217         var entries = this.dbClient.readViews();
218         var entries2 = this.dbClient.readTables();
219         if (entries == null) {
220             return false;
221         }
222         if (release == null) {
223             DatabaseVersion dbVersion;
224             try {
225                 dbVersion = this.dbClient.readActualVersion();
226             } catch (SQLException | ParseException e) {
227                 LOG.error("unable to detect db version", e);
228                 return false;
229             }
230             LOG.info("detected database version {}", dbVersion);
231             release = ReleaseGroup.CURRENT_RELEASE.getLatestCompatibleRelease(dbVersion);
232             if (release == null) {
233                 LOG.warn("unable to autodetect release for this database version for release {}",
234                         ReleaseGroup.CURRENT_RELEASE.name());
235                 return false;
236             }
237             LOG.info("autodetect release {}", release);
238         }
239         ReleaseInformation ri = ReleaseInformation.getInstance(release);
240         boolean response;
241         if (entries.isEmpty()) {
242             LOG.info("no aliases to clear");
243         } else {
244             //check for every component of release if alias exists
245             for (ComponentName component : ri.getComponents()) {
246                 String aliasToDelete = ri.getAlias(component, dbPrefix);
247                 var entryToDelete = entries.stream().filter(e->e.getName().equals(aliasToDelete)).findFirst().orElse(null);
248                 if (entryToDelete != null) {
249                     try {
250                         LOG.info("deleting alias {} for index {}", entryToDelete.getName(), entryToDelete.getTableReference());
251                         response = this.dbClient.deleteView(entryToDelete.getName());
252                         LOG.info(response ? "succeeded" : "failed");
253                     } catch (SQLException e) {
254                         LOG.error(e.getMessage());
255                         return false;
256                     }
257                 } else {
258                     //try to find malformed typed index with alias name
259                     var entry2ToDelete = entries2.stream().filter(e->e.getName().equals(aliasToDelete)).findFirst().orElse(null);
260                     if (entry2ToDelete != null) {
261                         try {
262                             LOG.info(LOG_DELETING_INDEX, entry2ToDelete.getName());
263                             response = this.dbClient.deleteTable(entry2ToDelete.getName());
264                             LOG.info(response ? "succeeded" : "failed");
265                         } catch (SQLException e) {
266                             LOG.error(e.getMessage());
267                             return false;
268                         }
269                     }
270                 }
271             }
272         }
273         if (entries2 == null) {
274             return false;
275         }
276         if (entries2.isEmpty()) {
277             LOG.info("no indices to clear");
278         } else {
279             //check for every component of release if index exists
280             for (ComponentName component : ri.getComponents()) {
281                 String indexToDelete = ri.getIndex(component, dbPrefix);
282                 var entryToDelete = entries2.stream().filter(e->e.getName().equals(indexToDelete)).findFirst().orElse(null);
283                 if (entryToDelete != null) {
284                     try {
285                         LOG.info(LOG_DELETING_INDEX, entryToDelete.getName());
286                         response = this.dbClient.deleteTable(entryToDelete.getName());
287                         LOG.info(response ? "succeeded" : "failed");
288                     } catch (SQLException e) {
289                         LOG.error(e.getMessage());
290                         return false;
291                     }
292                 }
293             }
294         }
295
296         return true;
297     }
298
299     /**
300      * @param timeoutms
301      * @return
302      */
303     public boolean clearCompleteDatabase(long timeoutms) {
304         if (timeoutms > 0) {
305             this.dbClient.waitForYellowStatus(timeoutms);
306         }
307         //check aliases and indices
308         var aliases = this.dbClient.readViews();
309         var indices = this.dbClient.readTables();
310         if (aliases == null || indices == null) {
311             return false;
312         }
313         for (var alias : aliases) {
314             try {
315                 LOG.info("deleting alias {} for index {}", alias.getName(), alias.getTableReference());
316                 this.dbClient.deleteView(alias.getName());
317             } catch (SQLException e) {
318                 LOG.error("problem deleting alias {}: {}", alias.getName(), e);
319                 return false;
320             }
321         }
322         for (var index : indices) {
323             try {
324                 LOG.info(LOG_DELETING_INDEX, index.getName());
325                 this.dbClient.deleteTable(index.getName());
326             } catch (SQLException e) {
327                 LOG.error("problem deleting index {}: {}", index.getName(), e);
328                 return false;
329             }
330         }
331         return true;
332     }
333
334 }