diff options
author | lj1412 <lji@research.att.com> | 2017-02-14 15:10:09 +0000 |
---|---|---|
committer | lj1412 <lji@research.att.com> | 2017-02-14 15:10:11 +0000 |
commit | 7927ff179242b796330d17869c83fa07751abf95 (patch) | |
tree | ba2b93e26ec71bff863bc7be9fb5dbd0b5d9c928 | |
parent | d1bf35c127a238238b573103edf7dbcb1ebd48ed (diff) |
Init dcae.pgaas
Change-Id: Ieef6b600f4cbb0bf4ee3910c1bfc6b36773cd2d2
Signed-off-by: lj1412 <lji@research.att.com>
200 files changed, 11636 insertions, 0 deletions
diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..0c21233 --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=gerrit.openecomp.org +port=29418 +project=dcae/pgaas.git diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c233514 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,19 @@ +===========LICENSE_START========================================== +=================================================================== +Copyright © 2017 AT&T Intellectual Property. All rights reserved. +=================================================================== +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +============LICENSE_END============================================ + +ECOMP and OpenECOMP are trademarks and service marks of AT&T Intellectual Property. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9c1717e --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# PostgreSQL as a Service (PGaaS) DB VM. + +This package is built on top of the stock PostgreSQL and Repmgr packages. + +PGaaS contains a set of configuration scripts, daemons, and administrative scripts +that allow PostgreSQL to be deployed, configured and managed by the DCAE Controller. +PGaaS may be deployed singly or in a cascaded cluster. + +The running daemons provide health check and other information suitable for the +DCAE Controller and future dashboard access. + + +## Building the Code + +To build the code, use the "make build" command. + +To build the code and upload to a debian repository, use the "make debian" command. diff --git a/cdf/.gitignore b/cdf/.gitignore new file mode 100644 index 0000000..7c32f55 --- /dev/null +++ b/cdf/.gitignore @@ -0,0 +1 @@ +install diff --git a/cdf/src/cdf-prop-value/README.md b/cdf/src/cdf-prop-value/README.md new file mode 100644 index 0000000..101471c --- /dev/null +++ b/cdf/src/cdf-prop-value/README.md @@ -0,0 +1,53 @@ +org.openecomp.dcae.cdf [^1] +====================== + +This repository contails two modules: +* `cdf-util`: a port of the support functions needed to support CdfPortValue command +* `cdf-prop-value` : contains only the CdfPortValue command + +## Building + +To build: +* `cd cdf-util-build; mvn package` + +## Usage + +Command: `/opt/cdf/bin/getpropvalue` + +`/opt/cdf/bin/getpropvalue [-x] -n property -f property-file` + Extract the named value from the given property-file (or full pathname[^2]) + +`/opt/cdf/bin/getpropvalue -e method [-n property] [-s salt] -v value` + Encrypt the given property with the given name and value + +`/opt/cdf/bin/getpropvalue -u value` + Decrypt the given value, expressed as a triple METHOD:HEXSALT:HEXVAL + +`/opt/cdf/bin/setencryptedvalues` (same as `/opt/cdf/bin/getpropvalue -E`) + Encrypt all lines that look like ENCRYPTME.METHOD.name=value + +## Examples + + # using config files: + + # echo ENCRYPTME.AES.input=bogus | /opt/cdf/bin/setencryptedvalues > testconfig.txt + + # cat testconfig.txt + input.x=AES:353438323235:bf046d8a3e8b12fb678f5dec1e9d5743 + + # /opt/cdf/bin/getpropvalue -x -n input -f /home/ht1659/src/cdf/testconfig.txt + bogus + + # No file: + + # /opt/cdf/bin/getpropvalue -e AES -v bogus + AES:34383638353831:0e699f0f818593e3adbc642efed20341 + + # /opt/cdf/bin/getpropvalue -u AES:323937323833:8d95d8803978c4b13497b394d56a4a9c + bogus + + + +[^1]: Version 1.0, 24 Dec 2015 + +[^2]: The property-file valued currently requires a rooted (full) pathname. diff --git a/cdf/src/cdf-prop-value/buildjars/gnu_getopt.jar b/cdf/src/cdf-prop-value/buildjars/gnu_getopt.jar Binary files differnew file mode 100644 index 0000000..8105152 --- /dev/null +++ b/cdf/src/cdf-prop-value/buildjars/gnu_getopt.jar diff --git a/cdf/src/cdf-prop-value/cdf-prop-value/pom.xml b/cdf/src/cdf-prop-value/cdf-prop-value/pom.xml new file mode 100644 index 0000000..208d5f7 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-prop-value/pom.xml @@ -0,0 +1,28 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.openecomp.dcae.cdf</groupId> + <artifactId>cdf-prop-value</artifactId> + <packaging>jar</packaging> + <version>1.0-SNAPSHOT</version> + <name>cdf-prop-value</name> + <url>http://maven.apache.org</url> + + <dependencies> + <dependency> + <groupId>org.openecomp.dcae.cdf</groupId> + <artifactId>cdf-util</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + + </dependencies> + +</project> diff --git a/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/.gitignore b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/.gitignore new file mode 100644 index 0000000..863d673 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/.gitignore @@ -0,0 +1 @@ +javadoc diff --git a/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/cdf-prop-value.jar b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/cdf-prop-value.jar Binary files differnew file mode 100644 index 0000000..ba5a4a7 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/cdf-prop-value.jar diff --git a/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/makefile b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/makefile new file mode 100644 index 0000000..3591a88 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/makefile @@ -0,0 +1,29 @@ +go=../../../../buildjars/gnu_getopt.jar +cu=../../../../cdf-util/src/main/java/cdf-util.jar + +.SUFFIXES= .class + +JAVA=org/openecomp/dcae/cdf/CdfPropValue.java +CLASSES=$(JAVA:.java=.class) + +all: + +build: cdf-prop-value.jar + +classes: $(CLASSES) + +clean: + find . -name '*.class' -exec rm -f {} + + rm -rf javadoc + +cdf-prop-value.jar: classes + rm -f cdf-prop-value.jar + jar cvf cdf-prop-value.jar $(CLASSES) + +$(CLASSES): $(JAVA) + javac -cp $(go):$(cu) $(JAVA) + +javadocs: + rm -rf javadoc + mkdir -p javadoc + javadoc -d javadoc -classpath $(go):$(cu) $(JAVA) diff --git a/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/org/openecomp/dcae/cdf/.gitignore b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/org/openecomp/dcae/cdf/.gitignore new file mode 100644 index 0000000..6b468b6 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/org/openecomp/dcae/cdf/.gitignore @@ -0,0 +1 @@ +*.class diff --git a/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/org/openecomp/dcae/cdf/CdfPropValue.java b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/org/openecomp/dcae/cdf/CdfPropValue.java new file mode 100644 index 0000000..870835a --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-prop-value/src/main/java/org/openecomp/dcae/cdf/CdfPropValue.java @@ -0,0 +1,86 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf; + +import org.openecomp.dcae.cdf.util.config.PropValue; +import java.util.logging.Logger; +import org.openecomp.dcae.cdf.util.config.Configuration; +import java.io.InputStream; +import java.io.PrintStream; + +public class CdfPropValue extends PropValue { + public static String getCDFHOME() { + String optCdf = System.getProperty("CDF_HOME"); + if (optCdf == null) optCdf = System.getenv("CDF_HOME"); + if (optCdf == null) optCdf = "/opt/app/cdf"; + return optCdf; + } + + public static String getGLOBALPROPFILE() { + String optCdfCfg = System.getProperty("CDF_CFG"); + if (optCdfCfg == null) optCdfCfg = System.getenv("CDF_CFG"); + if (optCdfCfg == null) optCdfCfg = getCDFHOME() + "/lib/cdf.cfg"; + return optCdfCfg; + } + + public static void init() { + PropValue.setGlobalPropFile(getGLOBALPROPFILE()); + PropValue.setEncryptionKeyProperty("Global_Title"); + } + static { + init(); + } + + public static void printEncryptedProperty(String method, String name, String salt, String value, String globalPropFile) { + PropValue.printEncryptedProperty(method, name, salt, value, globalPropFile); + } + public static String generateEncryptedProperty(String method, String salt, String value, String globalPropFile) throws Exception { + return PropValue.generateEncryptedProperty(method, salt, value, globalPropFile); + } + public static String generateEncryptedProperty(String method, String salt, String value, PropValue propValue) throws Exception { + return PropValue.generateEncryptedProperty(method, salt, value, propValue); + } + public static void extractProperty(String f, String name, boolean encrypted) { + PropValue.extractProperty(f, name, encrypted); + } + public static void extractProperty(String f, String name, boolean encrypted, String globalPropFile) { + PropValue.extractProperty(f, name, encrypted, globalPropFile); + } + public static String decryptTriple(String triple, String globalPropFile) { + return PropValue.decryptTriple(triple, globalPropFile); + } + public static String decryptTriple(String triple, PropValue propValue) { + return PropValue.decryptTriple(triple, propValue); + } + public static void encryptInput(InputStream in, PrintStream out) throws Exception { + PropValue.encryptInput(in, out); + } + public static void encryptInput() throws Exception { + PropValue.encryptInput(); + } + public static void encryptInput(String globalPropFile, InputStream sysin, PrintStream sysout) throws Exception { + PropValue.encryptInput(globalPropFile, sysin, sysout); + } + public static void main(String args[]) throws Exception { + PropValue.main(args); + } + + + public CdfPropValue(Configuration globalConfig, Logger logger) { + super(globalConfig, logger); + } + +} diff --git a/cdf/src/cdf-prop-value/cdf-prop-value/src/test/java/org/openecomp/dcae/cdf/cdfpropvalue/AppTest.java b/cdf/src/cdf-prop-value/cdf-prop-value/src/test/java/org/openecomp/dcae/cdf/cdfpropvalue/AppTest.java new file mode 100644 index 0000000..23ae351 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-prop-value/src/test/java/org/openecomp/dcae/cdf/cdfpropvalue/AppTest.java @@ -0,0 +1,53 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.cdfpropvalue; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/pom.xml b/cdf/src/cdf-prop-value/cdf-util/pom.xml new file mode 100644 index 0000000..3f012fb --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/pom.xml @@ -0,0 +1,26 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.openecomp.dcae.cdf</groupId> + <artifactId>cdf-util</artifactId> + <packaging>jar</packaging> + <version>1.0-SNAPSHOT</version> + <name>cdf-util</name> + <url>http://maven.apache.org</url> + + <dependencies> + <dependency> + <groupId>gnu.getopt</groupId> + <artifactId>java-getopt</artifactId> + <version>1.0.13</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/.gitignore b/cdf/src/cdf-prop-value/cdf-util/src/main/java/.gitignore new file mode 100644 index 0000000..863d673 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/.gitignore @@ -0,0 +1 @@ +javadoc diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/cdf-util.jar b/cdf/src/cdf-prop-value/cdf-util/src/main/java/cdf-util.jar Binary files differnew file mode 100644 index 0000000..8c43f9a --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/cdf-util.jar diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/makefile b/cdf/src/cdf-prop-value/cdf-util/src/main/java/makefile new file mode 100644 index 0000000..af2f0b0 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/makefile @@ -0,0 +1,47 @@ + +go=../../../../buildjars/gnu_getopt.jar + +.SUFFIXES= .class + +JAVA= org/openecomp/dcae/cdf/util/common/Classify.java \ + org/openecomp/dcae/cdf/util/common/Tuple3.java \ + org/openecomp/dcae/cdf/util/common/AsHex.java \ + org/openecomp/dcae/cdf/util/common/G2CookieGen.java \ + org/openecomp/dcae/cdf/util/common/Compress.java \ + org/openecomp/dcae/cdf/util/common/Pair.java \ + org/openecomp/dcae/cdf/util/common/Tuple4.java \ + org/openecomp/dcae/cdf/util/common/Hostname.java \ + org/openecomp/dcae/cdf/util/common/Uid.java \ + org/openecomp/dcae/cdf/util/common/Tuple2.java \ + org/openecomp/dcae/cdf/util/common/Pid.java \ + org/openecomp/dcae/cdf/util/common/Convert.java \ + org/openecomp/dcae/cdf/util/common/Popen.java \ + org/openecomp/dcae/cdf/util/config/PropValue.java \ + org/openecomp/dcae/cdf/util/config/Configuration.java \ + org/openecomp/dcae/cdf/util/config/EncryptedConfiguration.java \ + org/openecomp/dcae/cdf/util/config/Configurable.java \ + org/openecomp/dcae/cdf/util/config/ConfigurationRegistry.java \ + org/openecomp/dcae/cdf/util/threads/TaskThread.java \ + org/openecomp/dcae/cdf/util/threads/ThreadCommon.java + +CLASSES=$(JAVA:.java=.class) + +all: + +build: cdf-util.jar + +cdf-util.jar: $(CLASSES) + rm -f cdf-util.jar + jar cvf cdf-util.jar $(CLASSES) + +clean: + find . -name '*.class' -exec rm -f {} + + rm -rf javadoc + +$(CLASSES): $(JAVA) + javac -cp $(go) $(JAVA) + +javadocs: + rm -rf javadoc + mkdir -p javadoc + javadoc -d javadoc -classpath $(go) $(JAVA) diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/.gitignore b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/.gitignore new file mode 100644 index 0000000..6b468b6 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/.gitignore @@ -0,0 +1 @@ +*.class diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/AsHex.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/AsHex.java new file mode 100644 index 0000000..9fcbdbf --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/AsHex.java @@ -0,0 +1,116 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +public class AsHex +{ + public static String asHex(byte[] data, int offset, int length, String sep) { + return asHex(data, offset, length, true); + } + public static String asHex(byte[] data, String sep) { + return asHex(data, 0, data.length, sep); + } + public static String asHex(byte[] data, int offset, int length) { + return asHex(data, offset, length, " "); + } + public static String asHex(byte[] data) { + return asHex(data, 0, data.length); + } + + public static String asHex(String data) { + return asHex(data.getBytes()); + } + + static private int asHexBlockLength = 16; + public static void setAsHexBlockLength(int n) { asHexBlockLength = n; } + public static int getAsHexBlockLength() { return asHexBlockLength; } + + private final static char[] hexdigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + /** + * return a byte buf as a hex string + */ + public static String asHex(byte[] buf, int offset, int length, boolean addFinalNL) { + StringBuilder ret = new StringBuilder(); + return asHex(ret, buf, offset, length, addFinalNL).toString(); + } + + /** + * Return a byte buf as hex into the provided StringBuilder. + */ + public static StringBuilder asHex(StringBuilder ret, byte[] buf, int offset, int length, boolean addFinalNL) { + final int blockLength = asHexBlockLength; + for (int o = 0; o < length; o += blockLength) { + int iend = (o + blockLength < length) ? (o + blockLength) : length; + int pend = o + blockLength; + for (int i = o; i < iend; i++) { + int b = (int)(buf[i+offset] & 0xFF); + ret.append(hexdigits[b/16]); + ret.append(hexdigits[b%16]); + } + for (int i = iend; i < pend; i++) { + ret.append(" "); + } + ret.append(" "); + for (int i = o; i < iend; i++) { + byte b = buf[i+offset]; + int ib = (int)(b & 0xFF); + if ((ib >= 0x20) && (ib < 0x7f)) ret.append((char)b); + else ret.append('.'); + } + if (iend < length) ret.append('\n'); + } + if (addFinalNL && (length%blockLength != 0)) ret.append('\n'); + return ret; + } + + /** + * Return a byte buf as hex with a maximum number of lines. + */ + public static String asHexWithMaxLines(byte[] buf, int offset, int length, int maxLines, boolean addFinalNL) { + StringBuilder ret = new StringBuilder(); + return asHexWithMaxLines(ret, buf, offset, length, maxLines, addFinalNL).toString(); + } + + /** + * Return a byte buf as hex into the provided StringBuilder with a maximum number of lines. + */ + public static StringBuilder asHexWithMaxLines(StringBuilder ret, byte[] buf, int offset, int length, int maxLines, boolean addFinalNL) { + int bytesToPrint = length - offset; + if (maxLines < 1) maxLines = 1; + int maxBytesToPrint = maxLines * asHexBlockLength; + if (bytesToPrint <= maxBytesToPrint) { + return asHex(ret, buf, offset, length, addFinalNL); + } else { + if (bytesToPrint > maxBytesToPrint) bytesToPrint = maxBytesToPrint; + asHex(ret, buf, offset, offset + bytesToPrint, false); + ret.append(" ...."); + if (addFinalNL) ret.append("\n"); + return ret; + // return asHex(ret, buf, length - halfBytesToPrint, length, addFinalNL); + } + } + + // Convert a hex string back to a byte array. + // This assumes that there is no whitespace within the string. + // public static byte[] fromHex(String hexStr) { + // byte[] bts = new byte[hexStr.length() / 2]; + // for (int i = 0; i < bts.length; i++) { + // bts[i] = (byte) Integer.parseInt(hexStr.substring(2*i, 2*i+2), 16); + // } + // return bts; + // } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Classify.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Classify.java new file mode 100644 index 0000000..2334579 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Classify.java @@ -0,0 +1,109 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +import java.lang.Character; + +/** + * Classify holds various checking functions. + */ +public final class Classify { + + /** + * isHex(ch) - is a character a hex value? + * + * @param ch (char) + * @return boolean + */ + public static boolean isHex(char ch) { + return (ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f'); + } + + /** + * isValidGuid + * + * @param input (String) + * @return boolean + */ + + public static boolean isValidGuid(String input) { + // Checks if the GUID has the following format: "0f114b6f-3f1d-4c8f-a065-2a2ec3d0f522" + + if ( (input == null) || (input.length() != 36)) return false; + + for (int i=0; i < 36; i++) { + char c = input.charAt(i); + if ( (i==8 || i==13 || i==18 || i==23)) { + if (c != '-') return false; + } + else if (!isHex(c)) return false; + } + return true; + } + + + /** + * isValidClli + * + * @param input (String) + * @return boolean + */ + + public static boolean isValidClli(String input) { + // Checks if the CLLI only contains letters or digits. + + if (input == null) return false; + int len = input.length(); + if (len == 0) return false; + + for (int i=0; i < len; i++) { + char c = input.charAt(i); + if (!Character.isLetterOrDigit(c)) return false; + } + return true; + } + + + /** + * isValidCanonicalIpv4Address + * + * @param ipAddress (String) + * @return boolean + */ + + public static boolean isValidCanonicalIpv4Address(String ipAddress) { + + String[] parts = ipAddress.split( "\\." ); + + if ( parts.length != 4 ) { + return false; + } + for ( String s : parts ) { + try { + int i = Integer.parseInt( s ); + if ( (i < 0) || (i > 255) ) { + return false; + } + } catch (Exception ex) { + return false; + } + } + + return true; + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Compress.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Compress.java new file mode 100644 index 0000000..add6eca --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Compress.java @@ -0,0 +1,131 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +import java.util.zip.GZIPOutputStream; +import java.util.zip.ZipOutputStream; +import java.util.zip.ZipEntry; +// import java.io.InputStream; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.File; +import java.io.IOException; + +public class Compress { + + /** + * Compress a file with the gzip algorithm, sending output to outFilename. + * Based on code at http://www.java-tips.org/java-se-tips/java.util.zip/how-to-compress-a-file-in-the-gip-format.html. + */ + public static void gzip(String inFilename, String outFilename) throws IOException { + String tmpFilename = outFilename + ".tmp"; + try { + // Create the GZIP output stream + GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(tmpFilename)); + + // Open the input file + FileInputStream in = new FileInputStream(inFilename); + + // Transfer bytes from the input file to the GZIP output stream + byte[] buf = new byte[4096]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + in.close(); + + // Complete the GZIP file + out.finish(); + out.close(); + + // rename .gz.tmp to .gz + File target = new File(outFilename); + if (target.exists()) target.delete(); + File file = new File(tmpFilename); + boolean result = file.renameTo(target); + if (!result) throw new IOException("Cannot rename " + tmpFilename + " to " + outFilename); + } catch (IOException e) { + // If we can't write the gzip file, remove it and pass on the exception. + File f = new File(outFilename); + f.delete(); + throw e; + } + } + + /** + * Compress a file with the gzip algorithm, sending output to filename+".gz". + */ + public static void gzip(String filename) throws IOException { + gzip(filename, filename + ".gz"); + } + + /** + * Compress a file with the zip algorithm, sending output to outFilename + * Based on code at http://www.java-tips.org/java-se-tips/java.util.zip/how-to-compress-a-file-in-the-gip-format.html. + */ + public static void zip(String inFilename, String outFilename) throws IOException { + String tmpFilename = outFilename + ".tmp"; + try { + // Create the ZIP output stream + ZipOutputStream out = new ZipOutputStream(new FileOutputStream(tmpFilename)); + ZipEntry zipEntry = new ZipEntry(inFilename); + out.putNextEntry(zipEntry); + + // Open the input file + FileInputStream in = new FileInputStream(inFilename); + + // Transfer bytes from the input file to the ZIP output stream + byte[] buf = new byte[4096]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + in.close(); + + // Complete the ZIP file + out.finish(); + out.close(); + + // rename .zip.tmp to .zip + File target = new File(outFilename); + if (target.exists()) target.delete(); + File file = new File(tmpFilename); + boolean result = file.renameTo(target); + if (!result) throw new IOException("Cannot rename " + tmpFilename + " to " + outFilename); + } catch (IOException e) { + // If we can't write the zip file, remove it and pass on the exception. + File f = new File(outFilename); + f.delete(); + throw e; + } + } + + /** + * Compress a file with the gzip algorithm, sending output to filename+".zip". + */ + public static void zip(String filename) throws IOException { + zip(filename, filename + ".zip"); + } + + public static void main(String args[]) throws Exception { + if (args.length == 1) { + gzip(args[0]); + zip(args[0]); + } else { + System.err.println("Usage: java Compress filename"); + } + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Convert.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Convert.java new file mode 100644 index 0000000..9deba10 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Convert.java @@ -0,0 +1,97 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +import java.lang.Character; + +/** + * Covert holds various conversion functions. + */ +public final class Convert { + + private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); + + /** + * toHexString(String) - convert a string into its hex equivalent + */ + public static String toHexString(String buf) { + if (buf == null) return ""; + return toHexString(buf.getBytes()); + } + + /** + * toHexString(byte[]) - convert a byte-string into its hex equivalent + */ + public static String toHexString(byte[] buf) { + if (buf == null) return ""; + char[] chars = new char[2 * buf.length]; + for (int i = 0; i < buf.length; ++i) { + chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4]; + chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F]; + } + return new String(chars); + } + + // alternate implementation that's slightly slower + // protected static final byte[] Hexhars = { + // '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + // }; + // public static String encode(byte[] b) { + // StringBuilder s = new StringBuilder(2 * b.length); + // for (int i = 0; i < b.length; i++) { + // int v = b[i] & 0xff; + // s.append((char)Hexhars[v >> 4]); + // s.append((char)Hexhars[v & 0xf]); + // } + // return s.toString(); + // } + + /** + * Convert a hex string to its equivalent value. + */ + public static String stringFromHex(String hexString) throws Exception { + if (hexString == null) return ""; + return stringFromHex(hexString.toCharArray()); + } + + public static String stringFromHex(char[] hexCharArray) throws Exception { + if (hexCharArray == null) return ""; + return new String(bytesFromHex(hexCharArray)); + } + + public static byte[] bytesFromHex(String hexString) throws Exception { + if (hexString == null) return new byte[0]; + return bytesFromHex(hexString.toCharArray()); + } + + public static byte[] bytesFromHex(char[] hexCharArray) throws Exception { + if (hexCharArray == null) return new byte[0]; + int len = hexCharArray.length; + if ((len % 2) != 0) throw new Exception("Odd number of characters: '" + hexCharArray + "'"); + byte [] txtInByte = new byte [len / 2]; + int j = 0; + for (int i = 0; i < len; i += 2) { + txtInByte[j++] = (byte)(((fromHexDigit(hexCharArray[i], i) << 4) | fromHexDigit(hexCharArray[i+1], i)) & 0xFF); + } + return txtInByte; + } + + protected final static int fromHexDigit(char ch, int index) throws Exception { + int digit = Character.digit(ch, 16); + if (digit == -1) throw new Exception("Illegal hex character '" + ch + "' at index " + index); + return digit; + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/G2CookieGen.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/G2CookieGen.java new file mode 100644 index 0000000..aeed01b --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/G2CookieGen.java @@ -0,0 +1,209 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +import javax.crypto.Cipher; +// import javax.crypto.SecretKey; +// import javax.crypto.KeyGenerator; +// import javax.crypto.spec.IvParameterSpec; +import java.security.Key; +// import java.security.NoSuchAlgorithmException; +// import java.security.SecureRandom; +// import javax.crypto.SecretKey; +// import sun.misc.*; +import java.util.*; + +public class G2CookieGen +{ + private Cipher cipher; + private Key key = null; + + private static String alg = "DES"; + private static String desecb = "DES/ECB/PKCS5Padding"; + + public static String G2_CLIENT_MEC_ID_1 = "MEC0001"; + private static String G2_CLIENT_MEC_ID_2 = "MEC0002"; + public static String G2_ENCRYPT_KEY = "secretK9"; + public static String G2_EPOCH_TM_STR = null; + + + private static long G2_TM_DELTA_IN_MILLISECONDS = 10*60*1000; + + class G2WSSKey implements Key + { + private final byte[] keyBytes; + private final String alg; + + G2WSSKey(String algorithm, byte[] keyBytes) + { + this.alg = algorithm; + this.keyBytes = keyBytes; + } + + public String getAlgorithm() + { + return alg; + } + public String getFormat() + { + return "RAW"; + } + public byte[] getEncoded() + { + return (byte[])keyBytes.clone(); + } + } + + + public G2CookieGen() { + try { + cipher = Cipher.getInstance(desecb); + } catch (Throwable t) { + System.err.println(t.toString()); + return; + } + } + + + public static String getClient1MacId() { + return G2_CLIENT_MEC_ID_1; + } + + public static String getClient2MacId() { + return G2_CLIENT_MEC_ID_2; + } + + public static String toHexStringFromByteArray(byte[] bytes) + { + StringBuilder retString = new StringBuilder(); + for (int i = 0; i < bytes.length; ++i) { + retString.append(Integer.toHexString(0x0100 + (bytes[i] & 0x00FF)).substring(1)); + } + return retString.toString(); + } + + public static byte[] toByteArrayFromHexString(String hexStr) + { + byte[] bts = new byte[hexStr.length() / 2]; + for (int i = 0; i < bts.length; i++) { + bts[i] = (byte) Integer.parseInt(hexStr.substring(2*i, 2*i+2), 16); + } + return bts; + } + + public byte[] encryptData(String sData) + { + try { + byte[] data = sData.getBytes(); + //System.out.println("Original data : " + new String(data)); + if (key == null) setKey(G2_ENCRYPT_KEY); + cipher.init(Cipher.ENCRYPT_MODE, key); + byte[] result = cipher.doFinal(data); + return result; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public String decryptData(byte[] sData) + { + try { + cipher.init(Cipher.DECRYPT_MODE, key); + byte[] result = cipher.doFinal(sData); + return new String(result); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public String constructCookie(String mechId) { + return mechId + ":" + System.currentTimeMillis(); + } + + public void setKey(String g2EncryptKey) { + key = new G2WSSKey(this.alg, g2EncryptKey.getBytes()); + } + + public String getEncryptedCookie(String mechId, String g2EncryptKey) { + setKey(g2EncryptKey); + String tmp = constructCookie(mechId); + byte[] byteArray = this.encryptData(tmp); + return this.toHexStringFromByteArray(byteArray); + } + + public long getTimeMillisFromCookie(String cookie) { + StringTokenizer tkn = new StringTokenizer(cookie,":"); + String tmStr = null; + while (tkn.hasMoreTokens()) { + tmStr = tkn.nextToken(); + } + Long tmLong = new Long(tmStr); + return tmLong.longValue(); + } + + public boolean isValid(long tm) { + long ctm = System.currentTimeMillis(); +System.out.println("Current Time="+ctm); +System.out.println("G2_TM_DELTA_IN_MILLISECONDS="+G2_TM_DELTA_IN_MILLISECONDS); + if ( Math.abs(ctm - tm) <= G2_TM_DELTA_IN_MILLISECONDS ) { + return true; + } + return false; + } + + + public static void main(String argv[]) { + try { + if (argv.length > 0) { +System.out.println("using Client MACID="+argv[0]); + G2_CLIENT_MEC_ID_1 = argv[0]; + + } + + if (argv.length > 1) { + if (argv[1].length() == 8) { +System.out.println("using Key="+argv[1]); + G2_ENCRYPT_KEY = argv[1]; + } + } + + if (argv.length > 2) { +System.out.println("using Epoch Time (in seconds) ="+argv[2]); + G2_EPOCH_TM_STR = argv[2]; + } + + + G2CookieGen wssc = new G2CookieGen(); + +// System.out.println("tz_diff="+G2_CLIENT_TM_ZONE_TO_PDT_IN_MILLISECONDS); +System.out.println("macid="+G2_CLIENT_MEC_ID_1); + + String cookie = wssc.constructCookie(G2_EPOCH_TM_STR); +System.out.println("original cookie="+cookie); + + byte[] byteArrary = wssc.encryptData(cookie); + String hexString = wssc.toHexStringFromByteArray(byteArrary); +System.out.println("encrypted cookie="+hexString); + System.exit(0); + + } catch (Exception e) { + System.err.println("Error: " + e); + System.exit(1); + } + } /* main */ +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Hostname.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Hostname.java new file mode 100644 index 0000000..56f312b --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Hostname.java @@ -0,0 +1,108 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +import java.net.InetAddress; + +public class Hostname { + + /** + * Hostname FQDN + */ + public static String getHostName() { + return getHostName("unknown.unknown"); + } + + /** + * Hostname FQDN + */ + public static String getHostName(String def) { + return (uname == null) ? def : hostName; + } + + /** + * uname, the 1st portion of the hostname FQDN + */ + public static String getUname() { + return getUname("unknown"); + } + + /** + * uname, the 1st portion of the hostname FQDN + */ + public static String getUname(String def) { + return (uname == null) ? def : uname; + } + + /** + * Get an IP address for this machine + */ + public static String getLocalIP() { + return defaultLocalIP; + } + /** + * Get an IP address for this machine + */ + public static String getLocalIPinHex() { + return defaultLocalIPinHex; + } + /** + * Get a host name for this machine + */ + public static String getCanonicalHostName() { + return defaultCanonicalHostName; + } + + /** + * Value returned by getLocalIP() method + */ + private static String defaultLocalIP; + private static String defaultLocalIPinHex; + private static String defaultCanonicalHostName; + private static String hostName = null; // Hostname FQDN + private static String uname = null; // Hostname 1st part + + static { + try { + InetAddress ia = InetAddress.getLocalHost(); + defaultLocalIP = ia.getHostAddress(); + byte b[] = ia.getAddress(); + defaultLocalIPinHex = Convert.toHexString(b); + defaultCanonicalHostName = ia.getCanonicalHostName(); + } catch (Exception e) { + defaultLocalIP = "127.0.0.1"; + defaultLocalIPinHex = "7F000001"; + defaultCanonicalHostName = "localhost"; + } + + try { + hostName = InetAddress.getLocalHost().getHostName(); + String hostNameParts[] = hostName.split("\\."); + uname = hostNameParts[0]; + } catch (Exception ex) { + } + int dotInHostname = hostName.indexOf('.'); + if (dotInHostname > -1) hostName = hostName.substring(0, dotInHostname); + } + + public static void main(String args[]) { + System.out.println("getHostName() = '" + getHostName() + "'"); + System.out.println("getUname() = '" + getUname() + "'"); + System.out.println("getLocalIP() = '" + getLocalIP() + "'"); + System.out.println("getLocalIPinHex() = '" + getLocalIPinHex() + "'"); + System.out.println("getCanonicalHostName() = '" + getCanonicalHostName() + "'"); + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Pair.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Pair.java new file mode 100644 index 0000000..524aa6b --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Pair.java @@ -0,0 +1,40 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +public final class Pair<L,R> { + public final L left; + public final R right; + public Pair(L l, R r) { left = l; right = r; } + + @Override + public boolean equals(Object obj) { + Pair<L,R> o = (Pair<L,R>)obj; + return left.equals(o.left) && right.equals(o.right); + } + @Override + public String toString() { + return "(" + left + "," + right + ")"; + } + @Override + public int hashCode() { + return left.hashCode() + right.hashCode(); + } + + public static <L,R> Pair<L,R> of(L l, R r) { + return new Pair<L,R>(l, r); + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Pid.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Pid.java new file mode 100644 index 0000000..1730509 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Pid.java @@ -0,0 +1,37 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; +import java.io.File; + +public class Pid { + /** + * Return the pid. + */ + public static int getPid() { return pid; } + public static String getPidStr() { return pidStr; } + + private static int pid; + private static String pidStr; + static { + try { + pid = Integer.parseInt( ( new File("/proc/self")).getCanonicalFile().getName() ); + pidStr = Integer.toString(pid); + } catch (java.io.IOException e) { + pid = -1; + pidStr = "-1"; + } + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Popen.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Popen.java new file mode 100644 index 0000000..6d5e8fb --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Popen.java @@ -0,0 +1,84 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +public class Popen { + public static class Results { + public final String stdout, stderr; + public final int exitValue; + public Results(String so, String se, int e) { + stdout = so; stderr = se; exitValue = e; + } + } + + public static Results popen(String cmd) throws java.io.IOException, java.lang.InterruptedException { + return popen(cmd, null); + } + + public static Results popen(String cmd, String stdin) throws java.io.IOException, java.lang.InterruptedException { + Process process = Runtime.getRuntime().exec(cmd); + return proc(process, stdin); + } + + public static Results popen(String[] args) throws java.io.IOException, java.lang.InterruptedException { + return popen(args, null); + } + + public static Results popen(String[] args, String stdin) throws java.io.IOException, java.lang.InterruptedException { + Process process = Runtime.getRuntime().exec(args); + return proc(process, stdin); + } + + private static Results proc(Process process, String stdin) throws java.io.IOException, java.lang.InterruptedException { + OutputStream pinput = process.getOutputStream(); + InputStream poutput = process.getInputStream(); + InputStream perror = process.getErrorStream(); + + if (stdin != null) + pinput.write(stdin.getBytes()); + pinput.close(); + + String stdout = captureStream(poutput); + poutput.close(); + String stderr = captureStream(perror); + perror.close(); + process.waitFor(); + // System.out.println("stdin=\nnvvvvvvvvvvvvvvvv\n"); + // System.out.println(stdin); + // System.out.println("^^^^^^^^^^^^^^^^"); + // System.out.println("stdout=\nvvvvvvvvvvvvvvvv\n"); + // System.out.println(stdout); + // System.out.println("^^^^^^^^^^^^^^^^"); + // System.out.println("stderr=\nvvvvvvvvvvvvvvvv\n"); + // System.out.println(stderr); + // System.out.println("^^^^^^^^^^^^^^^^"); + return new Results(stdout, stderr, process.exitValue()); + } + + private static String captureStream(InputStream inp) throws java.io.IOException { + byte[] buf = new byte[8192]; + StringBuffer out = new StringBuffer(); + int b; + while ((b = inp.read(buf)) > 0) { + out.append(new String(buf, 0, b)); + } + return out.toString(); + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple2.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple2.java new file mode 100644 index 0000000..dd48ea8 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple2.java @@ -0,0 +1,25 @@ +// -*- indent-tabs-mode: nil -*- +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +public class Tuple2<T1,T2> { + public Tuple2(T1 n1, T2 n2) { + t1 = n1; t2 = n2; + } + public final T1 t1; + public final T2 t2; +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple3.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple3.java new file mode 100644 index 0000000..1293059 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple3.java @@ -0,0 +1,29 @@ +// -*- indent-tabs-mode: nil -*- +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +public class Tuple3<T1,T2,T3> extends Tuple2<T1,T2> { + public Tuple3(T1 n1, T2 n2, T3 n3) { + super(n1, n2); + t3 = n3; + } + public Tuple3(Tuple3<T1,T2,T3> t) { + super(t.t1, t.t2); + t3 = t.t3; + } + public final T3 t3; +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple4.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple4.java new file mode 100644 index 0000000..a2c10e8 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Tuple4.java @@ -0,0 +1,29 @@ +// -*- indent-tabs-mode: nil -*- +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; + +public class Tuple4<T1,T2,T3,T4> extends Tuple3<T1,T2,T3> { + public Tuple4(T1 n1, T2 n2, T3 n3, T4 n4) { + super(n1, n2, n3); + t4 = n4; + } + public Tuple4(Tuple4<T1,T2,T3,T4> t) { + super(t.t1, t.t2, t.t3); + t4 = t.t4; + } + public final T4 t4; +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Uid.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Uid.java new file mode 100644 index 0000000..6b2219f --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/common/Uid.java @@ -0,0 +1,69 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.common; +import java.io.File; +import java.io.BufferedReader; +import java.io.FileReader; +import org.openecomp.dcae.cdf.util.common.Popen; + +public class Uid { + /** + * Return the uid. + */ + public static int getUid() { return uid; } + public static String getUidStr() { return uidStr; } + + private static int uid = -1; + private static String uidStr = ""; + static { + try { + uid = getUidFromProcSelfStatus(); + if (uid == -1) uid = getUidFromIdU(); + uidStr = Integer.toString(uid); + } catch (java.io.IOException e) { + uid = -1; + uidStr = "-1"; + System.err.println("Exception: " + e); + } catch (Exception e) { + System.err.println("Exception: " + e); + } + + } + + private static int getUidFromProcSelfStatus() throws java.io.IOException { + int uid = -1; + if (true) return -1; + BufferedReader br = new BufferedReader(new FileReader(new File("/proc/self/status"))); + String thisLine = null; + while ((thisLine = br.readLine()) != null) { + if (thisLine.startsWith("Uid:")) { + String[] uids = thisLine.split("[: \t]+"); + if (uids.length > 1) { + uid = Integer.parseInt(uids[1]); + break; + } + } + } + br.close(); + return uid; + } + + private static int getUidFromIdU() throws java.io.IOException, java.lang.InterruptedException { + Popen.Results results = Popen.popen("/usr/bin/id -u"); + uid = Integer.parseInt(results.stdout.trim()); + return uid; + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/.gitignore b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/.gitignore new file mode 100644 index 0000000..6b468b6 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/.gitignore @@ -0,0 +1 @@ +*.class diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/Configurable.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/Configurable.java new file mode 100644 index 0000000..39e78ca --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/Configurable.java @@ -0,0 +1,28 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.config; + +/** + * The interface for an object that wants to be notified when the + * configuration files have changed (so it can re-configure itself) + */ + +public interface Configurable { + /** + * Configuration files have changed. + */ + public void reConfigure(); +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/Configuration.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/Configuration.java new file mode 100644 index 0000000..54ebf47 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/Configuration.java @@ -0,0 +1,579 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.config; + +import java.io.*; +import java.util.*; +import java.net.*; +import java.util.logging.*; +import org.openecomp.dcae.cdf.util.common.*; +import org.openecomp.dcae.cdf.util.threads.*; + +/** + * Class to monitor configuration parameters and notify + * other objects when they change + */ + +public class Configuration extends Thread implements Configurable { + /** + * Time between checks of config file in milliseconds + */ + private static int INTERVAL = 30000; + private int interval = INTERVAL; + /** + * Minimum age of config file before loading it in milliseconds + */ + private static final int MINAGE = 30000; + private int minage = MINAGE; + /** + * Value returned by getInstance() method + */ + private static Configuration defaultInstance = new Configuration(); + /** + * Value returned by getLocalIP() method + */ + private static String defaultLocalIP; + private static String defaultLocalIPinHex; + private static String defaultCanonicalHostName; + static { + try { + InetAddress ia = InetAddress.getLocalHost(); + defaultLocalIP = ia.getHostAddress(); + byte b[] = ia.getAddress(); + defaultLocalIPinHex = Convert.toHexString(b); + defaultCanonicalHostName = ia.getCanonicalHostName(); + } catch (Exception e) { + defaultLocalIP = "127.0.0.1"; + defaultLocalIPinHex = "7F000001"; + defaultCanonicalHostName = "localhost"; + } + } + /** + * Get an IP address for this machine + */ + public static String getLocalIP() { + return defaultLocalIP; + } + /** + * Get an IP address for this machine + */ + public static String getLocalIPinHex() { + return defaultLocalIPinHex; + } + /** + * Get a host name for this machine + */ + public static String getCanonicalHostName() { + return defaultCanonicalHostName; + } + /** + * Get a default global instance + */ + public static Configuration getInstance() { + return defaultInstance; + } + /** + * The current configuration + */ + private ResourceBundle config; + /** + * Where to log when things go wrong + */ + private static Logger logger = Logger.getLogger(Configuration.class.getName()); + /** + * The config file to read + */ + private File file; + /** + * The name of the config to read, when overriding the file. + */ + private String filename; + /** + * The last modified date of the config file + */ + private long curdate; + /** + * Should we stop scanning for config file updates? + */ + private boolean closed; + static boolean closeAll = false; + /** + * Have we started scanning for config file updates? + */ + private boolean initialized; + /** + * The name of the background thread monitoring the file. + */ + private static String monitorThreadName = "Configuration Monitor"; + /** + * How we keep track of registered Configurables. + */ + private ConfigurationRegistry configurationRegistry = new ConfigurationRegistry(); + /** + * included file. + */ + private Configuration subConfig = null; + private String subFile = null; + + public void reConfigure() { + configurationRegistry.reConfigureAll(logger); + } + + /** + * Create an instance using the default configuration file + * "configfile.properties" from the class path + */ + public Configuration() { + // logger.fine("Configuration()"); + } + /** + * Create an instance using a configuration file + * "FILENAME.properties" from the class path + */ + public Configuration(String filename) { + // logger.fine("Configuration(" + filename + ")"); + this.filename = filename; + } + /** + * Create an instance using a specific configuration file + */ + public Configuration(File file) { + this.file = file; + // logger.fine("Configuration(File)"); + } + + /** + * Change the configuration file to use + */ + public void setConfig(File file) { + this.file = file; + curdate = 0; + interrupt(); + } + /** + * Reset the interval used for rechecking the file. + * @param interval + */ + public synchronized void setInterval(int interval) { + this.interval = interval; + } + /** + * Reset the default interval used for rechecking the file. + * @param interval + */ + public synchronized void setDefaultInterval(int interval) { + this.INTERVAL = interval; + } + /** + * Reset the minimum age the file must be before being reread. + * This is used to prevent reading the file while it is being written, say by vi. + * @param minage + */ + public synchronized void setMinage(int minage) { + this.minage = minage; + } + /** + * Stop checking for config changes + */ + public void close() { + checkinit(); + closed = true; + if (Thread.currentThread() == this) { + return; + } + interrupt(); + try { + join(); + } catch (Exception e) { + } + } + /** + * Check the config file to see if it has changed + */ + private synchronized void check() { + long now = System.currentTimeMillis(); + if (logger.isLoggable(Level.FINE)) logger.fine("check(): now=" + Long.toString(now)); + try { + long ndate = file.lastModified(); + if (logger.isLoggable(Level.FINE)) logger.fine("file=" + file + ", ndate=" + Long.toString(ndate) + ", curdate=" + Long.toString(curdate,10)); + if (ndate == curdate || (now < ndate + minage && curdate != 0)) { + return; + } + if (logger.isLoggable(Level.FINE)) logger.fine("reloading file=" + file); + FileInputStream in = new FileInputStream(file); + config = new PropertyResourceBundle(in); + in.close(); + try { + String inc = config.getString("include"); + if ((inc != null) && !inc.equals("")) { + subFile = inc; + subConfig = new Configuration(subFile); + subConfig.registerConfigurable(this); + } + } catch (Exception e) { + } + + curdate = ndate; + configurationRegistry.reConfigureAll(logger); + // logger.info("CNFG0006: Configuration '" + file + "' reloaded"); + } catch (Exception e) { + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0004: Configuration file '" + file + "' inaccessible", e); + } + } + /** + * Make sure we're initialized and read the config file + * if necessary + */ + public void checkinit() { + // System.out.println("checkinit()"); + if (initialized) { + return; + } + initialized = true; + try { + if (file == null) { + if (filename == null) + filename = System.getProperty("configfile", "configfile"); + // logger.info("DAIS0073 0.8.73 >>> filename=" + filename); + if (filename.charAt(0) == '/') { + // logger.info("DAIS0073 0.8.73 filename has leading slash: " + filename); + file = new File(filename); + } else { + URI uri = getClass().getClassLoader().getResource(filename + ".properties").toURI(); + // logger.info("DAIS0073 0.8.73 uri=" + uri.toString()); + file = new File(uri); + } + } + } catch (Exception e) { + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0003: Cannot find configuration file '" + filename + "'", e); + } + check(); + setDaemon(true); + setName(monitorThreadName); + start(); + } + /** + * Check the config file to see if it has changed + */ + public void run() { + if (logger.isLoggable(Level.FINE)) logger.fine("Configuration::run()"); + while (!closed && !closeAll) { + try { + if (logger.isLoggable(Level.FINE)) logger.fine("sleeping " + Integer.toString(interval) + ", id=" + Long.toString(Thread.currentThread().getId()) + ", file=" + filename); + Thread.sleep(interval); + } catch (Exception e) { + } + if (logger.isLoggable(Level.FINE)) { + Thread currentThread = Thread.currentThread(); + logger.fine("checking id=" + Long.toString((currentThread != null) ? currentThread.getId() : -1) + ", file=" + filename); + } + check(); + } + } + + public static void wakeAllThreads() { + try { + Thread[] threads = ThreadCommon.getAllThreads( monitorThreadName ); + for ( Thread thread : threads ) + thread.interrupt(); + } catch (Exception e) { + } + } + + public static void closeAllThreads() { + closeAll = true; + wakeAllThreads(); + } + + /** + * Forward this Configurable to the ConfigurationRegistry to be registered. + */ + public void registerConfigurable(Configurable element) { + configurationRegistry.registerConfigurable(element); + } + /** + * Forward this Configurable to the ConfigurationRegistry to be deRegistered. + */ + public void deRegisterConfigurable(Configurable element) { + configurationRegistry.deRegisterConfigurable(element); + } + + /** + * Get a configuration parameter as a String. + * If undefined, return null and log an error. + * @return String + */ + public String getString(String name) { + return getString(name, null, true); + } + /** + * Get a configuration parameter as a String. + * If undefined, return the specified default value. + * @return String + */ + public String getString(String name, String deflt) { + return getString(name, deflt, false); + } + + public static String trimQuotes(String str) { + if (str == null) return null; + str = str.trim(); + int len = str.length(); + if (len < 2) return str; + char startChar = str.charAt(0); + char endChar = str.charAt(len-1); + boolean startDoubleQuote = startChar == '"'; + boolean startSingleQuote = startChar == '\''; + boolean endDoubleQuote = endChar == '"'; + boolean endSingleQuote = endChar == '\''; + if ((startDoubleQuote && endDoubleQuote) || + (startSingleQuote && endSingleQuote)) { + return str.substring(1, len-1); + } else { + return str; + } + } + + /** + * Get a configuration parameter as a String. + * If undefined, return the specified default value. + * If complaining, log an error. + * @return String + */ + public String getString(String name, String deflt, boolean complain) { + checkinit(); + try { + return trimQuotes(config.getString(name)); + } catch (Exception e) { + if (subConfig != null) { + try { + return subConfig.getString(name, deflt, complain); + } catch (Exception e2) { + } + } + if (complain) + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0001: '" + filename + "': Configuration property " + name + " must be defined", e); + return deflt; + } + } + + /** + * Get a configuration parameter as a String encoded using URL % escapes. + * If undefined, return null and log an error. + * @return String + */ + public String getDecodedString(String name) { + return getDecodedString(name, null, true); + } + /** + * Get a configuration parameter as a String encoded using URL % escapes. + * If undefined, return the specified default value. + * @return String + */ + public String getDecodedString(String name, String deflt) { + return getDecodedString(name, deflt, false); + } + /** + * Get a configuration parameter as a String encoded using URL % escapes. + * If undefined, return the specified default value. + * If complaining, log an error. + * @return String + */ + public String getDecodedString(String name, String deflt, boolean complain) { + checkinit(); + try { + return URLDecoder.decode(config.getString(name), "UTF-8"); + } catch (UnsupportedEncodingException e) { + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0007: UTF-8 is not recognized as a character set encoding", e); + return deflt; + } catch (Exception e) { + if (complain) + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0001: '" + filename + "': Configuration property " + name + " must be defined", e); + return deflt; + } + } + + /** + * Get a configuration parameter as a String[]. + * If undefined, return null and log an error. + * @return String[] + */ + public String[] getStrings(String name) { + return getStrings(name, null, "[ \t,]+", true); + } + /** + * Get a configuration parameter as a String[]. + * If undefined, return the specified default. + * @return String[] + */ + public String[] getStrings(String name, String[] deflt) { + return getStrings(name, deflt, "[ \t,]+", false); + } + /** + * Get a configuration parameter as a String[]. + * If undefined, return the specified default + * @return String[] + */ + public String[] getStrings(String name, String[] deflt, String pattern, boolean complain) { + name = getString(name, null, complain); + if (name == null) { + return deflt; + } + return name.trim().split(pattern); + } + + /** + * Get a configuration parameter as a String[], each String encoded using URL % escapes. + * If undefined, return null and log an error. + * @return String[] + */ + public String[] getDecodedStrings(String name) { + return getDecodedStrings(name, null, "[ \t,]+", true); + } + /** + * Get a configuration parameter as a String[], each String encoded using URL % escapes. + * If undefined, return the specified default. + * @return String[] + */ + public String[] getDecodedStrings(String name, String[] deflt) { + return getDecodedStrings(name, deflt, "[ \t,]+", false); + } + /** + * Get a configuration parameter as a String[], each String encoded using URL % escapes. + * If undefined, return the specified default. + * @return String[] + */ + public String[] getDecodedStrings(String name, String[] deflt, String pattern) { + return getDecodedStrings(name, deflt, pattern, false); + } + /** + * Get a configuration parameter as a String[], each String encoded using URL % escapes. + * If undefined, return the specified default. + * @return String[] + */ + public String[] getDecodedStrings(String name, String[] deflt, String pattern, boolean complain) { + name = getString(name, null, complain); + if (name == null) { + return deflt; + } + String[] strs = (name.trim().split(pattern)); + try { + for (int i = 0; i < strs.length; i++) { + strs[i] = URLDecoder.decode(strs[i], "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0007: UTF-8 is not recognized as a character set encoding", e); + } + return strs; + } + + /** + * Get a configuration parameter as a long. If undefined or non-numeric, return -1 and log an error. + */ + public long getLong(String name) { + return getLong(name, -1L); + } + /** + * Get a configuration parameter as a long. If undefined, return the specified default + * If non-numeric, return the specified default and log an error. + */ + public long getLong(String name, long deflt) { + String value = getString(name, null); + if (value == null) { + return deflt; + } + try { + return Long.parseLong(value.trim()); + } catch (Exception e) { + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0002: '" + filename + "': Configuration property " + name + " must be numeric", e); + return deflt; + } + } + + /** + * Get a configuration parameter as an int. If undefined or non-numeric, return -1 and log an error. + */ + public int getInt(String name) { + return getInt(name, -1); + } + /** + * Get a configuration parameter as an int. If undefined, return the specified default + * If non-numeric, return the specified default and log an error. + */ + public int getInt(String name, int deflt) { + String value = getString(name, null); + if (value == null) { + return deflt; + } + try { + return Integer.parseInt(value.trim()); + } catch (Exception e) { + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0002: '" + filename + "': Configuration property " + name + " must be numeric", e); + return deflt; + } + } + + /** + * Get a configuration parameter as an boolean. If undefined or non-numeric, return false and log an error. + */ + public boolean getBoolean(String name) { + return getBoolean(name, false); + } + /** + * Get a configuration parameter as an boolean. If undefined, return the specified default + * If non-numeric, return the specified default and log an error. + */ + public boolean getBoolean(String name, boolean deflt) { + String value = getString(name, null); + if (value == null) { + return deflt; + } + try { + return Boolean.parseBoolean(value.trim()); + } catch (Exception e) { + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0002: '" + filename + "': Configuration property " + name + " must be true/false", e); + return deflt; + } + } + + /** + * Get a configuration parameter as a double. If undefined or non-numeric, return -1 and log an error. + */ + public double getDouble(String name) { + return getDouble(name, -1); + } + /** + * Get a configuration parameter as a double. If undefined, return the specified default + * If non-numeric, return the specified default and log an error. + */ + public double getDouble(String name, double deflt) { + String value = getString(name, null); + if (value == null) { + return deflt; + } + try { + return Double.parseDouble(value); + } catch (Exception e) { + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0002: '" + filename + "': Configuration property " + name + " must be numeric", e); + return deflt; + } + } + + public Enumeration getKeys() { + checkinit(); + return (config != null) ? config.getKeys() : null; + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/ConfigurationRegistry.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/ConfigurationRegistry.java new file mode 100644 index 0000000..a8241f8 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/ConfigurationRegistry.java @@ -0,0 +1,106 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.config; + +import java.util.logging.*; +import java.lang.ref.*; + +/** + * Class to register and notify other objects when needed. + * Those other objects must implement Configurable. + */ + +public class ConfigurationRegistry { + public ConfigurationRegistry() { } + + /** + * The set of registered configurables + */ + private WeakReference<Configurable>[] configurables = new WeakReference[0]; + + /** + * Request callback whenever the configuration data changes + */ + public synchronized void registerConfigurable(Configurable element) { + // System.out.println("adding " + element.getClass().getName() + ", length=" + Integer.toString(configurables.length)); + for (int i = 0; i < configurables.length; i++) { + if (configurables[i].get() == element) { + return; + } + } + WeakReference<Configurable>[] nconfigurables = new WeakReference[configurables.length + 1]; + System.arraycopy(configurables, 0, nconfigurables, 0, configurables.length); + nconfigurables[configurables.length] = new WeakReference<Configurable>(element); + configurables = nconfigurables; + element.reConfigure(); + } + + /** + * Cancel request for callbacks when configuration changes + */ + public synchronized void deRegisterConfigurable(Configurable element) { + // System.out.println("removing " + element.getClass().getName() + ", length=" + Integer.toString(configurables.length)); + for (int i = 0; i < configurables.length; i++) { + if (configurables[i].get() == element) { + WeakReference<Configurable>[] nconfigurables = new WeakReference[configurables.length - 1]; + if (i > 0) { + System.arraycopy(configurables, 0, nconfigurables, 0, i); + } + if (i < nconfigurables.length) { + System.arraycopy(configurables, i + 1, nconfigurables, i, nconfigurables.length - i); + } + configurables = nconfigurables; + return; + } + } + } + + /** + * Notify all of the Configurables that they need to reConfigure. + */ + public void reConfigureAll() { + reConfigureAll(Logger.getLogger(ConfigurationRegistry.class.getName())); + } + + /** + * Notify all of the Configurables that they need to reConfigure. + */ + public void reConfigureAll(Logger logger) { + // System.out.println("reConfigureAll(), length=" + Integer.toString(configurables.length)); + for (int i = 0; i < configurables.length; i++) { + try { + // System.out.println("reConfigureAll(), i=" + Integer.toString(i)); + WeakReference<Configurable> wc = configurables[i]; + Configurable c = (wc != null) ? wc.get() : null; + if (c != null) + c.reConfigure(); + } catch (Exception e) { + WeakReference<Configurable> wc = configurables[i]; + Configurable c = (wc != null) ? wc.get() : null; + logger.log(Level.SEVERE, "DAIS0048 Unrecoverable configuration error CNFG0005: Problem while invoking reConfigure for: " + + ((wc == null) ? "null" : (c == null) ? "null/null" : c.getClass().getName()) + ": " + + e.getMessage(), e); + } + } + } + + /** + * Return the number of configurables that are registered. + */ + public int getCount() { + return configurables.length; + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/EncryptedConfiguration.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/EncryptedConfiguration.java new file mode 100644 index 0000000..9c546dc --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/EncryptedConfiguration.java @@ -0,0 +1,219 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.config; + +import java.util.logging.*; +// import java.lang.ref.*; +import org.openecomp.dcae.cdf.util.common.*; +import gnu.getopt.Getopt; +import java.security.*; +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; + +/** + * Class to manage encrypted configuration values. + */ + +public class EncryptedConfiguration { + /** + * Our secret key + */ + private String encryptionKey; + + /** + * Where to log when things go wrong + */ + private Logger logger; + + public EncryptedConfiguration(String key, Logger logger) { + encryptionKey = key.trim(); + this.logger = logger; + } + + /** + * Retrieve an encrypted string from the given configuration. + * The name will have ".x" appended to it. + * Decoded from hex, it will be "method:hexsalt:hexvalue". + * The format of the value will be in hex. + * Method will be "r" to begin with, for "rc4". + */ + public String getString(Configuration config, String name, String deflt, boolean complain) throws Exception { + return getString(config, name, deflt, complain, encryptionKey); + } + + /** + * Retrieve an encrypted string from the given configuration. + * The name will have ".x" appended to it. + * Decoded from hex, it will be "method:hexsalt:hexvalue". + * The format of the value will be in hex. + * Method will be "r" to begin with, for "rc4". + */ + public String getString(Configuration config, String name, String deflt, boolean complain, String key) throws Exception { + String str = config.getString(name + ".x", null, complain); + if (str == null) { + return deflt; + } + return decrypt(str, key); + } + + /** + * Decrypt a string in 'method:hexsalt:hexvalue' format. + */ + public static String decrypt(String triple, String key) throws Exception { + String[] strParts = triple.trim().split(":"); + if (strParts.length != 3) throw new Exception("Encrypted value must look like 'x:y:z'"); + return decrypt(strParts[0], Convert.stringFromHex(strParts[1]), key, Convert.bytesFromHex(strParts[2])); + } + + /** + * Decrypt a string 'method:hexsalt:hexvalue' format. + */ + public static String decrypt(String method, String salt, String key, byte[] bvalue) throws Exception { + /* if (false) { + System.out.println("method length=" + method.length()); System.out.println(AsHex.asHex(method)); + System.out.println("salt length=" + salt.length()); System.out.println(AsHex.asHex(salt)); + System.out.println("key length=" + key.length()); System.out.println(AsHex.asHex(key)); + System.out.println("bvalue length=" + bvalue.length); System.out.println(AsHex.asHex(bvalue)); + } */ + byte[] secretKey = runDigest(salt + "." + key); + + SecretKeySpec skeySpec = new SecretKeySpec(secretKey, method); + + Cipher cipher = Cipher.getInstance(method); // "AES" + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + + byte[] decrypted = cipher.doFinal(bvalue); + return new String(decrypted); + } + + /** + * Encrypt a string using the given method, salt and key. + */ + public static byte[] encrypt(String method, String salt, String key, String value) throws Exception { + byte[] bvalue = value.getBytes(); + byte[] secretKey = runDigest(salt + "." + key); + + SecretKeySpec skeySpec = new SecretKeySpec(secretKey, method); + + Cipher cipher = Cipher.getInstance(method); // "AES" + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + + byte[] encrypted = cipher.doFinal(bvalue); + return encrypted; + } + + /** + * Prepare a secret key by running a digest on it. + */ + private static byte[] runDigest(String text) throws Exception { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.reset(); + md.update(text.getBytes(), 0, text.length()); + return md.digest(); + } + + /** + * Encrypt a string using the given method, salt and key, and return it as a hex-formated triple. + */ + public static String encryptToTriple(String method, String salt, String key, String value) throws Exception { + StringBuilder sb = new StringBuilder(method); + sb.append(':').append(Convert.toHexString(salt)) + .append(':').append(Convert.toHexString(encrypt(method, salt, key, value))); + return sb.toString(); + } + + /** + * Create a value that can be used as a salt. + */ + public static String generateSalt() { + return Long.toString(System.currentTimeMillis() % 1000) + Pid.getPidStr(); + } + + public static void usage() { + usage(null); + } + + public static void usage(String msg) { + if (msg != null) System.out.println(msg); + System.out.println("Usage: java EncryptedConfiguration -D triple -k key\n" + + "java EncryptedConfiguration -d string -m method [-s salt | -S] -k key\n" + + "java EncryptedConfiguration -e string -m method [-s salt | -S] -k key\n" + + "-D\tdecrypt x:y:z triple\n" + + "-d\tdecrypt string (in hex)\n" + + "-e\tencrypt string\n" + + "-S\tgenerate a salt\n" + ); + System.exit(1); + } + + public static void main(String args[]) throws Exception { + Getopt g = new Getopt( "EncryptedConfiguration", args, "s:Sk:m:e:d:D:?" ); + + int c, verbosity = 0; + String salt = null, key = null, method = null, encStr = null, decStr = null, triple = null; + boolean genSalt = false; + + while ((c = g.getopt()) != -1) { + switch (c) { + case 's': salt = g.getOptarg(); break; + case 'S': genSalt = true; break; + case 'k': key = g.getOptarg(); break; + case 'm': method = g.getOptarg(); break; + case 'e': encStr = g.getOptarg(); break; + case 'd': decStr = g.getOptarg(); break; + case 'D': triple = g.getOptarg(); break; + case '?': usage(); break; + } + } + + if (triple == null) { + if ((salt == null) && !genSalt) usage("one of -s or -S must be specified"); + if ((salt != null) && genSalt) usage("only one of -s or -S must be specified"); + if (key == null) usage("-k must be specified"); + if (method == null) usage("-m must be specified"); + if ((encStr == null) && (decStr == null)) usage("one of -d or -e must be specified"); + if ((encStr != null) && (decStr != null)) usage("only one of -d or -e may be specified"); + if (genSalt) salt = generateSalt(); + if (encStr != null) + System.out.println(encryptToTriple(method, salt, key, encStr)); + if (decStr != null) + System.out.println(decrypt(method, salt, key, Convert.bytesFromHex(decStr))); + } else { + if (key == null) usage("-k not specified"); + System.out.println(decrypt(triple, key)); + } + + // http://forums.sun.com/thread.jspa?threadID=5290983 + // try { + // String message = "Strong Versus Unlimited Strength Cryptography"; + // SecretKeySpec skeySpec = new SecretKeySpec("0123456789ABCDEF".getBytes(), "AES"); //AES-128 + + // Cipher cipher = Cipher.getInstance("AES"); // "AES/ECB/NoPadding" + // cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + + // byte[] encrypted = cipher.doFinal(message.getBytes()); + // System.out.println("encrypted string: " + encrypted); //storing into MySQL DB + // System.out.println("in hex: '" + Convert.toHexString(encrypted) + "'"); + + // cipher.init(Cipher.DECRYPT_MODE, skeySpec); + // byte[] original = cipher.doFinal(encrypted); + // String originalString = new String(original); + // System.out.println("Original string: " + originalString); + // } catch (Exception e) { + // System.err.println("Exception caught: " + e.toString()); + // } + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/PropValue.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/PropValue.java new file mode 100644 index 0000000..07bbe06 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/config/PropValue.java @@ -0,0 +1,245 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.config; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.PrintStream; +import java.io.IOException; +import java.util.logging.Logger; +import org.openecomp.dcae.cdf.util.config.Configuration; +import org.openecomp.dcae.cdf.util.config.EncryptedConfiguration; +import gnu.getopt.Getopt; + +public class PropValue { + private EncryptedConfiguration encryptedConfiguration; + private String encryptionKey; + + public PropValue(Configuration globalConfig, Logger logger) { + encryptionKey = globalConfig.getString(getEncryptionKeyProperty()); + encryptedConfiguration = new EncryptedConfiguration(encryptionKey, logger); + } + + public String getEncryptedString(Configuration config, String name, String deflt, boolean complain) throws Exception { + return encryptedConfiguration.getString(config, name, deflt, complain); + } + + public String generateEncryptedProperty(String method, String salt, String value) throws Exception { + return generateEncryptedProperty(method, salt, value, this); + } + + public String decryptTriple(String triple) { + return decryptTriple(triple, this); + } + + public static void printEncryptedProperty(String method, String name, String salt, String value, String globalPropFile) { + try { + if (name != null) System.out.print(name + ".x="); + if (globalPropFile == null) globalPropFile = getGlobalPropFile(); + if (globalPropFile == null) throw new NullPointerException("globalPropFile not set"); + System.out.println(generateEncryptedProperty(method, salt, value, globalPropFile)); + } catch (Exception e) { + System.err.println("Cannot encrypt '" + value + "', method '" + method + "' for property '" + name + "': "+ e.toString()); + } + } + + public static String generateEncryptedProperty(String method, String salt, String value, String globalPropFile) throws Exception { + Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + if (globalPropFile == null) globalPropFile = getGlobalPropFile(); + if (globalPropFile == null) throw new NullPointerException("globalPropFile not set"); + PropValue propValue = new PropValue(new Configuration(globalPropFile), logger); + return generateEncryptedProperty(method, salt, value, propValue); + } + + public static String generateEncryptedProperty(String method, String salt, String value, PropValue propValue) throws Exception { + if (salt == null) salt = EncryptedConfiguration.generateSalt(); + return EncryptedConfiguration.encryptToTriple(method, salt, propValue.encryptionKey, value); + } + + public static void extractProperty(String f, String name, boolean encrypted) { + extractProperty(f, name, encrypted, null); + } + + public static void extractProperty(String f, String name, boolean encrypted, String globalPropFile) { + Configuration config = new Configuration(f); + Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + if (globalPropFile == null) globalPropFile = getGlobalPropFile(); + if (globalPropFile == null) throw new NullPointerException("globalPropFile not set"); + PropValue propValue = new PropValue(new Configuration(globalPropFile), logger); + String val = ""; + try { + if (encrypted) + val = propValue.getEncryptedString(config, name, "", true); + else + val = config.getString(name); + System.out.println(val); + } catch (Exception e) { + System.err.println("Cannot extract '" + name + "' from '" + config + "': " + e.toString()); + } + } + + public static void usage() { + usage(null); + } + + // public static String decryptTriple(String triple) { + // return decryptTriple(triple, null); + // } + + public static String decryptTriple(String triple, String globalPropFile) { + Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + if (globalPropFile == null) globalPropFile = getGlobalPropFile(); + if (globalPropFile == null) throw new NullPointerException("globalPropFile not set"); + PropValue propValue = new PropValue(new Configuration(globalPropFile), logger); + return decryptTriple(triple, propValue); + } + + public static String decryptTriple(String triple, PropValue propValue) { + String ret = null; + try { + ret = EncryptedConfiguration.decrypt(triple, propValue.encryptionKey); + } catch (Exception e) { + System.err.println("Cannot decrypt '" + triple + "': " + e.toString()); + } + return ret; + } + + public static void encryptInput(InputStream in, PrintStream out) throws Exception { + encryptInput(null, in, out); + } + public static void encryptInput() throws Exception { + encryptInput(null, System.in, System.out); + } + + private static void printEncryptedValue(Matcher m, PropValue propValue, PrintStream sysout) { + String method = m.group(1); + String name = m.group(2); + String value = m.group(3); + try { + sysout.println(name + ".x=" + + EncryptedConfiguration.encryptToTriple(method, + EncryptedConfiguration.generateSalt(), + propValue.encryptionKey, value)); + } catch (Exception e) { + System.err.println("Error: Cannot encrypt '" + value + "', method '" + method + "' for property '" + name + "': " + e.toString()); + } + } + + public static void encryptInput(String globalPropFile, InputStream sysin, PrintStream sysout) throws Exception { + String s; + + Pattern pDquote = Pattern.compile("^ENCRYPTME[.]([A-Z]*)[.]([^= \t]*)[ \t]*=[ \t]*\"([^\"]*)\"[ \t]*$"); + Pattern pSquote = Pattern.compile("^ENCRYPTME[.]([A-Z]*)[.]([^= \t]*)[ \t]*=[ \t]*'([^']*)'[ \t]*$"); + Pattern pNoWhite = Pattern.compile("^ENCRYPTME[.]([A-Z]*)[.]([^= \t]*)[ \t]*=[ \t]*([^ \t'\"]+)[ \t]*$"); +// Pattern pEncryptMe = Pattern.compile("^ENCRYPTME[.]([A-Z]*)[.]([^= \t]*)[ \t]*="); + + Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + if (globalPropFile == null) globalPropFile = getGlobalPropFile(); + if (globalPropFile == null) throw new NullPointerException("globalPropFile not set"); + PropValue propValue = new PropValue(new Configuration(globalPropFile), logger); + + BufferedReader in = new BufferedReader(new InputStreamReader(sysin)); + + try { + while ((s = in.readLine()) != null) { + // System.out.println("looking at '" + s + "'"); + Matcher mDquote = pDquote.matcher(s); + Matcher mSquote = pSquote.matcher(s); + Matcher mNoWhite = pNoWhite.matcher(s); +// Matcher mEncryptMe = pNoWhite.matcher(s); + if (mDquote.matches()) { + printEncryptedValue(mDquote, propValue, sysout); + } else if (mSquote.matches()) { + printEncryptedValue(mSquote, propValue, sysout); + } else if (mNoWhite.matches()) { + printEncryptedValue(mNoWhite, propValue, sysout); + } else if (s.startsWith("ENCRYPTME")) { + throw new Exception("Bad value to encrypt: '" + s + "'"); + } else { + // System.out.println("printing the line: '" + s + "'"); + sysout.println(s); + } + } + } catch (IOException e) { + System.err.println("Error: Cannot read from stdin: " + e.toString()); + } catch (Exception e) { + throw e; + } + } + + public static void usage(String msg) { + if (msg != null) System.err.println(msg); + System.err.println("Usage: java PropValue [-x] -n property -f property-file"); + System.err.println("\tExtract the named value from the given property-file (or full pathname)"); + System.err.println("Usage: java PropValue -e method [-n property] [-s salt] -v value"); + System.err.println("\tEncrypt the given property with the given name and value"); + System.err.println("Usage: java PropValue -E"); + System.err.println("\tEncrypt all lines that look like ENCRYPTME.METHOD.name=value"); + System.err.println("Usage: java PropValue -u value"); + System.err.println("\tDecrypt the given value, expressed as a triple METHOD:HEXSALT:HEXVAL"); + System.exit(1); + } + + public static void setGlobalPropFile(String g) { sGlobalPropFile = g; } + public static String getGlobalPropFile() { return sGlobalPropFile; } + private static String sGlobalPropFile = null; + + public static void setEncryptionKeyProperty(String e) { encryptionKeyProperty = e; } + public static String getEncryptionKeyProperty() { return encryptionKeyProperty; } + private static String encryptionKeyProperty = "Global_Title"; + + public static void main(String args[]) throws Exception { + Getopt g = new Getopt( "PropValue", args, "e:Ef:G:n:s:u:v:x" ); + String propfile = null, name = null, method = null, value = null, unencrypt = null; + String globalPropFile = getGlobalPropFile(); + boolean useDecryption = false, encryptStdin = false; + String salt = null; + int c; + + while ((c = g.getopt()) != -1) { + switch (c) { + case 'e': method = g.getOptarg(); break; + case 'E': encryptStdin = true; break; + case 'f': propfile = g.getOptarg(); break; + case 'G': globalPropFile = g.getOptarg(); break; + case 'n': name = g.getOptarg(); break; + case 's': salt = g.getOptarg(); break; + case 'u': unencrypt = g.getOptarg(); break; + case 'v': value = g.getOptarg(); break; + case 'x': useDecryption = true; break; + case '?': usage(); break; + } + } + if (encryptStdin) { + if (name != null || propfile != null || method != null || value != null) usage("cannot use -E with other options"); + encryptInput(System.in, System.out); + } else if (unencrypt == null) { + if (method != null) { + if (value == null) usage("-v required"); + printEncryptedProperty(method, name, salt, value, globalPropFile); + } else { + if (name == null) usage("-n is required"); + if (propfile == null) usage("-f is required"); + extractProperty(propfile, name, useDecryption, globalPropFile); + } + } else { + System.out.println(decryptTriple(unencrypt, globalPropFile)); + } + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/.gitignore b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/.gitignore new file mode 100644 index 0000000..6b468b6 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/.gitignore @@ -0,0 +1 @@ +*.class diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/TaskThread.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/TaskThread.java new file mode 100644 index 0000000..2fee87a --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/TaskThread.java @@ -0,0 +1,164 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.threads; + +/** + * A thread with a queue of runnable tasks to execute in the + * thread + */ +public class TaskThread extends Thread { + /** + * Allocates a new TaskThread object. + */ + public TaskThread() { + } + /** + * Allocates a new TaskThread object. + */ + public TaskThread(Runnable target) { + super(target); + } + /** + * Allocates a new TaskThread object. + */ + public TaskThread(ThreadGroup group, Runnable target) { + super(group, target); + } + /** + * Allocates a new TaskThread object. + */ + public TaskThread(String name) { + super(name); + } + /** + * Allocates a new TaskThread object. + */ + public TaskThread(ThreadGroup group, String name) { + super(group, name); + } + /** + * Allocates a new TaskThread object. + */ + public TaskThread(Runnable target, String name) { + super(target, name); + } + /** + * Allocates a new TaskThread object. + */ + public TaskThread(ThreadGroup group, Runnable target, String name) { + super(group, target, name); + } + /** + * A queued request to be run in the TaskThread + */ + private static class Task { + public Task next; + public Runnable target; + public Task(Runnable target) { + this.target = target; + } + } + private Task head; + private Task tail; + protected boolean closed; + /** + * Queue up a task to be executed by this thread. + */ + protected synchronized void queueRequest(Runnable r) { + Task t = new Task(r); + if (head == null) { + head = t; + wakeup(); + } else { + tail.next = t; + } + tail = t; + } + /** + * Mark as closed and wake up. + */ + protected synchronized void markClosed() { + if (!closed) { + closed = true; + wakeup(); + } + } + /** + * Wait for the next queued request. If closed, return + * null. Relies on the default implementation of wakeup. + */ + protected synchronized Runnable waitNextRequest() { + Task t; + while ((t = head) == null && !closed) { + try { + wait(); + } catch (Exception e) { + } + } + head = t.next; + if (head == null) { + tail = null; + } + return t.target; + } + /** + * Get the next queued request or null if none + */ + protected synchronized Runnable nextRequest() { + Task t = head; + if (t == null) { + return null; + } + head = t.next; + if (head == null) { + tail = null; + } + return t.target; + } + /** + * Wake up the thread to process tasks. + * Implementation depends on what the thread + * is waiting on. The default implementation + * does a this.notify(). + */ + protected void wakeup() { + notify(); + } + /** + * Process any pending requests then return + */ + protected void processQueuedRequests() { + Runnable r; + while ((r = nextRequest()) != null) { + r.run(); + } + } + /** + * Check whether any tasks are pending + */ + protected boolean areTasksPending() { + return (head != null); + } + /** + * Wait for and process pending requests until closed + */ + protected void processRequestsForever() { + Runnable r; + while ((r = waitNextRequest()) != null) { + r.run(); + } + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/ThreadCommon.java b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/ThreadCommon.java new file mode 100644 index 0000000..fd9f3c1 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/main/java/org/openecomp/dcae/cdf/util/threads/ThreadCommon.java @@ -0,0 +1,119 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util.threads; + +/** + * Some functions to manipulate thread info. Based on + * http://nadeausoftware.com/articles/2008/04/java_tip_how_list_and_find_threads_and_thread_groups + * which is licensed under LGPL 2 + */ + +import java.lang.management.*; +// import java.util.Arrays; + +public final class ThreadCommon { + /** + * ThreadCommon is not to be instantiated. + */ + private ThreadCommon() { } + + private static ThreadGroup rootThreadGroup = null; + + /** + * Get the root thread group in the thread group tree. + * Since there is always a root thread group, this + * method never returns null. + * + * @return the root thread group + */ + public static synchronized ThreadGroup getRootThreadGroup() { + if ( rootThreadGroup != null ) + return rootThreadGroup; + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + ThreadGroup ptg; + while ( (ptg = tg.getParent()) != null ) + tg = ptg; + return tg; + } + + /** + * Get a list of all threads. Since there is always at + * least one thread, this method never returns null or + * an empty array. + * + * @return an array of threads + */ + public static Thread[] getAllThreads() { + final ThreadGroup root = getRootThreadGroup(); + final ThreadMXBean thbean = ManagementFactory.getThreadMXBean(); + int nAlloc = thbean.getThreadCount(); + int n = 0; + Thread[] threads; + do { + nAlloc *= 2; + threads = new Thread[ nAlloc ]; + n = root.enumerate( threads, true ); + } while ( n == nAlloc ); + return copyOf( threads, n ); + } + + /** + * Get the thread with the given name. A null is returned + * if no such thread is found. If more than one thread has + * the same name, the first one found is returned. + * + * @param name the thread name to search for + * @return the thread, or null if not found + * @throws NullPointerException + * if the name is null + */ + public static Thread getFirstThread( final String name ) { + if ( name == null ) + throw new NullPointerException( "Null name" ); + final Thread[] threads = getAllThreads(); + for ( Thread thread : threads ) + if ( thread.getName().equals( name ) ) + return thread; + return null; + } + + /** + * Get a list of all threads with a given thread name. + * + * @param name the name to look for + * @return an array of threads in that state + */ + public static Thread[] getAllThreads( final String name ) { + if ( name == null ) + throw new NullPointerException( "Null name" ); + final Thread[] allThreads = getAllThreads(); + final Thread[] found = new Thread[allThreads.length]; + int nFound = 0; + for ( Thread thread : allThreads ) + if ( thread.getName().equals(name) ) + found[nFound++] = thread; + return copyOf( found, nFound ); + } + + // return java.util.Arrays.copyOf( found, nFound ); + private static Thread[] copyOf( Thread[] threads, int n ) { + Thread[] nthreads = new Thread[ n ]; + for (int i = 0; i < n; i++) { + nthreads[i] = threads[i]; + } + return nthreads; + } +} diff --git a/cdf/src/cdf-prop-value/cdf-util/src/test/java/org/openecomp/dcae/cdf/util/AppTest.java b/cdf/src/cdf-prop-value/cdf-util/src/test/java/org/openecomp/dcae/cdf/util/AppTest.java new file mode 100644 index 0000000..85d5cd8 --- /dev/null +++ b/cdf/src/cdf-prop-value/cdf-util/src/test/java/org/openecomp/dcae/cdf/util/AppTest.java @@ -0,0 +1,53 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +package org.openecomp.dcae.cdf.util; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/cdf/src/cdf-prop-value/makefile b/cdf/src/cdf-prop-value/makefile new file mode 100644 index 0000000..194ad29 --- /dev/null +++ b/cdf/src/cdf-prop-value/makefile @@ -0,0 +1,13 @@ +all: + +build: + cd cdf-util/src/main/java && $(MAKE) build + cd cdf-prop-value/src/main/java && $(MAKE) build + +javadocs: + cd cdf-util/src/main/java && $(MAKE) javadocs + cd cdf-prop-value/src/main/java && $(MAKE) javadocs + +clean: + cd cdf-util/src/main/java && $(MAKE) clean + cd cdf-prop-value/src/main/java && $(MAKE) clean diff --git a/cdf/src/common/postinst b/cdf/src/common/postinst new file mode 100755 index 0000000..7c92a27 --- /dev/null +++ b/cdf/src/common/postinst @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +exec 1> /tmp/cdf.out 2>&1 +set -x +if [ -d /opt/app/postgresql-9.5.2 ] +then export OPENECOMP=false NOTOPENECOMP=true +else export OPENECOMP=true NOTOPENECOMP=false +fi + +echo STARTING $0 $(date) +umask 0 +echo STARTING $0 $(date) >> /tmp/pgaas.inst.report + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +id +umask 022 + +# randomize the the CDF package +ranval=$(dd if=/dev/urandom count=1 ibs=8 2>/dev/null | od -x -w1000 | sed -e 's/^0000000 //' -e 's/ //g' -e 1q) +CDFCFG=${INSTALL_ROOT}/opt/app/cdf/lib/cdf.cfg +rm -f ${CDFCFG} +sed -e 's/${HOSTNAME}/'"$(hostname -f)"'/' -e 's/${RANDOM}/'"$ranval"'0/' < ${INSTALL_ROOT}/opt/app/cdf/lib/cdf.cfg.tmpl > ${CDFCFG} +chown postgres:postgres ${CDFCFG} + +echo ENDING $0 $(date) +echo ENDING $0 $(date) >> /tmp/pgaas.inst.report +if $NOTOPENECOMP +then sed -n '/^STARTING/,/^ENDING/p' `dirname $0`/../../proc_out >> /tmp/pgaas.inst.report +fi diff --git a/cdf/src/makefile b/cdf/src/makefile new file mode 100644 index 0000000..2c67454 --- /dev/null +++ b/cdf/src/makefile @@ -0,0 +1,52 @@ + +DEVBIN=../../bin +PKG=cdf +REPACKAGESWMOPTS= +REPACKAGEDEBIANOPTS= + +INS= ../install +INSSTG= $(INS)/stage +INSCOM= $(INS)/common + +all: + +clean-stage: + rm -rf $(INSSTG) + +clean-common: + rm -rf $(INSCOM) + +clean: + rm -rf $(INS) + cd cdf-prop-value && $(MAKE) clean + +build: javadocs build-java + +build-java: + cd cdf-prop-value && $(MAKE) build + +javadocs: + cd cdf-prop-value && $(MAKE) javadocs + +stage: build clean-stage clean-common + mkdir -p $(INS) + find stage common ! -name makefile ! -name '*~' | cpio -pudmv $(INS) + cp cdf-prop-value/cdf-prop-value/src/main/java/cdf-prop-value.jar $(INS)/stage/opt/app/cdf/lib/cdf-prop-value-1.0.0.jar + cp cdf-prop-value/cdf-util/src/main/java/cdf-util.jar $(INS)/stage/opt/app/cdf/lib/jars/cdf-util-1.0.0.jar + chmod a+x $(INSCOM)/* + cp -p repackage.* $(INS) + + +debian: stage + repackage -b debian $(REPACKAGEDEBIANOPTS) -d $(INS) -u + repackage -b debian $(REPACKAGEDEBIANOPTS) -d $(INS) -u -B LATEST + @echo debian built + + +upload-javadocs: javadocs + cd cdf-prop-value && find cdf-*/src/main/java/javadoc -type f | while read f; do \ + curl -k --user "$${OPENECOMP_NEXUS_USER}:$${OPENECOMP_NEXUS_PASSWORD}" --upload-file "$$f" "$${OPENECOMP_NEXUS_JAVADOC}/org.openecomp.dcae.storage.cdf/1.0.0/$$f"; \ + done + # OPENECOMP_NEXUS_JAVADOC https://ecomp-nexus:8443/repository/dcae-javadoc/ + # https://162.242.254.138:8443/#browse/browse/components:dcae-javadoc + diff --git a/cdf/src/repackage.json b/cdf/src/repackage.json new file mode 100644 index 0000000..8802627 --- /dev/null +++ b/cdf/src/repackage.json @@ -0,0 +1,29 @@ +{ + "executionUser": "postgres", + "maintainer": "OpenECOMP <dcae@lists.openecomp.org>", + "executionGroup": "postgres", + "directoryTreeTops": { + "/opt": "/opt/app/cdf" + }, + "description": " PostgreSQL as a Service main scripts ", + "docker": { + "tag": "latest", + "externalDependencies": [] + }, + "version": "1.0.0", + "applicationName": "cdf", + "internalDependencies": [], + "fileGroup": "postgres", + "fileUser": "postgres", + "groupId": "org.openecomp.dcae.storage.pgaas", + "debian": { + "externalDependencies": [ + { + "libgetopt-java": ">= 1.0.14" + } + ], + "groupId": "org.openecomp.dcae.storage.pgaas", + "conflicts": [], + "replaces": [] + } +}
\ No newline at end of file diff --git a/cdf/src/stage/opt/app/cdf/bin/getpropvalue b/cdf/src/stage/opt/app/cdf/bin/getpropvalue new file mode 100755 index 0000000..cd81814 --- /dev/null +++ b/cdf/src/stage/opt/app/cdf/bin/getpropvalue @@ -0,0 +1,31 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +P=/opt/cdf +CDF=/opt/app/cdf + +export PATH=/opt/java/jdk/jdk170/bin:$PATH +export CLASSPATH=$CDF/lib/cdf-prop-value-1.0.0.jar:$CDF/lib/jars/cdf-util-1.0.0.jar +if [ -f $CDF/lib/jars/gnu_getopt.jar ] +then CLASSPATH=$CLASSPATH:$CDF/lib/jars/gnu_getopt.jar ] +elif [ -f /usr/share/java/gnu-getopt.jar ] +then CLASSPATH=$CLASSPATH:/usr/share/java/gnu-getopt.jar +else echo "$0: Cannot find gnu-getopt.jar" 1>&2; exit 1 +fi + +PropValue=org.openecomp.dcae.cdf.CdfPropValue +CfgFile=$CDF/lib/cdf.cfg + +java $PropValue -f $CfgFile "$@" diff --git a/cdf/src/stage/opt/app/cdf/bin/setencryptedvalues b/cdf/src/stage/opt/app/cdf/bin/setencryptedvalues new file mode 100755 index 0000000..dc5b6d0 --- /dev/null +++ b/cdf/src/stage/opt/app/cdf/bin/setencryptedvalues @@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +P=/opt/cdf +CDF=/opt/app/cdf + +export PATH=/opt/java/jdk/jdk170/bin:$PATH +export CLASSPATH=$CDF/lib/cdf-prop-value-1.0.0.jar:$CDF/lib/jars/cdf-util-1.0.0.jar +if [ -f $CDF/lib/jars/gnu_getopt.jar ] +then CLASSPATH=$CLASSPATH:$CDF/lib/jars/gnu_getopt.jar ] +elif [ -f /usr/share/java/gnu-getopt.jar ] +then CLASSPATH=$CLASSPATH:/usr/share/java/gnu-getopt.jar +else echo "$0: Cannot find gnu-getopt.jar" 1>&2; exit 1 +fi + +PropValue=org.openecomp.dcae.cdf.CdfPropValue + +java $PropValue -E "$@" diff --git a/cdf/src/stage/opt/app/cdf/lib/cdf.cfg.tmpl b/cdf/src/stage/opt/app/cdf/lib/cdf.cfg.tmpl new file mode 100644 index 0000000..7292963 --- /dev/null +++ b/cdf/src/stage/opt/app/cdf/lib/cdf.cfg.tmpl @@ -0,0 +1,18 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +#### +#### nothing should need to change below +#### +Global_Title="This is the Common DCAE Framework 1.0 ${HOSTNAME} ${RANDOM}" diff --git a/cdf/src/stage/opt/app/cdf/lib/jars/which_files b/cdf/src/stage/opt/app/cdf/lib/jars/which_files new file mode 100644 index 0000000..47a7ba7 --- /dev/null +++ b/cdf/src/stage/opt/app/cdf/lib/jars/which_files @@ -0,0 +1,3 @@ +The following files were installed here: + +gnu_getopt.jar diff --git a/makefile b/makefile new file mode 100644 index 0000000..0758d61 --- /dev/null +++ b/makefile @@ -0,0 +1,21 @@ + +all: + +STAGEDIRS=cdf postgresql-prep postgresql-config pgaas pgaas-post + +build: + for i in $(STAGEDIRS); do ( cd $$i/src && $(MAKE) build ) done + +clean: + for i in $(STAGEDIRS); do ( cd $$i/src && $(MAKE) clean ) done + +stage: + for i in $(STAGEDIRS); do ( cd $$i/src && $(MAKE) stage ) done + +upload-javadocs: + for i in $(STAGEDIRS); do ( cd $$i/src && $(MAKE) upload-javadocs ) done + + +debian: + for i in $(STAGEDIRS); do ( cd $$i/src && $(MAKE) debian ) done + diff --git a/pgaas-post/.gitignore b/pgaas-post/.gitignore new file mode 100644 index 0000000..7c32f55 --- /dev/null +++ b/pgaas-post/.gitignore @@ -0,0 +1 @@ +install diff --git a/pgaas-post/src/common/postinst b/pgaas-post/src/common/postinst new file mode 100755 index 0000000..47b1add --- /dev/null +++ b/pgaas-post/src/common/postinst @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +exec 1> /tmp/pgaas-post.out 2>&1 +set -x +if [ -d /opt/app/postgresql-9.5.2 ] +then export OPENECOMP=false NOTOPENECOMP=true +else export OPENECOMP=true NOTOPENECOMP=false +fi + +if $OPENECOMP +then INSTALL_ROOT= +fi + +echo STARTING $0 $(date) +umask 0 +echo STARTING $0 $(date) >> /tmp/pgaas.inst.report + +if [ -d /opt/app/postgresql-9.5.2 ] +then chmod 751 /opt/app/postgresql-9.5.2 /opt/app/postgresql-9.5.2/bin /opt/app/postgresql-9.5.2/lib +fi + +/opt/app/pgaas-post/bin/pgaas-verify-install + +echo ENDING $0 $(date) +echo ENDING $0 $(date) >> /tmp/pgaas.inst.report +if $NOTOPENECOMP +then sed -n '/^STARTING/,/^ENDING/p' `dirname $0`/../../proc_out >> /tmp/pgaas.inst.report +fi diff --git a/pgaas-post/src/common/postrm b/pgaas-post/src/common/postrm new file mode 100755 index 0000000..42d2529 --- /dev/null +++ b/pgaas-post/src/common/postrm @@ -0,0 +1 @@ +echo STARTING $0 $(date) diff --git a/pgaas-post/src/makefile b/pgaas-post/src/makefile new file mode 100644 index 0000000..d66d74b --- /dev/null +++ b/pgaas-post/src/makefile @@ -0,0 +1,38 @@ + +DEVBIN=../../bin +PKG=pgaas-post +REPACKAGESWMOPTS= +REPACKAGEDEBIANOPTS= + +INS= ../install +INSSTG= $(INS)/stage +INSCOM= $(INS)/common + +all: + +clean-stage: + rm -rf $(INSSTG) + +clean-common: + rm -rf $(INSCOM) + +clean: + rm -rf $(INS) + +build: + +stage: clean-stage clean-common + find common ! -name makefile ! -name '*~' | cpio -pudmv $(INS) + find stage ! -name makefile ! -name '*~' | cpio -pudmv $(INS) + chmod a+x $(INSSTG)/opt/app/pgaas-post/bin/* + cp -p repackage.* $(INS) + + +debian: stage + repackage -y repackage.json -b debian -d $(INS) -u + repackage -y repackage.json -b debian -d $(INS) -u -B LATEST + @echo debian built + +upload-javadocs: + @echo nothing to do here + diff --git a/pgaas-post/src/repackage.json b/pgaas-post/src/repackage.json new file mode 100644 index 0000000..134c00c --- /dev/null +++ b/pgaas-post/src/repackage.json @@ -0,0 +1,23 @@ +{ + "version": "1.0.0", + "executionUser": "root", + "description": " PostgreSQL as a Service main scripts ", + "maintainer": "OpenECOMP <dcae@lists.openecomp.org>", + "debian": { + "replaces": [], + "groupId": "org.openecomp.dcae.storage.pgaas", + "externalDependencies": [], + "conflicts": [] + }, + "internalDependencies": [], + "fileUser": "root", + "docker": { + "tag": "latest", + "externalDependencies": [] + }, + "directoryTreeTops": {}, + "groupId": "org.openecomp.dcae.storage.pgaas", + "fileGroup": "root", + "applicationName": "pgaas-post", + "executionGroup": "root" +}
\ No newline at end of file diff --git a/pgaas-post/src/stage/opt/app/pgaas-post/bin/pgaas-verify-install b/pgaas-post/src/stage/opt/app/pgaas-post/bin/pgaas-verify-install new file mode 100644 index 0000000..5bf3962 --- /dev/null +++ b/pgaas-post/src/stage/opt/app/pgaas-post/bin/pgaas-verify-install @@ -0,0 +1,227 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +die() +{ + exec 1>&2 + echo "$@" + logger --stderr --priority local1.error --tag "DCAE" "$@" + exit 1 +} + +usage() +{ + exec 1>&2 + [ $# -gt 0 ] && echo "$@" + b=$(basename $0) + echo "Usage: $b [-v]" + echo "$b runs a variety of tests on the PG VM and database" + echo "It must be run as root or postgres." + echo "If run as root, it will do additional tests that are" + echo "not possible as a normal user." + echo " -v verbose" + echo " -P do not print VERIFIED" + exit 1 +} + +PRINTVERIFIED=: +while getopts Pv c +do + case $c in + P ) PRINTVERIFIED=false ;; + v ) set -x ;; + '?' ) usage ;; + esac +done +shift `expr $OPTIND - 1` + +# this can be run as root +ROOT=false +case `id` in + *"(root)"* ) ROOT=true ;; + *"(postgres)"* ) ;; + * ) echo "$0 must be run as either root or postgres" ;; +esac + +VERIFIEDCOUNT=0 +FAILEDCOUNT=0 +TOTALCOUNT=0 + +verified() +{ + (( VERIFIEDCOUNT = VERIFIEDCOUNT + 1 )) + (( TOTALCOUNT = TOTALCOUNT + 1 )) + $PRINTVERIFIED && echo "VERIFIED: $@" +} + +failed() +{ + (( FAILEDCOUNT = FAILEDCOUNT + 1 )) + (( TOTALCOUNT = TOTALCOUNT + 1 )) + echo "FAILED: $@" +} + +tabtext() +{ + echo "$@" | sed 's/^/ /' +} + +HOSTNAME=$(hostname -f) + +################################################################ +################ things set up by ################ +################ openstack ################ +################################################################ + +case $HOSTNAME in + *.*.* ) verified "hostname has a FQDN" ;; + * ) failed "hostname does not have a FQDN" ;; +esac + +################################################################ +################ things set up by ################ +################ controller dcae_install ################ +################################################################ + +if grep '^dcae:' /etc/passwd > /dev/null +then verified "dcae user exists" +else failed "dcae user does not exist" +fi +if $ROOT +then + if [ -s /etc/sudoers.d/dcae-postgres ] + then verified "dcae can sudo to postgres" + else failed "dcae cannot sudo to postgres" + fi +fi + +SHOWDF=false +for i in /opt/tools /dbroot/pgdata /dbroot/pglogs +do + if df -h 2>&1 | grep " $i"'$' > /dev/null + then verified "$i has its own filesystem" + else failed "$i does not have its own filesystem"; SHOWDF=true + fi +done +$SHOWDF && tabtext "$(df -h 2>&1)" + +if grep '^postgres:' /etc/passwd > /dev/null +then verified "postgres user exists" +else failed "postgres user does not exist" +fi + +################################################################ +################ things set up by ################ +################ cdf package ################ +################################################################ + +if [ -d /opt/app/cdf ] +then verified "/opt/app/cdf is present" +else failed "/opt/app/cdf is present" +fi + +cdfcall=$(/opt/app/cdf/bin/getpropvalue -n foo 2>&1) +case "$cdfcall" in + *Configuration?property*must?be?defined* ) verified "CDF is installed and working" ;; + * ) failed "CDF is not installed and working"; tabtext "$cdfcall" ;; +esac + +################################################################ +################ things set up by ################ +################ postgresql-prep package ################ +################################################################ + +if grep "^pgnodes=.*$HOSTNAME" /opt/app/cdf/lib/cdf.cfg > /dev/null +then verified "HOSTNAME is part of cluster (cdf.cfg pgnodes)" +else failed "HOSTNAME is not part of cluster (cdf.cfg pgnodes)" +fi + +# check for certificate presence goes here + +if [ -s /lib/systemd/system/pgaas-idns.service ] +then verified "found pgaas-idns service properly installed for Ubuntu 16" +elif [ -s /etc/init/pgaas-init.conf ] +then verified "found pgaas-idns service properly installed for Ubuntu 14" +else failed "pgaas-idns service has not bee installed properly" +fi + +if [ -d /var/run/postgresql ] +then verified "/var/run/postgresql exists" +else failed "/var/run/postgresql does not exist" +fi + +if [ -s /etc/logrotate.d/pgaas ] +then verified "/etc/logrotate.d/pgaas has been installed" +else failed "/etc/logrotate.d/pgaas has not been installed" +fi + + +################################################################ +################ things set up by ################ +################ postgresql-config ################ +################################################################ + +if ps -fu postgres | grep "postgres: logger process" > /dev/null +then verified "postgres is running" +else failed "postgres does not have a logger process running" +fi + +if pgrep repmgrd > /dev/null +then verified "repmgrd is running" +else failed "repmgrd is not running" +fi + +if [ -f /opt/app/pgaas/bin/runpsqll ] +then + verified "/opt/app/pgaas/bin/runpsqll is installed" + roles=$( /opt/app/pgaas/bin/runpsqll "select rolname from pg_roles" ) + case "$roles" in + *repmgr* ) verified "postgres repmgr role name is present" ;; + * ) failed "postgres repmgr role name was not added"; tabtext "$roles" ;; + esac + rolcount=$( /opt/app/pgaas/bin/runpsqll "select count(rolname) from pg_roles" | awk 'NF > 0 {print $1}' ) + case $rolcount in + 1 | 2 ) failed "no additional postgresql role names have been added"; tabtext "$roles" ;; + * ) verified "additional postgresql role names have been added" ;; + esac + dxpgtemporal=$( /opt/app/pgaas/bin/runpsqll "select count(extname) from pg_extension where extname = 'temporal_tables'" | awk 'NF > 0 {print $1}' ) + case $dxpgtemporal in + 1 ) verified "temporal_tables extension has been added" ;; + * ) failed "temporal_tables extension has not been added" ;; + esac +else + failed "/opt/app/pgaas/bin/runpsqll is not installed" +fi + +if ps -ef | grep python3 | grep iDNS-responder.py > /dev/null +then verified "iDNS-responder.py is running" +else failed "iDNS-responder.py is not running" +fi + +if [ -f /opt/app/pgaas/bin/check_cluster ] +then + verified "/opt/app/pgaas/bin/check_cluster is installed" + ckcl=$( /opt/app/pgaas/bin/check_cluster 2>&1 ) + case $ckcl in + *No?such?file?or?directory* ) failed "check_cluster not found"; tabtext "$ckcl" ;; + *ERROR* ) failed "check_cluster returned error:"; tabtext "$ckcl" ;; + *WARNING* ) failed "check_cluster returned a warning:"; tabtext "$ckcl" ;; + * ) verified "check_cluster succeeded" ;; + esac +else + failed "/opt/app/pgaas/bin/check_cluster is not installed" +fi + +echo "$VERIFIEDCOUNT tests passed, $FAILEDCOUNT tests failed, $TOTALCOUNT total tests run" diff --git a/pgaas/src/common/postinst b/pgaas/src/common/postinst new file mode 100755 index 0000000..f24e364 --- /dev/null +++ b/pgaas/src/common/postinst @@ -0,0 +1,55 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +exec 1> /tmp/pgaas.out 2>&1 +set -x +if [ -d /opt/app/postgresql-9.5.2 ] +then export OPENECOMP=false NOTOPENECOMP=true +else export OPENECOMP=true NOTOPENECOMP=false +fi + +if $OPENECOMP +then INSTALL_ROOT= +fi + +echo STARTING $0 $(date) +umask 0 +echo STARTING $0 $(date) >> /tmp/pgaas.inst.report +set -x + +export CDFCFG=${INSTALL_ROOT}/opt/app/cdf/lib/cdf.cfg +export PGAASCFG=${INSTALL_ROOT}/opt/app/pgaas/lib/pgaas.cfg + +if [ -d /opt/app/postgresql-9.5.2 ] +then PGDIR=/opt/app/postgresql-9.5.2 +else PGDIR=/usr/lib/postgresql/9.5 +fi +if [ -d /opt/app/postgresql-config-9.5.2/ ] +then CFGDIR=/opt/app/postgresql-config-9.5.2 +else CFGDIR=/opt/app/postgresql-config +fi + +( + grep '^postgres.x' < $CDFCFG | sed -e 's/^postgres/dcae_admin_db_password/' + echo "db_configuration=$CFGDIR/main/postgresql.conf" + echo "pg_bin_directory=$PGDIR/bin" + echo "skip_configuration_file=$CFGDIR/lib/ignore-database-reconfiguration" +) >> $PGAASCFG + +echo ENDING $0 $(date) +echo ENDING $0 $(date) >> /tmp/pgaas.inst.report +if $NOTOPENECOMP +then sed -n '/^STARTING/,/^ENDING/p' `dirname $0`/../../proc_out >> /tmp/pgaas.inst.report +fi diff --git a/pgaas/src/makefile b/pgaas/src/makefile new file mode 100644 index 0000000..73fe4eb --- /dev/null +++ b/pgaas/src/makefile @@ -0,0 +1,41 @@ + +DEVBIN=../../bin +PKG=pgaas +REPACKAGESWMOPTS= +REPACKAGEDEBIANOPTS= + +INS= ../install +INSSTG= $(INS)/stage +INSCOM= $(INS)/common +KEEP= + +all: + +clean-stage: + rm -rf $(INSSTG) + +clean-common: + rm -rf $(INSCOM) + +clean-ins: clean-stage clean-common + rm -rf $(INS) + +clean: clean-ins + rm -rf *~ + +build: + +stage: clean-stage + cd stage/opt/app/pgaas && $(MAKE) stage STAGEDIR=../../../../$(INSSTG) + find common ! -name makefile ! -name '*~' | cpio -pudmv $(INS) + cp -p repackage.* $(INS) + + +debian: stage + repackage -b debian $(REPACKAGEDEBIANOPTS) -d $(INS) -u + repackage -b debian $(REPACKAGEDEBIANOPTS) -d $(INS) -u -B LATEST + @echo debian built + +upload-javadocs: + @echo nothing to do here + diff --git a/pgaas/src/repackage.json b/pgaas/src/repackage.json new file mode 100644 index 0000000..578fe76 --- /dev/null +++ b/pgaas/src/repackage.json @@ -0,0 +1,25 @@ +{ + "executionGroup": "postgres", + "executionUser": "postgres", + "applicationName": "pgaas", + "description": " PostgreSQL as a Service main scripts ", + "maintainer": "OpenECOMP <dcae@lists.openecomp.org>", + "fileUser": "postgres", + "internalDependencies": [], + "directoryTreeTops": { + "/opt": "/opt/app/pgaas" + }, + "debian": { + "conflicts": [], + "groupId": "org.openecomp.dcae.storage.pgaas", + "replaces": [], + "externalDependencies": [] + }, + "fileGroup": "postgres", + "docker": { + "tag": "latest", + "externalDependencies": [] + }, + "version": "1.0.0", + "groupId": "org.openecomp.dcae.storage.pgaas" +}
\ No newline at end of file diff --git a/pgaas/src/stage/opt/app/pgaas/bin/check_cluster b/pgaas/src/stage/opt/app/pgaas/bin/check_cluster new file mode 100755 index 0000000..0af6d95 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/check_cluster @@ -0,0 +1,154 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# NAME +# check_cluster - check the state of the cluster +# +# USAGE +# check_cluster [-v] [-l] [-t timeout] +# -l do not check localhost first (and restarting the service if necessary) +# -t timeout set how long to wait when accessing the servers +# -v verbose +# +# DESCRIPTION +# Loop through the nodes in the cluster, using pgwget to determine how many are masters, secondaries, or not up. +# Complain about certain situations. +# If there are multiple masters, and this not the first master in the list, then: +# run pg_ctl_restart +# prevent /ro from returning true + +CDF=/opt/app/cdf +if [ -d /opt/app/postgresql-9.5.2 ] +then PGDIR=/opt/app/postgresql-9.5.2 +else PGDIR=/usr/lib/postgresql/9.5 +fi +if [ -d /opt/app/postgresql-config-9.5.2/ ] +then CFGDIR=/opt/app/postgresql-config-9.5.2/ +else CFGDIR=/opt/app/postgresql-config/ +fi +PATH=$PGDIR/bin:$CDF/bin:/opt/app/pgaas/bin:/opt/app/postgresql-prep/bin:$PATH + +usage() +{ + exec 1>&2 + [ $# -gt 0 ] && echo "$@" + echo "Usage: $0 [-v] [-l] [-t timeout]" + echo -e " -l do not check localhost first (and restarting the service if necessary)" + echo -e " -t timeout set how long to wait when accessing the servers" + echo -e " -v verbose" + exit 1 +} + +VERBOSE=false +TIMEOUT=10 +TESTLOCAL=: +while getopts lt:v c +do + case "$c" in + l ) TESTLOCAL=false ;; + t ) TIMEOUT=$OPTARG ;; + v ) VERBOSE=: ;; + \?) usage ;; + esac +done +shift $(($OPTIND - 1)) + +# loop through the nodes in the cluster, using pgwget to determine if any are a master. Save in $@ +master_count=0 +secondary_count=0 +total_count=0 +down_count=0 + +DOWNS= +MASTERS= +SECONDARIES= + +MSEP= +SSEP= +DSEP= +HOSTNAME=$(hostname -f) +FOUNDPREVIOUSMASTER= +DOPGCTLSTOP= + +if $TESTLOCAL +then + isrw=`pgwget --tries=1 --read-timeout=$TIMEOUT --quiet -O/dev/stdout http://localhost:8000/isrw` + case "$isrw" in + Master | Secondary ) ;; + * ) + echo "$(date)|WARNING|RESTARTED|Local iDNS-responder.py not responding. Restarting." + ps -fu postgres | grep "python3 /opt/app/postgresql-prep/bin/iDNS-responder.py" | grep -v grep | awk '{print "kill " $2}' | sh + sleep 10 + ;; + esac +fi + +for i in $(getpropvalue -n pgnodes | sed 's/|/ /g') +do + $VERBOSE && echo -n "Checking $i" + isrw=`pgwget --tries=1 --read-timeout=10 --quiet -O/dev/stdout http://$i:8000/isrw` + $VERBOSE && echo ": $isrw" + case "$isrw" in + Master ) + (( master_count = master_count + 1 )) + (( total_count = total_count + 1 )) + MASTERS="$MASTERS$MSEP$i" + MSEP=" " + if [ -z "$FOUNDPREVIOUSMASTER" ] + then FOUNDPREVIOUSMASTER=yes + elif [ -n "$FOUNDPREVIOUSMASTER" -a "$i" = $HOSTNAME ] + then DOPGCTLSTOP=yes + fi + ;; + Secondary ) + (( secondary_count = secondary_count + 1 )) + (( total_count = total_count + 1 )) + SECONDARIES="$SECONDARIES$SSEP$i" + SSEP=" " + ;; + * ) + DOWNS="$DOWNS$DSEP$i" + DSEP=" " + (( down_count = down_count + 1 )) + (( total_count = total_count + 1 )) + ;; + esac +done + +(( up_count = master_count + secondary_count )) + +date=$(date) +echo "$date|INFO|masters=$master_count $MASTERS|secondaries=$secondary_count $SECONDARIES|down=$down_count $DOWNS|" + +FORCEROOFF=/var/run/postgresql/force-ro-off +if [ $master_count -lt 1 ] +then echo "$date|FATAL|NOMASTER|NO MASTER FOUND|" +elif [ $master_count -gt 1 ] +then echo "$date|FATAL|MULTIPLEMASTERS|TOO MANY MASTERS FOUND|" + if [ -n "$DOPGCTLSTOP" ] + then + touch "$FORCEROOFF" + pg_ctl_stop + fi +fi +if [ -z "$DOPGCTLSTOP" -a -f "$FORCEROOFF" ] +then rm -f "$FORCEROOFF" +fi +if [ $up_count -eq 0 ] +then echo "$date|FATAL|NOREADER|NO SECONDARY FOUND" +fi +if [ $down_count -ne 0 ] +then echo "$date|ERROR|DOWN|One or more systems are down: $DOWNS" +fi diff --git a/pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py b/pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py new file mode 100755 index 0000000..80da148 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py @@ -0,0 +1,819 @@ +#!/usr/bin/python3 +# -*- indent-tabs-mode: nil -*- +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +""" + +NAME + dcae_admin_db.py - given a database description json file, update the current VM accordingly + +USAGE + dcae_admin_db.py [options] configurationChanged json-file + dcae_admin_db.py [options] suspend + dcae_admin_db.py [options] resume + dcae_admin_db.py [options] test + + options: + + -H / --dbhost= - host name, defaults to CFG['dcae_admin_db_hostname'] + -d / --dbdir= - database directory path, defaults to CFG['db_directory'] + -c / --dbconf= - database configuration path, defaults to CFG['db_configuration'] + -D / --dbname= - database name, defaults to CFG['dcae_admin_db_databasename'] + -U / --user= - user to login as, defaults to CFG['dcae_admin_db_username'] + -P / --password= - password for user, defaults to CFG['dcae_admin_db_password'] + -B / --bindir= - postgresql bin directory, defaults to CFG['pg_bin_directory'] + -i / --ignorefile= - skip configuration if this file is present, defaults to CFG['skip_configuration_file'] + -n / --nocreate - do not create the databases / users + -I / --ignoredb - ignore current state of database + -R / --remove - remove old databases / users + -J / --jsontop= - top of json tree, as in \"['pgaas']\" + -e / --errors= - where to redirect error output, defaults to CFG['dcae_admin_db_errors_file'] then stderr + -t / --trace= - where to redirect trace output, defaults to CFG['dcae_admin_db_trace_file'] then stderr + -v / --verbose - verbose, defaults to CFG['dcae_admin_db_verbosity'] + +DESCRIPTION + This program is intended to be executed by the DCAE controller manager. + +When creating a database and set of users, execute the equivalent of this: + + CREATE USER tstdb_admin WITH PASSWORD 'tst'; + CREATE USER tstdb_user WITH PASSWORD 'tst'; + CREATE USER tstdb_viewer WITH PASSWORD 'tst'; + + CREATE ROLE testdb_common_user_role; + CREATE ROLE testdb_common_viewer_role; + + CREATE DATABASE testdb with owner tstdb_admin; + + \connect testdb + + REVOKE ALL on DATABASE testdb FROM testdb_common_viewer_role; + REVOKE ALL on DATABASE testdb FROM testdb_common_user_role; + REVOKE ALL on DATABASE testdb FROM tstdb_user; + REVOKE ALL on DATABASE testdb FROM tstdb_viewer; + + GRANT testdb_common_viewer_role TO testdb_common_user_role; /* user can do everything viewer can */ + GRANT testdb_common_user_role TO tstdb_admin; /* admin can do everything user and viewer can */ + + GRANT CONNECT ON DATABASE testdb TO testdb_common_viewer_role; /* viewer, user, admin can connect */ + + CREATE SCHEMA testdb_db_common AUTHORIZATION tstdb_admin; /* create a schema we can optionally use */ + + ALTER ROLE tstdb_admin IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */ + ALTER ROLE testdb_common_user_role IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */ + ALTER ROLE testdb_common_viewer_role IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */ + + GRANT USAGE ON SCHEMA testdb_db_common TO testdb_common_viewer_role; /* viewer,user can select from schema */ + GRANT CREATE ON SCHEMA testdb_db_common TO tstdb_admin; /* admin can create on schema */ + + ALTER DEFAULT PRIVILEGES FOR ROLE tstdb_admin GRANT SELECT ON TABLES TO testdb_common_viewer_role; /* viewer, user, admin can select on tables */ + ALTER DEFAULT PRIVILEGES FOR ROLE tstdb_admin GRANT INSERT, UPDATE, DELETE, TRUNCATE ON TABLES TO testdb_common_user_role; /* user, admin can ins/upd/del/tru on tables */ + ALTER DEFAULT PRIVILEGES FOR ROLE tstdb_admin GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO testdb_common_user_role; /* user, admin can update on sequences */ + + GRANT TEMP ON DATABASE testdb TO testdb_common_user_role; /* user, admin can create temp tables */ + + GRANT testdb_common_user_role TO tstdb_user; + GRANT testdb_common_viewer_role TO tstdb_viewer; + ALTER ROLE tstdb_user IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */ + ALTER ROLE tstdb_viewer IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */ + +""" + +import getopt +import psycopg2 +import sys +import re +import subprocess +import json +import os +import time + +sys.path.append("/opt/app/pgaas/lib") +import CommonLogger + +verbose = 0 +quiet = False +errorOutput = sys.stderr +traceOutput = sys.stderr +errorLogger = debugLogger = auditLogger = metricsLogger = None + +def usage(msg = None): + """ + Print a usage message and exit + """ + sys.stdout = sys.stderr + if msg != None: + print(msg) + print("Usage:") + print("dcae_admin_db.py [options] configurationChanged json-file") + print("dcae_admin_db.py [options] suspend") + print("dcae_admin_db.py [options] resume") + print("dcae_admin_db.py [options] test") + print("") + print("options:") + print("-H / --dbhost= - host name, defaults to CFG['dcae_admin_db_hostname']") + print("-d / --dbdir= - database directory path, defaults to CFG['db_directory']") + print("-c / --dbconf= - database directory path, defaults to CFG['db_configuration']") + print("-D / --dbname= - database name, defaults to CFG['dcae_admin_db_databasename']") + print("-n / --nocreate - do not create the databases / users") + print("-I / --ignoredb - ignore current state of database") + print("-U / --user= - user to login as, defaults to CFG['dcae_admin_db_username']") + print("-P / --password= - password for user, defaults to CFG['dcae_admin_db_password']") + print("-B / --bindir= - postgresql bin directory, defaults to CFG['pg_bin_directory']") + print("-i / --ignorefile= - skip configuration if this file is present, defaults to CFG['skip_configuration_file']") + print("-R / --remove - remove old databases / users") + print("-J / --jsontop= - top of json tree, as in \"['pgaas']\"") + print("-l / --logcfg= - ECOMP DCAE Common Logging configuration file") + print("-e / --errors= - where to redirect error output, defaults to CFG['dcae_admin_db_errors_file'] then stderr") + print("-t / --trace= - where to redirect trace output, defaults to CFG['dcae_admin_db_trace_file'] then stderr") + print("-v - verbose") + sys.exit(2) + +def checkOption(options, name, propname, optletter, encrypted=False, cdfPropname = None): + """ + Check if the specified option exists. If not, grab it from the configuration file. + Complain if it still does not exist. + """ + if name not in options: + ret = getPgaasPropValue(propname, encrypted=encrypted, dflt=None, skipComplaining=True) + if ret is None and cdfPropname is not None: + ret = getCdfPropValue(cdfPropname, encrypted=encrypted) + options[name] = ret + requireOption("either %s or config[%s]" % (optletter, propname), options[name]) + +def reviewOpts(): + """ + Parse the options passed to the command, and return them in the dictionary + """ + try: + opts, args = getopt.getopt(sys.argv[1:], "B:c:D:d:e:H:IJ:l:nP:Rt:U:hv?", + [ "dbhost=", "dbdir=", "dbconf=", + "dbname=", "dbuser=", "dbpassword=", + "bindir=", "errors=", "trace=", "logcfg=", + "nocreate", "ignoredb", "remove", "ignorefile=", + "jsontop=", + "help", "verbose"]) + except getopt.GetoptError as err: + usage(str(err)) + + propVerbosity = getPgaasPropValue("dcae_admin_db_verbosity", dflt='0') + if propVerbosity is not None: + global verbose + verbose = int(propVerbosity) + retOptions = { } + ignoreFile = getPgaasPropValue("skip_configuration_file", dflt=None) + for o, a in opts: + if o in ("-v", "--verbose"): + # global verbose + verbose += 1 + elif o in ("-c", "--dbconf"): + retOptions["dbconf"] = a + elif o in ("-H", "--dbhost"): + retOptions["dbhost"] = a + elif o in ("-d", "--dbdir"): + retOptions["dbdir"] = a + elif o in ("-D", "--dbname"): + retOptions["dbname"] = a + elif o in ("-U", "--dbuser"): + retOptions["dbuser"] = a + elif o in ("-P", "--dbpassword"): + retOptions["dbpassword"] = a + elif o in ("-B", "--bindir"): + retOptions["bindir"] = a + elif o in ("-n", "--nocreate"): + retOptions["nocreate"] = True + elif o in ("-I", "--ignoredb"): + retOptions["ignoredb"] = True + elif o in ("-R", "--remove"): + retOptions["noremove"] = True + elif o in ("-J", "--jsontop"): + retOptions["jsontop"] = a + elif o in ("-l", "--logcfg"): + retOptions["logcfg"] = a + elif o in ("-e", "--errors"): + retOptions["errors"] = a + elif o in ("-i", "--ignorefile"): + ignoreFile = a + elif o in ("-t", "--trace"): + retOptions["trace"] = a + elif o in ("-h", "--help"): + usage() + else: + usage("unhandled option: %s" % o) + if "errors" not in retOptions: + retOptions["errors"] = getPgaasPropValue("dcae_admin_db_errors_file") + if "errors" in retOptions and retOptions["errors"] is not None: + try: + errorOutput = open(retOptions["errors"], "a") + except Exception as e: + die("Cannot open errors file '%s': %s" % (retOptions["errors"], e)) + if ignoreFile is not None: + trace("checking to see if skip_configuration_file(%s) exists" % ignoreFile) + retOptions["ignorefile"] = "yes" if os.path.isfile(ignoreFile) else "no" + trace("ignorefile=%s" % retOptions["ignorefile"]) + else: + retOptions["ignorefile"] = None + if "trace" not in retOptions: + retOptions["trace"] = getPgaasPropValue("dcae_admin_db_trace_file") + if "trace" in retOptions and retOptions["trace"] is not None: + try: + traceOutput = open(retOptions["trace"], "a") + except Exception as e: + die("Cannot open trace file '%s': %s" % (retOptions["trace"], e)) + if "logcfg" not in retOptions: + retOptions["logcfg"] = getPgaasPropValue("dcae_admin_db_common_logger_config") + if "logcfg" in retOptions and retOptions["logcfg"] is not None: + logcfg = retOptions["logcfg"] + import uuid + instanceUUID = uuid.uuid1() + serviceName = "DCAE/pgaas" + # print(">>>>>>>>>>>>>>>> using common logger. UUID=%s, serviceName=%s, cfg=%s" % (instanceUUID, serviceName, logcfg)) + global errorLogger, debugLogger, auditLogger, metricsLogger + errorLogger = CommonLogger.CommonLogger(logcfg, "error", instanceUUID=instanceUUID, serviceName=serviceName) + debugLogger = CommonLogger.CommonLogger(logcfg, "debug", instanceUUID=instanceUUID, serviceName=serviceName) + auditLogger = CommonLogger.CommonLogger(logcfg, "audit", instanceUUID=instanceUUID, serviceName=serviceName) + metricsLogger = CommonLogger.CommonLogger(logcfg, "metrics", instanceUUID=instanceUUID, serviceName=serviceName) + auditLogger.info("using common logger. UUID=%s, serviceName=%s, cfg=%s" % (instanceUUID, serviceName, logcfg)) + + checkOption(retOptions, "dbname", "dcae_admin_db_databasename", "-D") + checkOption(retOptions, "dbuser", "dcae_admin_db_username", "-U") + checkOption(retOptions, "dbpassword", "dcae_admin_db_password", "-P", encrypted=True, cdfPropname="postgres") + checkOption(retOptions, "dbhost", "dcae_admin_db_hostname", "-H") + checkOption(retOptions, "dbdir", "db_directory", "-d") + checkOption(retOptions, "bindir", "pg_bin_directory", "-B") + if "jsontop" not in retOptions: + retOptions["jsontop"] = getPgaasPropValue("dcae_admin_db_jsontop") + trace("env=%s" % str(os.environ)) + trace("ignorefile=%s" % ignoreFile) + return retOptions, args + +def main(): + keyedOptions, args = reviewOpts() + trace("Invoked as: %s" % str(sys.argv)) + audit("Invoked as: %s" % str(sys.argv)) + + if len(args) == 0: + usage("no operation specified") + elif args[0] == "configurationChanged": + if len(args) != 2: + usage("too many arguments") + configurationChanged(keyedOptions, args[1]) + elif args[0] == "suspend": + if len(args) != 1: + usage("too many arguments") + suspendOperations(keyedOptions) + elif args[0] == "resume": + if len(args) != 1: + usage("too many arguments") + resumeOperations(keyedOptions) + elif args[0] == "test": + if len(args) != 1: + usage("too many arguments") + testOperations(keyedOptions) + else: + usage("unrecognized operation '%s'" % args[0]) + +def suspendOperations(options): + """ + Execute the "suspend" sub-command. + """ + runProgram(["pkill", "repmgrd"]) + program = options["bindir"] + "/pg_ctl" + cmd = [program, "stop", "-D", options["dbdir"]] + runProgram(cmd) + audit("suspendOperations") + +def resumeOperations(options): + """ + Execute the "resume" sub-command. + """ + cmd = [options["bindir"] + "/pg_ctl", "start", "-D", options["dbdir"], "-o", "configfile=" + options["dbconf"]] + runProgram(cmd) + runProgram(["/opt/app/pgaas/bin/repmgrcd", "-d"]) + audit("resumeOperations") + +def testOperations(options): + """ + Respond to the "test" sub-command. + """ + program = options["bindir"] + "/pg_ctl" + cmd = [program, "status", "-D", options["dbdir"]] + ret = runProgram(cmd) + # pg_ctl: no server running + # pg_ctl: server is running (PID: 13988) + cmdRepmgr = ["pgrep", "repmgrd"] + retRepmgr = runProgram(cmdRepmgr) + + msg = "????" + if re.search("no server running", ret): + msg = "RED: no PG server running" + elif re.search("server is running", ret) and re.search("[0-9]+", retRepmgr): + msg = "GREEN" + elif re.search("server is running", ret): + msg = "YELLOW: no repmgrd running" + elif re.search("[0-9]+", retRepmgr): + msg = "YELLOW: no PG server running" + else: + msg = "YELLOW: neither PG server nor repmgrd are running" + audit("test: " + msg) + print(msg, end="") + +def runProgram(cmd): + """ + Run the given command, returning the standard output as a string. + If there is an error, return None. + """ + try: + p=subprocess.Popen(cmd,shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + (stdout, stderr) = p.communicate() + except Exception as e: + print("Error running program because: {0}".format(e), file=errorOutput) + return None + else: + if stderr: + print("Error running program because: {0} ".format(stderr), file=errorOutput) + return None + else: + trace("runProgram() => " + str(stdout), minLevel=2) + return stdout.decode('utf-8').rstrip('\n') + +def configurationChanged(options, jsonFile): + """ + We received a new JSON configuration file + """ + audit("configurationChanged " + jsonFile) + if options["ignorefile"] == "yes": + trace("skipping database reconfiguration because skip_configuration_file exists") + return + + if not os.path.isfile(jsonFile): + die("json file %s does not exist" % jsonFile) + + try: + inp = json.load(open(jsonFile,"r")) + except Exception as e: + die("Cannot open jsonFile '%s': %s" % (jsonFile, e)) + + if verbose: + dumpJSON(inp, "incoming JSON") + + jsonTop = options["jsontop"] + if not jsonTop is None: + e = "inp" + jsonTop + trace("eval(%s)" % e) + inp = eval(e,{"__builtins__":None},{"inp":inp}) + if verbose: + dumpJSON(inp, "modified JSON") + + # trace("version=%s" % requireJSON("version", inp, "version")) + requireJSON("databases", inp, "databases") + con = None + try: + con = dbConnect(database = options["dbname"], user = options["dbuser"], password = options["dbpassword"], host = options["dbhost"]) + setupDatabases(con, options, requireJSON("databases", inp, "databases")) + + except psycopg2.DatabaseError as e: + die('Error %s' % e) + + finally: + if con: + con.commit() + con.close() + +def dumpJSON(js, msg): + tracePrint("vvvvvvvvvvvvvvvv %s" % msg) + tracePrint(json.dumps(js, indent=4)) + tracePrint("^^^^^^^^^^^^^^^^ %s" % msg) + +def setupDatabases(con, options, dbList): + """ + Do what is needed to set up all of the databases + """ + currentDatabases = dbGetFirstColumnAsMap(con, "select datname from pg_database where datistemplate = false") + currentRolenames = dbGetFirstColumnAsMap(con, "select rolname from pg_roles") + trace("currentDatabases = " + str(currentDatabases)) + for dbName in dbList: + trace("dbName='%s'" % str(dbName)) + setupDatabase(con, options, currentDatabases, currentRolenames, dbName, dbList[dbName]) + +def setupDatabase(con, options, currentDatabases, currentRolenames, dbName, dbInfo): + """ + Do what is needed to set up a given databases and its users + """ + + dbOwnerRole = requireJSON("databases[].ownerRole", dbInfo, "ownerRole") + trace("dbName='%s', dbOwnerRole='%s'" % (dbName, dbOwnerRole)) + doesDbExist = dbName in currentDatabases + trace("does %s exist? %s" % (dbName, doesDbExist)) + foundOwnerRole = False + dbRoles = dbInfo["roles"] + for name in dbRoles: + u = dbRoles[name] + if name == dbOwnerRole and u["role"] == "admin": + foundOwnerRole = True + if u["role"] not in ("admin","writer","reader"): + die("For database %s, the role '%s' is not one of admin/writer/reader" % (dbName, u.role)) + if not foundOwnerRole: + die("For database %s, information on the ownerRole '%s' was not found" % (dbName, dbOwnerRole)) + for name in dbRoles: + userInfo = dbRoles[name] + if name in currentRolenames and ("ignoredb" not in options or not options["ignoredb"]): + trace("The role %s already exists, skipping" % name) + updatePassword(con, options, dbName, name, userInfo) + else: + setupUser(con, options, dbName, name, userInfo) + if doesDbExist and ("ignoredb" not in options or not options["ignoredb"]): + trace("The database %s already exists, skipping" % dbName) + else: + makeDatabase(con, options, dbName, dbOwnerRole, dbInfo, dbRoles) + for name in dbRoles: + userInfo = dbRoles[name] + if name in currentRolenames and ("ignoredb" not in options or not options["ignoredb"]): + trace("The role %s already exists, skipping grants" % name) + else: + modifyGrants(con, options, dbName, name, userInfo) + +def makeDatabase(con, options, dbName, dbOwnerRole, dbInfo, dbRoles): + """ + Execute the SQL to create a database + + TODO: verify grants against what is actually there + """ + ownerRole = dbInfo["ownerRole"] + userRole = "{0}_common_user_role".format(dbName) + viewerRole = "{0}_common_viewer_role".format(dbName) + + optionalDbExecute(con, options, "CREATE ROLE {0}".format(userRole)) + optionalDbExecute(con, options, "CREATE ROLE {0}".format(viewerRole)) + + trace("Creating database %s with owner '%s'" % (dbName, ownerRole)) + optionalDbExecute(con, options, "CREATE DATABASE %s WITH OWNER %s" % (dbName, ownerRole)) + con2 = None + try: + con2 = dbConnect(database = dbName, user = options["dbuser"], password = options["dbpassword"], host = options["dbhost"]) + + optionalDbExecute(con2, options, "REVOKE ALL on DATABASE {0} FROM {1}".format(dbName, viewerRole)) + optionalDbExecute(con2, options, "REVOKE ALL on DATABASE {0} FROM {1}".format(dbName, userRole)) + for name in dbRoles: + userInfo = dbRoles[name] + if userInfo["role"] == "writer" or userInfo["role"] == "reader": + optionalDbExecute(con2, options, "REVOKE ALL on DATABASE {0} FROM {1}".format(dbName, name)) + + # user can do everything viewer can + optionalDbExecute(con2, options, "GRANT {0} TO {1}".format(viewerRole, userRole)) + # admin can do everything user and viewer can + optionalDbExecute(con2, options, "GRANT {0} TO {1}".format(userRole, ownerRole)) + + # viewer, user, admin can connect + optionalDbExecute(con2, options, "GRANT CONNECT ON DATABASE {0} TO {1}".format(dbName, viewerRole)) + + # create a schema we can optionally use * + schemaName = "{0}_db_common".format(dbName) + optionalDbExecute(con2, options, "CREATE SCHEMA if not exists {0} AUTHORIZATION {1}".format(schemaName, ownerRole)) + + # search_path is not inherited, so set it here + for role in [ ownerRole, userRole, viewerRole ]: + optionalDbExecute(con2, options, "ALTER ROLE {1} IN DATABASE {0} SET search_path = public, {2}".format(dbName, role, schemaName)) + + # viewer,user can select from schema + optionalDbExecute(con2, options, "GRANT USAGE ON SCHEMA {0} TO {1}".format(schemaName, viewerRole)) + # admin can create on schema + optionalDbExecute(con2, options, "GRANT CREATE ON SCHEMA {0} TO {1}".format(schemaName, ownerRole)) + + # viewer, user, admin can select on tables + optionalDbExecute(con2, options, "ALTER DEFAULT PRIVILEGES FOR ROLE {1} GRANT SELECT ON TABLES TO {0}".format(viewerRole, ownerRole)) + # user, admin can ins/upd/del/tru on tables + optionalDbExecute(con2, options, "ALTER DEFAULT PRIVILEGES FOR ROLE {1} GRANT INSERT, UPDATE, DELETE, TRUNCATE ON TABLES TO {0}".format(userRole, ownerRole)) + # user, admin can update on sequences + optionalDbExecute(con2, options, "ALTER DEFAULT PRIVILEGES FOR ROLE {1} GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO {0}".format(userRole, ownerRole)) + + # user, admin can create temp tables + optionalDbExecute(con2, options, "GRANT TEMP ON DATABASE {0} TO {1}".format(dbName, userRole)) + + for name in dbRoles: + userInfo = dbRoles[name] + if userInfo["role"] == "writer": + optionalDbExecute(con2, options, "GRANT {0} TO {1}".format(userRole, name)) + elif userInfo["role"] == "reader": + optionalDbExecute(con2, options, "GRANT {0} TO {1}".format(viewerRole, name)) + + # search_path is not inherited, so set it here + optionalDbExecute(con2, options, "ALTER ROLE {1} IN DATABASE {0} SET search_path = public, {2}".format(dbName, name, schemaName)) + + except psycopg2.DatabaseError as e: + die('Error %s' % e) + + finally: + if con2: + con2.commit() + con2.close() + +def checkUsername(userName): + """ + A value of type name is a string of 63 or fewer characters1. A name must start + with a letter or an underscore; the rest of the string can contain letters, + digits, and underscores. + """ + trace("checkUsername(%s)" % userName) + if re.match("[A-Za-z_][A-Za-z0-9_]*$", userName): + return True + else: + errorPrint("%s is not a valid userName" % userName) + return False + +def setupUser(con, options, dbName, userName, userInfo): + """ + Do what is needed to to set up a user for a database + """ + if checkUsername(userName): + trace("For dbName='%s', create user '%s'" % (dbName, userName)) + userPassword = userInfo["password"] + optionalDbExecute(con, options, "create user %s with password '%s'" % (userName, userPassword)) + +def updatePassword(con, options, dbName, userName, userInfo): + """ + Do what is needed to update a user's password + """ + if checkUsername(userName): + trace("For dbName='%s', alter user '%s' password" % (dbName, userName)) + userPassword = userInfo["password"] + optionalDbExecute(con, options, "alter user %s with password '%s'" % (userName, userPassword)) + +def modifyGrants(con, options, dbName, userName, userInfo): + """ + Do what is needed to to set up a user for a database with the proper grants + + TODO: if user exist, verify current grants + """ + if checkUsername(userName): + userRole = userInfo["role"] + trace("For dbName='%s', set up user '%s' as a '%s'" % (dbName, userName, userRole)) + if userRole == "writer": + optionalDbExecute(con, options, "grant %s_common_user_role to %s" % (dbName, userName)) + elif userRole == "reader": + optionalDbExecute(con, options, "grant %s_common_viewer_role to %s" % (dbName, userName)) + # elif userRole == "admin": + # optionalDbExecute(con, options, "grant %s_common_admin_role to %s" % (dbName, userName)) + else: + trace("nothing to grant %s" % userName) + +def optionalDbExecute(con, options, cmd): + if "nocreate" in options and options["nocreate"]: + print(cmd) + else: + audit("Running: " + cmd) + dbExecute(con, cmd) + +""" +database utility functions +""" + +# def dbGetMap(con, cmd, args=[], skipTrace=False): +# def dbGetOneRowMap(con, cmd, args=[], skipTrace=False): + +def dbGetFirstRowOneValue(con, cmd, args=[], skipTrace=False): + """ + Do a select and return a single value from the first row + """ + row = dbGetFirstRow(con, cmd, args, skipTrace) + trace("row=" + str(row)) + if row is not None and len(row) > 0: + return row[0] + return None + +def dbGetFirstRow(con, cmd, args=[], skipTrace=False): + """ + Do a select and return the values from the first row + """ + cursor = dbExecute(con, cmd, args, skipTrace) + return cursor.fetchone() + +def dbGetFirstColumn(con, cmd, args=[], skipTrace=False): + """ + Do a select and return the first column's value from each row + """ + ret = [] + cursor = dbExecute(con, cmd, args, skipTrace) + for row in cursor: + for col in row: + ret.append(col) + break + return ret + +def dbGetFirstColumnAsMap(con, cmd, args=[], skipTrace=False, val=1): + """ + Do a select and return the first column's value from each row + """ + ret = {} + cursor = dbExecute(con, cmd, args, skipTrace) + for row in cursor: + for col in row: + ret[col] = val + break + return ret + +def dumpTable(con, tableName, max=-1): + """ + If being extra verbose, print out the entire table + """ + if verbose < 2: + return + print("================ " + tableName + " ================", file=traceOutput) + + cols = dbGetFirstColumn(con, "select column_name from information_schema.columns where table_name='" + tableName + "'", skipTrace=True) + print("num", end="|", file=traceOutput) + for col in cols: + print(col, end="|", file=traceOutput) + print("", file=traceOutput) + + if max > -1: + cursor = dbExecute(con, "select * from " + tableName + " limit " + str(max), skipTrace=True) + else: + cursor = dbExecute(con, "select * from " + tableName, skipTrace=True) + i = 0 + for row in cursor: + print("%d" % i, end="|", file=traceOutput) + i += 1 + for col in row: + print("%s" % (col), end="|", file=traceOutput) + print("", file=traceOutput) + print("================================================", file=traceOutput) + +def dbExecute(con, statement, args=[], skipTrace=False): + """ + Create a cursor, instantiate the arguments into a statement, trace print the statement, and execute the statement. + Return the cursor + """ + cursor = con.cursor() + stmt = cursor.mogrify(statement, args); + if not skipTrace: + trace("executing:" + str(stmt)) + cursor.execute(stmt) + global quiet + if not skipTrace: + trace("statusmessage=" + cursor.statusmessage + ", rowcount=" + str(cursor.rowcount)) + return cursor + +def dbConnect(database, user, password, host, autocommit = True): + """ + Create a connection, logging it in the process + Return the connection + """ + trace("connecting to database %s as %s on host %s" % (database, user, host)) + con =psycopg2.connect(database = database, user = user, password = password, host = host) + con.autocommit = autocommit + return con + +""" +Utility functions +""" + +def die(msg): + """ + Print a message to the error file and exit. + """ + errorPrint(msg) + sys.exit(1) + +def errorPrint(msg, file=errorOutput): + """ + Print a message to the error file. + """ + global errorLogger + # print("----------------> errorLogger=%s" % str(errorLogger)) + if errorLogger is not None: + errorLogger.error(msg) + else: + taggedPrint("ERROR", msg, file=file) + + +def tracePrint(msg, file=traceOutput): + """ + Print a message to the trace file. + """ + global debugLogger + # print("----------------> debugLogger=%s" % str(debugLogger)) + if debugLogger is not None: + debugLogger.debug(msg) + else: + taggedPrint("DEBUG", msg, file=file) + +def taggedPrint(tag, msg, file): + """ + Print a message to the trace file. + """ + dt = time.strftime('%Y-%m-%d %T', time.localtime()) + print("%s %s: %s" % (dt, tag, msg), file=file) + +def requireOption(nm, val): + """ + Die if a program parameter is not set + """ + return require("option", nm, val) + +def requireJSON(prnm, dict, nm): + """ + Die if a JSON value is not set + """ + if nm not in dict: + die("The JSON value '%s' is missing" % prnm) + return dict[nm] + +def require(type, nm, val): + """ + Die if a value is not set + """ + if val is None: + die("The %s '%s' is missing" % (type, nm)) + return val + +def trace(msg, minLevel=1): + """ + Print a message to trace output if verbose is turned on. + """ + global verbose + if verbose >= minLevel: + tracePrint(msg) + +def audit(msg): + """ + Print a message to audit log if one is being used + """ + global auditLogger + if auditLogger is not None: + auditLogger.info(msg) + +def getCdfPropValue(nm, encrypted=False, cfg="/opt/app/cdf/lib/cdf.cfg", dflt=None, skipComplaining=False): + """ + Return a value from the configuration file /opt/app/cdf/lib/cdf.cfg + """ + return getPropValue(nm=nm, encrypted=encrypted, cfg=cfg, dflt=dflt, skipComplaining=skipComplaining) + +def getPgaasPropValue(nm, encrypted=False, cfg="/opt/app/pgaas/lib/pgaas.cfg", dflt=None, skipComplaining=False): + """ + Return a value from the configuration file /opt/app/pgaas/lib/pgaas.cfg + """ + return getPropValue(nm=nm, encrypted=encrypted, cfg=cfg, dflt=dflt, skipComplaining=skipComplaining) + +getPropDict = { } + +def getPropValue(nm, encrypted=False, cfg=None, dflt=None, skipComplaining=False): + """ + Return a value from the specified configuration file + """ + if cfg is None: + return None + global getPropDict + if getPropDict.get(cfg): + savedDate = getPropDict[cfg] + # trace("getPropValue: savedDate[" + cfg + "]=" + str(savedDate)) + cfgDate = os.path.getmtime(cfg) + # trace("getPropValue: cfgDate=" + str(cfgDate)) + if float(savedDate) >= float(cfgDate): # cfg has not changed + val = getPropDict.get(cfg + ":" + nm) + # trace("getPropValue: val=" + val) + if val is not None: + # trace("getPropValue: getPropValue(saved) => '%s'" % str(val)) + return val + else: # clear out any previously saved keys + cfgcolon = cfg + ":" + for k in list(getPropDict.keys()): + if re.match(cfgcolon, k): + del getPropDict[k] + getPropValueProgram = '/opt/app/cdf/bin/getpropvalue' + if encrypted: + cmd = [getPropValueProgram, "-f", cfg, "-x", "-n", nm] + else: + cmd = [getPropValueProgram, "-f", cfg, "-n", nm] + # trace("getPgaasPropValue: cmd=" + str(cmd)) + + try: + with subprocess.Popen(cmd,shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p: + (origString, stderrString) = p.communicate() + except Exception as e: + traceback.print_exc() + print("Error decoding string because {0}".format(e), file=errorOutput) + return None + else: + if stderrString: + if not re.search("Configuration property .* must be defined", stderrString.decode('utf-8')) and not skipComplaining: + print("Error decoding string because: {0} ".format(stderr), file=errorOutput) + return dflt + else: + trace("getPgaasPropValue() => " + str(origString), minLevel=2) + return origString.decode('utf-8').rstrip('\n') + +if __name__ == "__main__": + main() diff --git a/pgaas/src/stage/opt/app/pgaas/bin/in.json b/pgaas/src/stage/opt/app/pgaas/bin/in.json new file mode 100755 index 0000000..cad8790 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/in.json @@ -0,0 +1,97 @@ +{ + # sample configuration file for dcae_admin_db + "vmConfiguration": { + "$class": "org.openecomp.dcae.controller.service.storage.postgres.service.PostgresServiceConfiguration", + "state": "master", + "databases": { + "odcit": { + "created": "2016-05-10T01:52:39.431+0000", + "description": "A&AI DCAE Central Inventory", + "ownerRole": "odcit_admin", + "contacts": {"ws4361": { + "created": "2016-05-10T01:52:39.431+0000", + "fullName": "Wen Shang" + }}, + "roles": { + "odcit_admin": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "odcit123", + "role": "admin", + "roleComments": [] + }, + "odcit_user": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "odcit123", + "role": "writer", + "roleComments": [] + }, + "odcit_viewer": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "odcit123", + "role": "reader", + "roleComments": [] + } + } + }, + "dmaap": { + "created": "2016-05-10T01:52:39.431+0000", + "description": "DMAAP Databus Configuration", + "ownerRole": "dmaap_admin", + "contacts": {"dl715d": { + "created": "2016-05-10T01:52:39.431+0000", + "fullName": "Dominic Lunanuova" + }}, + "roles": { + "dmaap_admin": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "dmaap123", + "role": "admin", + "roleComments": [] + }, + "dmaap_user": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "dmaap123", + "role": "writer", + "roleComments": [] + }, + "dmaap_viewer": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "dmaap123", + "role": "reader", + "roleComments": [] + } + } + }, + "dcae_inv": { + "created": "2016-05-10T01:52:39.431+0000", + "description": "Running DCAE Services", + "ownerRole": "dcae_inv_admin", + "contacts": { + "mh677g": { + "created": "2016-05-10T01:52:39.431+0000", + "fullName": "Michael Hwang" + }}, + "roles": { + "dcae_inv_admin": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "dcae_inv123", + "role": "admin", + "roleComments": [] + }, + "dcae_inv_user": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "dcae_inv123", + "role": "writer", + "roleComments": [] + }, + "dcae_inv_viewer": { + "created": "2016-05-10T01:52:39.431+0000", + "password": "dcae_inv123", + "role": "reader", + "roleComments": [] + } + } + } + } + } +}
\ No newline at end of file diff --git a/pgaas/src/stage/opt/app/pgaas/bin/isrw b/pgaas/src/stage/opt/app/pgaas/bin/isrw new file mode 100755 index 0000000..4ec3dcb --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/isrw @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +export PATH=$PATH:/opt/java/jdk/jdk170/bin:/opt/app/cdf/bin:/opt/app/pgaas/bin:$PATH + +case `show_pg_is_in_recovery` in + *f* ) echo "Master" ;; + *t* ) echo "Secondary" ;; +esac diff --git a/pgaas/src/stage/opt/app/pgaas/bin/list_masters b/pgaas/src/stage/opt/app/pgaas/bin/list_masters new file mode 100755 index 0000000..d922739 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/list_masters @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# NAME +# list_masters - loop through the nodes in the cluster, using pgwget to determine if any are a master. + +CDF=/opt/app/cdf +if [ -d /opt/app/postgresql-9.5.2 ] +then PGDIR=/opt/app/postgresql-9.5.2 +else PGDIR=/usr/lib/postgresql/9.5 +fi +if [ -d /opt/app/postgresql-config-9.5.2/ ] +then CFGDIR=/opt/app/postgresql-config-9.5.2/ +else CFGDIR=/opt/app/postgresql-config/ +fi +PATH=$PGDIR/bin:$CDF/bin:/opt/app/pgaas/bin:/opt/app/postgresql-prep/bin:$PATH + +# loop through the nodes in the cluster, using pgwget to determine if any are a master. Save in $@ +for i in $(getpropvalue -n pgnodes | sed 's/|/ /g') +do + if pgwget --quiet -O/dev/null http://$i:8000/rw + then set -- "$@" $i + fi +done + +echo "$@" +case $# in + 1 ) exit 0 ;; # one master exists and is running + 0 ) exit 1 ;; # no masters exist + * ) exit 2 ;; # more than one master exist +esac diff --git a/pgaas/src/stage/opt/app/pgaas/bin/makefile b/pgaas/src/stage/opt/app/pgaas/bin/makefile new file mode 100644 index 0000000..877f8ba --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/makefile @@ -0,0 +1,47 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +all: + +test: + sudo -u postgres ./dcae_admin_db.py configurationChanged in.json + +STAGEDIR=/dev/null +DISTPATH=/opt/app/pgaas + +PYFILES= dcae_admin_db.py +SHFILES= check_cluster isrw list_masters \ + pg_ctl_promote pg_ctl_restart pg_ctl_start pg_ctl_status pg_ctl_stop repmgrc repmgrdc \ + runpsql runpsqll startpsql setpropvalue show_pg_is_in_recovery show_pg_stat_activity show_pg_stat_archiver show_pg_stat_bgwriter \ + show_pg_stat_database show_pg_stat_database_conflicts show_pg_statio_user_functions show_pg_statio_user_indexes \ + show_pg_statio_user_sequences show_pg_statio_user_tables show_pg_stat_user_indexes show_pg_stat_user_tables \ + update_var_run_isrw + +stage: + rm -rf $(STAGEDIR)/$(DISTPATH)/bin + mkdir -p $(STAGEDIR)/$(DISTPATH)/bin + for i in *; do \ + case $$i in \ + *.py ) \ + j=`basename $$i .py`; \ + cp $$i $(STAGEDIR)/$(DISTPATH)/bin/$$j; \ + chmod a+x $(STAGEDIR)/$(DISTPATH)/bin/$$j; \ + ;; \ + makefile | *~ ) ;; \ + * ) \ + cp $$i $(STAGEDIR)/$(DISTPATH)/bin/$$i; \ + chmod a+x $(STAGEDIR)/$(DISTPATH)/bin/$$i; \ + ;; \ + esac; \ + done diff --git a/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_promote b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_promote new file mode 100755 index 0000000..b7d011c --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_promote @@ -0,0 +1,23 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +pg_ctl promote -D /dbroot/pgdata/main/ diff --git a/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_restart b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_restart new file mode 100755 index 0000000..db00739 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_restart @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +if [ -f /opt/app/postgresql-config/main/postgresql.conf ] +then CONF=/opt/app/postgresql-config/main/postgresql.conf +elif [ -f /opt/app/postgresql-config-9.5.2/main/postgresql.conf ] +then CONF=/opt/app/postgresql-config-9.5.2/main/postgresql.conf +else echo "$0: Cannot find PostgreSQL configuration" 1>&2; exit 1 +fi + +pg_ctl stop -D /dbroot/pgdata/main/ +echo Restarting in 5 +sleep 1 +echo Restarting in 4 +sleep 1 +echo Restarting in 3 +sleep 1 +echo Restarting in 2 +sleep 1 +echo Restarting in 1 +sleep 1 +pg_ctl start -D /dbroot/pgdata/main/ -o "-c config_file=$CONF" diff --git a/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_start b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_start new file mode 100755 index 0000000..8cb831c --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_start @@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +if [ -f /opt/app/postgresql-config/main/postgresql.conf ] +then CONF=/opt/app/postgresql-config/main/postgresql.conf +elif [ -f /opt/app/postgresql-config-9.5.2/main/postgresql.conf ] +then CONF=/opt/app/postgresql-config-9.5.2/main/postgresql.conf +else echo "$0: Cannot find PostgreSQL configuration" 1>&2; exit 1 +fi + +pg_ctl start -D /dbroot/pgdata/main/ -o "-c config_file=$CONF" diff --git a/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_status b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_status new file mode 100755 index 0000000..416a00e --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_status @@ -0,0 +1,23 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +pg_ctl status -D /dbroot/pgdata/main/ diff --git a/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_stop b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_stop new file mode 100755 index 0000000..0d9a23c --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/pg_ctl_stop @@ -0,0 +1,23 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +pg_ctl stop -D /dbroot/pgdata/main/ diff --git a/pgaas/src/stage/opt/app/pgaas/bin/repmgrc b/pgaas/src/stage/opt/app/pgaas/bin/repmgrc new file mode 100644 index 0000000..57240cf --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/repmgrc @@ -0,0 +1,29 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +if [ -f /opt/app/postgresql-config/main/repmgr.conf ] +then CONF=/opt/app/postgresql-config/main/repmgr.conf +elif [ -f /opt/app/postgresql-config-9.5.2/main/repmgr.conf ] +then CONF=/opt/app/postgresql-config-9.5.2/main/repmgr.conf +else echo "$0: Cannot find repmgr configuration" 1>&2; exit 1 +fi + +repmgr -f $CONF "$@" diff --git a/pgaas/src/stage/opt/app/pgaas/bin/repmgrdc b/pgaas/src/stage/opt/app/pgaas/bin/repmgrdc new file mode 100644 index 0000000..cfbaa22 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/repmgrdc @@ -0,0 +1,29 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +if [ -f /opt/app/postgresql-config/main/repmgr.conf ] +then CONF=/opt/app/postgresql-config/main/repmgr.conf +elif [ -f /opt/app/postgresql-config-9.5.2/main/repmgr.conf ] +then CONF=/opt/app/postgresql-config-9.5.2/main/repmgr.conf +else echo "$0: Cannot find repmgr configuration" 1>&2; exit 1 +fi + +repmgrd -f $CONF "$@" diff --git a/pgaas/src/stage/opt/app/pgaas/bin/runpsql b/pgaas/src/stage/opt/app/pgaas/bin/runpsql new file mode 100755 index 0000000..be7f705 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/runpsql @@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +export PATH=$PATH:/opt/java/jdk/jdk170/bin:/opt/app/cdf/bin:/opt/app/pgaas/bin + +[ -z "$PGUSER" ] && PGUSER=postgres +[ -z "$PGHOST" ] && PGHOST=`hostname -f` +[ -z "$PGDBNM" ] && PGDBNM=postgres +echo "$@;" | +psql --host=$PGHOST --username=$PGUSER --dbname=$PGDBNM + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/runpsqll b/pgaas/src/stage/opt/app/pgaas/bin/runpsqll new file mode 100755 index 0000000..749ae5d --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/runpsqll @@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +export PATH=$PATH:/opt/java/jdk/jdk170/bin:/opt/app/cdf/bin:/opt/app/pgaas/bin + +[ -z "$PGUSER" ] && PGUSER=postgres +[ -z "$PGHOST" ] && PGHOST=`hostname -f` +[ -z "$PGDBNM" ] && PGDBNM=postgres +echo "$@;" | +psql --tuples-only --host=$PGHOST --username=$PGUSER --dbname=$PGDBNM + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/setpropvalue b/pgaas/src/stage/opt/app/pgaas/bin/setpropvalue new file mode 100755 index 0000000..2b45fa8 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/setpropvalue @@ -0,0 +1,67 @@ +#!/bin/bash + +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +die() +{ + exec 1>&2 + echo "$@" + exit 1 +} + +usage() +{ + exec 1>&2 + [ $# -gt 0 ] && echo "$@" + echo "Usage: $0 -n name -v value [-x]" + echo " -n\tname to configure" + echo " -v\tvalue to set" + echo " -x\tencrypt the value" + exit 1 +} + +NAME= +VAL= +ENCRYPT=false +FILE=/opt/app/pgaas/lib/pgaas.cfg + +while getopts f:n:v:x c +do + case "$c" in + f ) FILE=$OPTARG ;; + n ) NAME=$OPTARG ;; + v ) VAL=$OPTARG ;; + x ) ENCRYPT=true ;; + \?) usage ;; + esac +done +shift $(($OPTIND - 1)) + +[ -n "$NAME" ] || die "-n name is required" +[ -n "$VAL" ] || die "-v value is required" +[ -f "$FILE" -a -w "$FILE" ] || die "-f file must exist and be writable" + +ed $FILE <<-! + H + g/^$NAME[=]/d + g/^$NAME[.]x=/d + w + q +! + +if $ENCRYPT +then /opt/app/cdf/bin/getpropvalue -e AES -n $NAME -v $VAL >> $FILE +else echo "$NAME='$VAL'" >> $FILE +fi diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_is_in_recovery b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_is_in_recovery new file mode 100755 index 0000000..b26904d --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_is_in_recovery @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsqll "select * from pg_is_in_recovery()" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_activity b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_activity new file mode 100755 index 0000000..d9f43c8 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_activity @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_stat_activity" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_archiver b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_archiver new file mode 100755 index 0000000..fac0bdb --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_archiver @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_stat_archiver" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_bgwriter b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_bgwriter new file mode 100755 index 0000000..cf6d01a --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_bgwriter @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_stat_bgwriter" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_database b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_database new file mode 100755 index 0000000..c74316e --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_database @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_stat_database" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_database_conflicts b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_database_conflicts new file mode 100755 index 0000000..73d47bd --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_database_conflicts @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_stat_database_conflicts" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_user_indexes b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_user_indexes new file mode 100755 index 0000000..9ff29d9 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_user_indexes @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_stat_user_indexes" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_user_tables b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_user_tables new file mode 100755 index 0000000..5eb1393 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_stat_user_tables @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_stat_user_tables" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_functions b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_functions new file mode 100755 index 0000000..dfaf570 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_functions @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_statio_user_functions" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_indexes b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_indexes new file mode 100755 index 0000000..6ec3043 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_indexes @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_statio_user_indexes" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_sequences b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_sequences new file mode 100755 index 0000000..9d658e4 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_sequences @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_statio_user_sequences" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_tables b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_tables new file mode 100755 index 0000000..4017988 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/show_pg_statio_user_tables @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +export PATH=/opt/app/pgaas/bin:$PATH + +runpsql "select * from pg_statio_user_tables" + diff --git a/pgaas/src/stage/opt/app/pgaas/bin/startpsql b/pgaas/src/stage/opt/app/pgaas/bin/startpsql new file mode 100644 index 0000000..5368fc8 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/startpsql @@ -0,0 +1,29 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +export PATH=$PATH:/opt/java/jdk/jdk170/bin:/opt/app/cdf/bin:/opt/app/pgaas/bin + +[ -z "$PGUSER" ] && PGUSER=postgres +[ -z "$PGHOST" ] && PGHOST=`hostname -f` +[ -z "$PGDBNM" ] && PGDBNM=postgres + +psql --host=$PGHOST --username=$PGUSER --dbname=$PGDBNM diff --git a/pgaas/src/stage/opt/app/pgaas/bin/update_var_run_isrw b/pgaas/src/stage/opt/app/pgaas/bin/update_var_run_isrw new file mode 100755 index 0000000..082c094 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/update_var_run_isrw @@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /usr/lib/postgresql/9.5/bin ] +then PATH=$PATH:/usr/lib/postgresql/9.5/bin +elif [ -d /opt/app/postgresql-9.5.2/bin ] +then PATH=$PATH:/opt/app/postgresql-9.5.2/bin +else echo "$0: Cannot find PostgreSQL bin" 1>&2; exit 1 +fi + +export PATH=$PATH:/opt/java/jdk/jdk170/bin:/opt/app/cdf/bin:/opt/app/pgaas/bin + +for i in 1 2 3 4 5 6 +do + isrw > /var/run/postgresql/isrw.tmp 2>&1 && + mv /var/run/postgresql/isrw.tmp /var/run/postgresql/isrw + sleep 10 +done diff --git a/pgaas/src/stage/opt/app/pgaas/bin/verify_pg_privileges b/pgaas/src/stage/opt/app/pgaas/bin/verify_pg_privileges new file mode 100755 index 0000000..628b63d --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/bin/verify_pg_privileges @@ -0,0 +1,188 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +defdbname="test" +defprefix="tst" +dbname="$defdbname" +prefix="$defprefix" + +usage() +{ + exec 1>&2 + [ -n $# ] && echo " $@" + echo "Usage: $0 -A pswd -U pswd -V pswd [-d dbname] [-p prefix] [-P] [-v]" + echo " -d dbname - database name, defaults to '$defdbname'" + echo " -p prefix - prefix of usernames, defaults to '$defprefix'" + echo " -A pswd - password for admin role" + echo " -U pswd - password for user role" + echo " -V pswd - password for viewer role" + echo " -v verbose" + echo " -P do not print VERIFIED" + exit 1 +} + +PRINTVERIFIED=: +VERBOSE=false +PSWDA= +PSWDU= +PSWDV= +while getopts A:d:p:PU:vV: c +do + case $c in + A ) PSWDA="$OPTARG" ;; + d ) dbname="$OPTARG" ;; + p ) prefix="$OPTARG" ;; + P ) PRINTVERIFIED=false ;; + U ) PSWDU="$OPTARG" ;; + v ) VERBOSE=: ;; + V ) PSWDV="$OPTARG" ;; + '?' ) usage ;; + esac +done +shift `expr $OPTIND - 1` + +[ -n "$PSWDA" ] || usage "-A is missing" +[ -n "$PSWDU" ] || usage "-U is missing" +[ -n "$PSWDV" ] || usage "-V is missing" + +admin="${prefix}_admin" +user="${prefix}_user" +viewer="${prefix}_viewer" + +TMP1=$(mktemp /tmp/vpp1.$$.XXXXXXXXXX) +TMP2=$(mktemp /tmp/vpp2.$$.XXXXXXXXXX) +trap 'rm -f $TMP1 $TMP2' 0 1 2 3 15 + +VERIFIEDCOUNT=0 +FAILEDCOUNT=0 +TOTALCOUNT=0 + +verified() +{ + (( VERIFIEDCOUNT = VERIFIEDCOUNT + 1 )) + (( TOTALCOUNT = TOTALCOUNT + 1 )) + $PRINTVERIFIED && echo "VERIFIED: $@" +} + +failed() +{ + (( FAILEDCOUNT = FAILEDCOUNT + 1 )) + (( TOTALCOUNT = TOTALCOUNT + 1 )) + echo "FAILED: $@" +} + +tabtext() +{ + echo "$@" | sed 's/^/ /' +} + +tabfile() +{ + sed 's/^/ /' "$@" +} + +runtests() +{ + name="$1" + pswd="$2" + while read cmd; read expected + do + echo "$cmd" | psql --host=localhost --dbname="$dbname" --username="$name" > $TMP1 2>&1 + if fgrep "$expected" $TMP1 > /dev/null + then verified "user $name executed $cmd, expected $expected" + $VERBOSE && echo -e "\tGot:" && tabfile "$TMP1" + else failed "user $name executed $cmd, expected $expected, got:"; tabfile $TMP1 + fi + done +} + +psql --host=localhost --dbname="$dbname" --username="$admin" <<EOF > /dev/null + drop table if exists foo; + drop table if exists foou; + drop table if exists fooud; + drop table if exists foouc; + drop table if exists foov; + drop table if exists foovd; + drop table if exists foovc; +EOF + +# tests to be run as admin user +cat <<-EOF > $TMP2 + create table foo ( fooint int ); /* table for admin to add and drop */ + CREATE TABLE + insert into foo (fooint) values (1); /* admin can add values */ + INSERT 0 1 + select count(*) from foo; /* admin can select */ + (1 row) + delete from foo where fooint = '1'; /* admin can delete */ + DELETE 1 + select * from foo; /* admin can select */ + (0 rows) + drop table foo; /* admin can drop */ + DROP TABLE + create table foou (fooint int ); /* table for user to add/delete to */ + CREATE TABLE + create table fooud (fooint int ); /* table for user to try dropping */ + CREATE TABLE + create table foov (fooint int ); /* table for viewer to try adding/deleting from */ + CREATE TABLE + create table foovd (fooint int ); /* table for viewer to try dropping */ + CREATE TABLE +EOF +runtests "$admin" "$PWDA" < $TMP2 + +# tests to be run as writer +cat <<-EOF > $TMP2 + create table foouc ( fooint int ); /* user cannot create a table */ + ERROR: + drop table fooud; /* user cannot drop a table */ + ERROR: + insert into foou (fooint) values (1); /* user can add values */ + INSERT 0 1 + select count(*) from foou; /* user can select values */ + (1 row) + insert into foou (fooint) values (2); /* user can add values */ + INSERT 0 1 + select * from foou; /* user can select values */ + (2 rows) + delete from foou where fooint = '2'; /* user can delete values */ + DELETE 1 + select * from foou; /* user can select values */ + (1 row) +EOF +runtests "$user" "$PWDU" < $TMP2 + +# tests to be run as read-only +cat <<-EOF > $TMP2 + create table foovc ( fooint int ); /* user cannot create a table */ + ERROR: + drop table foovd; /* user cannot drop a table */ + ERROR: + insert into foov (fooint) values (1); /* user can add values */ + ERROR: + select count(*) from foov; /* user can select values */ + (1 row) + insert into foov (fooint) values (2); /* user can add values */ + ERROR: + select * from foov; /* user can select values */ + (0 rows) + delete from foov where fooint = '2'; /* user can delete values */ + ERROR: + select * from foov; /* user can select values */ + (0 rows) +EOF +runtests "$viewer" "$PWDV" < $TMP2 + +echo "$VERIFIEDCOUNT tests passed, $FAILEDCOUNT tests failed, $TOTALCOUNT total tests run" diff --git a/pgaas/src/stage/opt/app/pgaas/etc/create_dcae_rotate.sql b/pgaas/src/stage/opt/app/pgaas/etc/create_dcae_rotate.sql new file mode 100644 index 0000000..2200362 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/etc/create_dcae_rotate.sql @@ -0,0 +1,21 @@ +/* + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ +create table dcae_rotate ( + basetablename varchar(129) not null, /* the base table name to derive other tables from */ + columnname varchar(64) not null, /* which column name to use as the datestamp */ + count int, /* how many periods to keep around */ + period varchar(20) /* one of 'week', 'month' or 'day' */ +); diff --git a/pgaas/src/stage/opt/app/pgaas/etc/dcae_admin_db.cfg b/pgaas/src/stage/opt/app/pgaas/etc/dcae_admin_db.cfg new file mode 100644 index 0000000..139597f --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/etc/dcae_admin_db.cfg @@ -0,0 +1,2 @@ + + diff --git a/pgaas/src/stage/opt/app/pgaas/etc/makefile b/pgaas/src/stage/opt/app/pgaas/etc/makefile new file mode 100644 index 0000000..b997f14 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/etc/makefile @@ -0,0 +1,25 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +all: + +STAGEDIR=/dev/null +DISTPATH=/opt/app/pgaas + +CFGFILES= create_dcae_rotate.sql + +stage: + rm -rf $(STAGEDIR)/$(DISTPATH)/etc + mkdir -p $(STAGEDIR)/$(DISTPATH)/etc + cp $(CFGFILES) $(STAGEDIR)/$(DISTPATH)/etc diff --git a/pgaas/src/stage/opt/app/pgaas/lib/CommonLogger.py b/pgaas/src/stage/opt/app/pgaas/lib/CommonLogger.py new file mode 100644 index 0000000..d18fc7d --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/lib/CommonLogger.py @@ -0,0 +1,889 @@ +#!/usr/bin/python +# -*- indent-tabs-mode: nil -*- vi: set expandtab: +"""ECOMP Common Logging library in Python. + +CommonLogger.py + +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +Original Written by: Terry Schmalzried +Date written: October 1, 2015 +Last updated: December 1, 2016 + +version 0.8 +""" + +from __future__ import print_function +import os, sys, getopt, logging, logging.handlers, time, re, uuid, socket, threading + +class CommonLogger: + """ECOMP Common Logging object. + + Public methods: + __init__ + setFields + debug + info + warn + error + fatal + """ + + UnknownFile = -1 + ErrorFile = 0 + DebugFile = 1 + AuditFile = 2 + MetricsFile = 3 + DateFmt = '%Y-%m-%dT%H:%M:%S' + + def __init__(self, configFile, logKey, **kwargs): + """Construct a Common Logger for one Log File. + + Arguments: + configFile -- configuration filename. + logKey -- the keyword in configFile that identifies the log filename. + + Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error + style -- the log file format (style) to use when writing log messages, + one of CommonLogger.ErrorFile, CommonLogger.DebugFile, + CommonLogger.AuditFile and CommonLogger.MetricsFile, or + one of the strings "error", "debug", "audit" or "metrics". + May also be set in the config file using a field named + <logKey>Style (where <logKey> is the value of the logKey + parameter). The keyword value overrides the value in the + config file. + requestID (dame) -- optional default value for this log record field. + serviceInstanceID (am) -- optional default value for this log record field. + threadID (am) -- optional default value for this log record field. + serverName (am) -- optional default value for this log record field. + serviceName (am) -- optional default value for this log record field. + instanceUUID (am) -- optional default value for this log record field. + severity (am) -- optional default value for this log record field. + serverIPAddress (am) -- optional default value for this log record field. + server (am) -- optional default value for this log record field. + IPAddress (am) -- optional default value for this log record field. + className (am) -- optional default value for this log record field. + timer (am) -- (ElapsedTime) optional default value for this log record field. + partnerName (ame) -- optional default value for this log record field. + targetEntity (me) -- optional default value for this log record field. + targetServiceName (me) -- optional default value for this log record field. + statusCode (am) -- optional default value for this log record field. + responseCode (am) -- optional default value for this log record field. + responseDescription (am) -- optional default value for this log record field. + processKey (am) -- optional default value for this log record field. + targetVirtualEntity (m) -- optional default value for this log record field. + customField1 (am) -- optional default value for this log record field. + customField2 (am) -- optional default value for this log record field. + customField3 (am) -- optional default value for this log record field. + customField4 (am) -- optional default value for this log record field. + errorCategory (e) -- optional default value for this log record field. + errorCode (e) -- optional default value for this log record field. + errorDescription (e) -- optional default value for this log record field. + + Note: the pipe '|' character is not allowed in any log record field. + """ + + self._monitorFlag = False + + # Get configuration parameters + self._logKey = str(logKey) + self._configFile = str(configFile) + self._rotateMethod = 'time' + self._timeRotateIntervalType = 'midnight' + self._timeRotateInterval = 1 + self._sizeMaxBytes = 0 + self._sizeRotateMode = 'a' + self._socketHost = None + self._socketPort = 0 + self._typeLogger = 'filelogger' + self._backupCount = 6 + self._logLevelThreshold = self._intLogLevel('') + self._logFile = None + self._begTime = None + self._begMsec = 0 + self._fields = {} + self._fields["style"] = CommonLogger.UnknownFile + try: + self._configFileModified = os.path.getmtime(self._configFile) + for line in open(self._configFile): + line = line.split('#',1)[0] # remove comments + if '=' in line: + key, value = [x.strip() for x in line.split('=',1)] + if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none']: + self._rotateMethod = value.lower() + elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']: + self._timeRotateIntervalType = value + elif key == 'timeRotateInterval' and int( value ) > 0: + self._timeRotateInterval = int( value ) + elif key == 'sizeMaxBytes' and int( value ) >= 0: + self._sizeMaxBytes = int( value ) + elif key == 'sizeRotateMode' and value in ['a']: + self._sizeRotateMode = value + elif key == 'backupCount' and int( value ) >= 0: + self._backupCount = int( value ) + elif key == self._logKey + 'SocketHost': + self._socketHost = value + elif key == self._logKey + 'SocketPort' and int( value ) == 0: + self._socketPort = int( value ) + elif key == self._logKey + 'LogType' and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']: + self._typeLogger = value.lower() + elif key == self._logKey + 'LogLevel': + self._logLevelThreshold = self._intLogLevel(value.upper()) + elif key == self._logKey + 'Style': + self._fields["style"] = value + elif key == self._logKey: + self._logFile = value + except Exception as x: + print("exception reading '%s' configuration file: %s" %(self._configFile, str(x)), file=sys.stderr) + sys.exit(2) + except: + print("exception reading '%s' configuration file" %(self._configFile), file=sys.stderr) + sys.exit(2) + + if self._logFile is None: + print('configuration file %s is missing definition %s for log file' %(self._configFile, self._logKey), file=sys.stderr) + sys.exit(2) + + + # initialize default log fields + # timestamp will automatically be generated + for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \ + 'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \ + 'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \ + 'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \ + 'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \ + 'errorDescription' ]: + if key in kwargs and kwargs[key] != None: + self._fields[key] = kwargs[key] + + self._resetStyleField() + + # Set up logger + self._logLock = threading.Lock() + with self._logLock: + self._logger = logging.getLogger(self._logKey) + self._logger.propagate = False + self._createLogger() + + self._defaultServerInfo() + + # spawn a thread to monitor configFile for logLevel and logFile changes + self._monitorFlag = True + self._monitorThread = threading.Thread(target=self._monitorConfigFile, args=()) + self._monitorThread.daemon = True + self._monitorThread.start() + + + def _createLogger(self): + if self._typeLogger == 'filelogger': + self._mkdir_p(self._logFile) + if self._rotateMethod == 'time': + self._logHandler = logging.handlers.TimedRotatingFileHandler(self._logFile, \ + when=self._timeRotateIntervalType, interval=self._timeRotateInterval, \ + backupCount=self._backupCount, encoding=None, delay=False, utc=True) + elif self._rotateMethod == 'size': + self._logHandler = logging.handlers.RotatingFileHandler(self._logFile, \ + mode=self._sizeRotateMode, maxBytes=self._sizeMaxBytes, \ + backupCount=self._backupCount, encoding=None, delay=False) + + else: + self._logHandler = logging.handlers.WatchedFileHandler(self._logFile, \ + mode=self._sizeRotateMode, \ + encoding=None, delay=False) + elif self._typeLogger == 'stderrlogger': + self._logHandler = logging.handlers.StreamHandler(sys.stderr) + elif self._typeLogger == 'stdoutlogger': + self._logHandler = logging.handlers.StreamHandler(sys.stdout) + elif self._typeLogger == 'socketlogger': + self._logHandler = logging.handlers.SocketHandler(self._socketHost, self._socketPort) + elif self._typeLogger == 'nulllogger': + self._logHandler = logging.handlers.NullHandler() + + if self._fields["style"] == CommonLogger.AuditFile or self._fields["style"] == CommonLogger.MetricsFile: + self._logFormatter = logging.Formatter(fmt='%(begtime)s,%(begmsecs)03d+00:00|%(endtime)s,%(endmsecs)03d+00:00|%(message)s', datefmt=CommonLogger.DateFmt) + else: + self._logFormatter = logging.Formatter(fmt='%(asctime)s,%(msecs)03d+00:00|%(message)s', datefmt='%Y-%m-%dT%H:%M:%S') + self._logFormatter.converter = time.gmtime + self._logHandler.setFormatter(self._logFormatter) + self._logger.addHandler(self._logHandler) + + def _resetStyleField(self): + styleFields = ["error", "debug", "audit", "metrics"] + if self._fields['style'] in styleFields: + self._fields['style'] = styleFields.index(self._fields['style']) + + def __del__(self): + if self._monitorFlag == False: + return + + self._monitorFlag = False + + if self._monitorThread is not None and self._monitorThread.is_alive(): + self._monitorThread.join() + + self._monitorThread = None + + + def _defaultServerInfo(self): + + # If not set or purposely set = None, then set default + if self._fields.get('server') is None: + try: + self._fields['server'] = socket.getfqdn() + except Exception as err: + try: + self._fields['server'] = socket.gethostname() + except Exception as err: + self._fields['server'] = "" + + # If not set or purposely set = None, then set default + if self._fields.get('serverIPAddress') is None: + try: + self._fields['serverIPAddress'] = socket.gethostbyname(self._fields['server']) + except Exception as err: + self._fields['serverIPAddress'] = "" + + + def _monitorConfigFile(self): + while self._monitorFlag: + try: + fileTime = os.path.getmtime(self._configFile) + if fileTime > self._configFileModified: + self._configFileModified = fileTime + ReopenLogFile = False + logFile = self._logFile + with open(self._configFile) as fp: + for line in fp: + line = line.split('#',1)[0] # remove comments + if '=' in line: + key, value = [x.strip() for x in line.split('=',1)] + if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none'] and self._rotateMethod != value: + self._rotateMethod = value.lower() + ReopenLogFile = True + elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']: + self._timeRotateIntervalType = value + ReopenLogFile = True + elif key == 'timeRotateInterval' and int( value ) > 0: + self._timeRotateInterval = int( value ) + ReopenLogFile = True + elif key == 'sizeMaxBytes' and int( value ) >= 0: + self._sizeMaxBytes = int( value ) + ReopenLogFile = True + elif key == 'sizeRotateMode' and value in ['a']: + self._sizeRotateMode = value + ReopenLogFile = True + elif key == 'backupCount' and int( value ) >= 0: + self._backupCount = int( value ) + ReopenLogFile = True + elif key == self._logKey + 'SocketHost' and self._socketHost != value: + self._socketHost = value + ReopenLogFile = True + elif key == self._logKey + 'SocketPort' and self._socketPort > 0 and self._socketPort != int( value ): + self._socketPort = int( value ) + ReopenLogFile = True + elif key == self._logKey + 'LogLevel' and self._logLevelThreshold != self._intLogLevel( value.upper() ): + self._logLevelThreshold = self._intLogLevel(value.upper()) + elif key == self._logKey + 'LogType' and self._typeLogger != value and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']: + self._typeLogger = value.lower() + ReopenLogFile = True + elif key == self._logKey + 'Style': + self._fields["style"] = value + self._resetStyleField() + elif key == self._logKey and self._logFile != value: + logFile = value + ReopenLogFile = True + if ReopenLogFile: + with self._logLock: + self._logger.removeHandler(self._logHandler) + self._logFile = logFile + self._createLogger() + except Exception as err: + pass + + time.sleep(5) + + + def setFields(self, **kwargs): + """Set default values for log fields. + + Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error + style -- the log file format (style) to use when writing log messages + requestID (dame) -- optional default value for this log record field. + serviceInstanceID (am) -- optional default value for this log record field. + threadID (am) -- optional default value for this log record field. + serverName (am) -- optional default value for this log record field. + serviceName (am) -- optional default value for this log record field. + instanceUUID (am) -- optional default value for this log record field. + severity (am) -- optional default value for this log record field. + serverIPAddress (am) -- optional default value for this log record field. + server (am) -- optional default value for this log record field. + IPAddress (am) -- optional default value for this log record field. + className (am) -- optional default value for this log record field. + timer (am) -- (ElapsedTime) optional default value for this log record field. + partnerName (ame) -- optional default value for this log record field. + targetEntity (me) -- optional default value for this log record field. + targetServiceName (me) -- optional default value for this log record field. + statusCode (am) -- optional default value for this log record field. + responseCode (am) -- optional default value for this log record field. + responseDescription (am) -- optional default value for this log record field. + processKey (am) -- optional default value for this log record field. + targetVirtualEntity (m) -- optional default value for this log record field. + customField1 (am) -- optional default value for this log record field. + customField2 (am) -- optional default value for this log record field. + customField3 (am) -- optional default value for this log record field. + customField4 (am) -- optional default value for this log record field. + errorCategory (e) -- optional default value for this log record field. + errorCode (e) -- optional default value for this log record field. + errorDescription (e) -- optional default value for this log record field. + + Note: the pipe '|' character is not allowed in any log record field. + """ + + for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \ + 'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \ + 'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \ + 'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \ + 'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \ + 'errorDescription' ]: + if key in kwargs: + if kwargs[key] != None: + self._fields[key] = kwargs[key] + elif key in self._fields: + del self._fields[key] + + self._defaultServerInfo() + + + def debug(self, message, **kwargs): + """Write a DEBUG level message to the log file. + + Arguments: + message -- value for the last log record field. + + Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error + style -- the log file format (style) to use when writing log messages + requestID (dame) -- optional default value for this log record field. + serviceInstanceID (am) -- optional default value for this log record field. + threadID (am) -- optional default value for this log record field. + serverName (am) -- optional default value for this log record field. + serviceName (am) -- optional default value for this log record field. + instanceUUID (am) -- optional default value for this log record field. + severity (am) -- optional default value for this log record field. + serverIPAddress (am) -- optional default value for this log record field. + server (am) -- optional default value for this log record field. + IPAddress (am) -- optional default value for this log record field. + className (am) -- optional default value for this log record field. + timer (am) -- (ElapsedTime) optional default value for this log record field. + partnerName (ame) -- optional default value for this log record field. + targetEntity (me) -- optional default value for this log record field. + targetServiceName (me) -- optional default value for this log record field. + statusCode (am) -- optional default value for this log record field. + responseCode (am) -- optional default value for this log record field. + responseDescription (am) -- optional default value for this log record field. + processKey (am) -- optional default value for this log record field. + targetVirtualEntity (m) -- optional default value for this log record field. + customField1 (am) -- optional default value for this log record field. + customField2 (am) -- optional default value for this log record field. + customField3 (am) -- optional default value for this log record field. + customField4 (am) -- optional default value for this log record field. + errorCategory (e) -- optional default value for this log record field. + errorCode (e) -- optional default value for this log record field. + errorDescription (e) -- optional default value for this log record field. + + Note: the pipe '|' character is not allowed in any log record field. + """ + + self._log('DEBUG', message, **kwargs) + + def info(self, message, **kwargs): + """Write an INFO level message to the log file. + + Arguments: + message -- value for the last log record field. + + Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error + style -- the log file format (style) to use when writing log messages + requestID (dame) -- optional default value for this log record field. + serviceInstanceID (am) -- optional default value for this log record field. + threadID (am) -- optional default value for this log record field. + serverName (am) -- optional default value for this log record field. + serviceName (am) -- optional default value for this log record field. + instanceUUID (am) -- optional default value for this log record field. + severity (am) -- optional default value for this log record field. + serverIPAddress (am) -- optional default value for this log record field. + server (am) -- optional default value for this log record field. + IPAddress (am) -- optional default value for this log record field. + className (am) -- optional default value for this log record field. + timer (am) -- (ElapsedTime) optional default value for this log record field. + partnerName (ame) -- optional default value for this log record field. + targetEntity (me) -- optional default value for this log record field. + targetServiceName (me) -- optional default value for this log record field. + statusCode (am) -- optional default value for this log record field. + responseCode (am) -- optional default value for this log record field. + responseDescription (am) -- optional default value for this log record field. + processKey (am) -- optional default value for this log record field. + targetVirtualEntity (m) -- optional default value for this log record field. + customField1 (am) -- optional default value for this log record field. + customField2 (am) -- optional default value for this log record field. + customField3 (am) -- optional default value for this log record field. + customField4 (am) -- optional default value for this log record field. + errorCategory (e) -- optional default value for this log record field. + errorCode (e) -- optional default value for this log record field. + errorDescription (e) -- optional default value for this log record field. + + Note: the pipe '|' character is not allowed in any log record field. + """ + + self._log('INFO', message, **kwargs) + + def warn(self, message, **kwargs): + """Write a WARN level message to the log file. + + Arguments: + message -- value for the last log record field. + + Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error + style -- the log file format (style) to use when writing log messages + requestID (dame) -- optional default value for this log record field. + serviceInstanceID (am) -- optional default value for this log record field. + threadID (am) -- optional default value for this log record field. + serverName (am) -- optional default value for this log record field. + serviceName (am) -- optional default value for this log record field. + instanceUUID (am) -- optional default value for this log record field. + severity (am) -- optional default value for this log record field. + serverIPAddress (am) -- optional default value for this log record field. + server (am) -- optional default value for this log record field. + IPAddress (am) -- optional default value for this log record field. + className (am) -- optional default value for this log record field. + timer (am) -- (ElapsedTime) optional default value for this log record field. + partnerName (ame) -- optional default value for this log record field. + targetEntity (me) -- optional default value for this log record field. + targetServiceName (me) -- optional default value for this log record field. + statusCode (am) -- optional default value for this log record field. + responseCode (am) -- optional default value for this log record field. + responseDescription (am) -- optional default value for this log record field. + processKey (am) -- optional default value for this log record field. + targetVirtualEntity (m) -- optional default value for this log record field. + customField1 (am) -- optional default value for this log record field. + customField2 (am) -- optional default value for this log record field. + customField3 (am) -- optional default value for this log record field. + customField4 (am) -- optional default value for this log record field. + errorCategory (e) -- optional default value for this log record field. + errorCode (e) -- optional default value for this log record field. + errorDescription (e) -- optional default value for this log record field. + + Note: the pipe '|' character is not allowed in any log record field. + """ + + self._log('WARN', message, **kwargs) + + def error(self, message, **kwargs): + """Write an ERROR level message to the log file. + + Arguments: + message -- value for the last log record field. + + Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error + style -- the log file format (style) to use when writing log messages + requestID (dame) -- optional default value for this log record field. + serviceInstanceID (am) -- optional default value for this log record field. + threadID (am) -- optional default value for this log record field. + serverName (am) -- optional default value for this log record field. + serviceName (am) -- optional default value for this log record field. + instanceUUID (am) -- optional default value for this log record field. + severity (am) -- optional default value for this log record field. + serverIPAddress (am) -- optional default value for this log record field. + server (am) -- optional default value for this log record field. + IPAddress (am) -- optional default value for this log record field. + className (am) -- optional default value for this log record field. + timer (am) -- (ElapsedTime) optional default value for this log record field. + partnerName (ame) -- optional default value for this log record field. + targetEntity (me) -- optional default value for this log record field. + targetServiceName (me) -- optional default value for this log record field. + statusCode (am) -- optional default value for this log record field. + responseCode (am) -- optional default value for this log record field. + responseDescription (am) -- optional default value for this log record field. + processKey (am) -- optional default value for this log record field. + targetVirtualEntity (m) -- optional default value for this log record field. + customField1 (am) -- optional default value for this log record field. + customField2 (am) -- optional default value for this log record field. + customField3 (am) -- optional default value for this log record field. + customField4 (am) -- optional default value for this log record field. + errorCategory (e) -- optional default value for this log record field. + errorCode (e) -- optional default value for this log record field. + errorDescription (e) -- optional default value for this log record field. + + Note: the pipe '|' character is not allowed in any log record field. + """ + + self._log('ERROR', message, **kwargs) + + def fatal(self, message, **kwargs): + """Write a FATAL level message to the log file. + + Arguments: + message -- value for the last log record field. + + Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error + style -- the log file format (style) to use when writing log messages + requestID (dame) -- optional default value for this log record field. + serviceInstanceID (am) -- optional default value for this log record field. + threadID (am) -- optional default value for this log record field. + serverName (am) -- optional default value for this log record field. + serviceName (am) -- optional default value for this log record field. + instanceUUID (am) -- optional default value for this log record field. + severity (am) -- optional default value for this log record field. + serverIPAddress (am) -- optional default value for this log record field. + server (am) -- optional default value for this log record field. + IPAddress (am) -- optional default value for this log record field. + className (am) -- optional default value for this log record field. + timer (am) -- (ElapsedTime) optional default value for this log record field. + partnerName (ame) -- optional default value for this log record field. + targetEntity (me) -- optional default value for this log record field. + targetServiceName (me) -- optional default value for this log record field. + statusCode (am) -- optional default value for this log record field. + responseCode (am) -- optional default value for this log record field. + responseDescription (am) -- optional default value for this log record field. + processKey (am) -- optional default value for this log record field. + targetVirtualEntity (m) -- optional default value for this log record field. + customField1 (am) -- optional default value for this log record field. + customField2 (am) -- optional default value for this log record field. + customField3 (am) -- optional default value for this log record field. + customField4 (am) -- optional default value for this log record field. + errorCategory (e) -- optional default value for this log record field. + errorCode (e) -- optional default value for this log record field. + errorDescription (e) -- optional default value for this log record field. + + Note: the pipe '|' character is not allowed in any log record field. + """ + + self._log('FATAL', message, **kwargs) + + def _log(self, logLevel, message, **kwargs): + """Write a message to the log file. + + Arguments: + logLevel -- value ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', ...) for the log record. + message -- value for the last log record field. + + Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error + style -- the log file format (style) to use when writing log messages + requestID (dame) -- optional default value for this log record field. + serviceInstanceID (am) -- optional default value for this log record field. + threadID (am) -- optional default value for this log record field. + serverName (am) -- optional default value for this log record field. + serviceName (am) -- optional default value for this log record field. + instanceUUID (am) -- optional default value for this log record field. + severity (am) -- optional default value for this log record field. + serverIPAddress (am) -- optional default value for this log record field. + server (am) -- optional default value for this log record field. + IPAddress (am) -- optional default value for this log record field. + className (am) -- optional default value for this log record field. + timer (am) -- (ElapsedTime) optional default value for this log record field. + partnerName (ame) -- optional default value for this log record field. + targetEntity (me) -- optional default value for this log record field. + targetServiceName (me) -- optional default value for this log record field. + statusCode (am) -- optional default value for this log record field. + responseCode (am) -- optional default value for this log record field. + responseDescription (am) -- optional default value for this log record field. + processKey (am) -- optional default value for this log record field. + targetVirtualEntity (m) -- optional default value for this log record field. + customField1 (am) -- optional default value for this log record field. + customField2 (am) -- optional default value for this log record field. + customField3 (am) -- optional default value for this log record field. + customField4 (am) -- optional default value for this log record field. + errorCategory (e) -- optional default value for this log record field. + errorCode (e) -- optional default value for this log record field. + errorDescription (e) -- optional default value for this log record field. + + Note: the pipe '|' character is not allowed in any log record field. + """ + + # timestamp will automatically be inserted + style = int(self._getVal('style', '', **kwargs)) + requestID = self._getVal('requestID', '', **kwargs) + serviceInstanceID = self._getVal('serviceInstanceID', '', **kwargs) + threadID = self._getVal('threadID', threading.currentThread().getName(), **kwargs) + serverName = self._getVal('serverName', '', **kwargs) + serviceName = self._getVal('serviceName', '', **kwargs) + instanceUUID = self._getVal('instanceUUID', '', **kwargs) + upperLogLevel = self._noSep(logLevel.upper()) + severity = self._getVal('severity', '', **kwargs) + serverIPAddress = self._getVal('serverIPAddress', '', **kwargs) + server = self._getVal('server', '', **kwargs) + IPAddress = self._getVal('IPAddress', '', **kwargs) + className = self._getVal('className', '', **kwargs) + timer = self._getVal('timer', '', **kwargs) + partnerName = self._getVal('partnerName', '', **kwargs) + targetEntity = self._getVal('targetEntity', '', **kwargs) + targetServiceName = self._getVal('targetServiceName', '', **kwargs) + statusCode = self._getVal('statusCode', '', **kwargs) + responseCode = self._getVal('responseCode', '', **kwargs) + responseDescription = self._noSep(self._getVal('responseDescription', '', **kwargs)) + processKey = self._getVal('processKey', '', **kwargs) + targetVirtualEntity = self._getVal('targetVirtualEntity', '', **kwargs) + customField1 = self._getVal('customField1', '', **kwargs) + customField2 = self._getVal('customField2', '', **kwargs) + customField3 = self._getVal('customField3', '', **kwargs) + customField4 = self._getVal('customField4', '', **kwargs) + errorCategory = self._getVal('errorCategory', '', **kwargs) + errorCode = self._getVal('errorCode', '', **kwargs) + errorDescription = self._noSep(self._getVal('errorDescription', '', **kwargs)) + + detailMessage = self._noSep(message) + if bool(re.match(r" *$", detailMessage)): + return # don't log empty messages + + useLevel = self._intLogLevel(upperLogLevel) + if useLevel >= self._logLevelThreshold: + with self._logLock: + if style == CommonLogger.ErrorFile: + self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \ + %(requestID, threadID, serviceName, partnerName, targetEntity, targetServiceName, + errorCategory, errorCode, errorDescription, detailMessage)) + elif style == CommonLogger.DebugFile: + self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \ + %(requestID, threadID, serverName, serviceName, instanceUUID, upperLogLevel, + severity, serverIPAddress, server, IPAddress, className, timer, detailMessage)) + elif style == CommonLogger.AuditFile: + endAuditTime, endAuditMsec = self._getTime() + if self._begTime is not None: + d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec } + else: + d = { 'begtime': endAuditTime, 'begmsecs': endAuditMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec } + self._begTime = None + unused = "" + self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \ + %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName, + statusCode, responseCode, responseDescription, instanceUUID, upperLogLevel, + severity, serverIPAddress, timer, server, IPAddress, className, unused, + processKey, customField1, customField2, customField3, customField4, detailMessage), extra=d) + elif style == CommonLogger.MetricsFile: + endMetricsTime, endMetricsMsec = self._getTime() + if self._begTime is not None: + d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec } + else: + d = { 'begtime': endMetricsTime, 'begmsecs': endMetricsMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec } + self._begTime = None + unused = "" + self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \ + %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName, + targetEntity, targetServiceName, statusCode, responseCode, responseDescription, + instanceUUID, upperLogLevel, severity, serverIPAddress, timer, server, IPAddress, + className, unused, processKey, targetVirtualEntity, customField1, customField2, + customField3, customField4, detailMessage), extra=d) + else: + print("!!!!!!!!!!!!!!!! style not set: %s" % self._fields["style"]) + + def _getTime(self): + ct = time.time() + lt = time.localtime(ct) + return (time.strftime(CommonLogger.DateFmt, lt), (ct - int(ct)) * 1000) + + def setStartRecordEvent(self): + """ + Set the start time to be saved for both audit and metrics records + """ + self._begTime, self._begMsec = self._getTime() + + def _getVal(self, key, default, **kwargs): + val = self._fields.get(key) + if key in kwargs: val = kwargs[key] + if val is None: val = default + return self._noSep(val) + + def _noSep(self, message): + if message is None: return '' + return re.sub(r'[\|\n]', ' ', str(message)) + + def _intLogLevel(self, logLevel): + if logLevel == 'FATAL': useLevel = 50 + elif logLevel == 'ERROR': useLevel = 40 + elif logLevel == 'WARN': useLevel = 30 + elif logLevel == 'INFO': useLevel = 20 + elif logLevel == 'DEBUG': useLevel = 10 + else: useLevel = 0 + return useLevel + + def _mkdir_p(self, filename): + """Create missing directories from a full filename path like mkdir -p""" + + if filename is None: + return + + folder=os.path.dirname(filename) + + if folder == "": + return + + if not os.path.exists(folder): + try: + os.makedirs(folder) + except OSError as err: + print("error number %d creating %s directory to hold %s logfile: %s" %(err.errno, err.filename, filename, err.strerror), file=sys.stderr) + sys.exit(2) + except Exception as err: + print("error creating %s directory to hold %s logfile: %s" %(folder, filename, str(err)), file=sys.stderr) + sys.exit(2) + +def __checkTime1(line): + format = r'[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9][+]00:00[|]' + format = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[+]00:00[|]' + m = re.match(format, line) + if not m: + print("ERROR: time string did not match proper time format, %s" %line) + print("\t: format=%s" % format) + return 1 + return 0 + +def __checkTime2(line, different): + format = '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:([0-9][0-9]),([0-9][0-9][0-9])[+]00:00[|][0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:([0-9][0-9]),([0-9][0-9][0-9])[+]00:00[|]' + format = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:([0-9]{2}),([0-9]{3})[+]00:00[|][0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:([0-9]{2}),([0-9]{3})[+]00:00[|]' + m = re.match(format, line) + if not m: + print("ERROR: time strings did not match proper time format, %s" %line) + print("\t: format=%s" % format) + return 1 + second1 = int(m.group(1)) + msec1 = int(m.group(2)) + second2 = int(m.group(3)) + msec2 = int(m.group(4)) + if second1 > second2: second2 += 60 + t1 = second1 * 1000 + msec1 + t2 = second2 * 1000 + msec2 + diff = t2 - t1 + # print("t1=%d (%d,%d) t2=%d (%d,%d), diff = %d" % (t1, second1, msec1, t2, second2, msec2, diff)) + if different: + if diff < 500: + print("ERROR: times did not differ enough: %s" % line) + return 1 + else: + if diff > 10: + print("ERROR: times were too far apart: %s" % line) + return 1 + return 0 + +def __checkLog(logfile, numLines, numFields): + lineCount = 0 + errorCount = 0 + with open(logfile, "r") as fp: + for line in fp: + # print("saw line %s" % line) + lineCount += 1 + c = line.count('|') + if c != numFields: + print("ERROR: wrong number of fields. Expected %d, got %d: %s" % (numFields, c, line)) + errorCount += 1 + if re.search("should not appear", line): + print("ERROR: a line appeared that should not have appeared, %s" % line) + errorCount += 1 + elif re.search("single time", line): + errorCount += __checkTime1(line) + elif re.search("time should be the same", line): + errorCount += __checkTime2(line, different=False) + elif re.search("time should be different", line): + errorCount += __checkTime2(line, different=True) + else: + print("ERROR: an unknown message appeared, %s" % line) + errorCount += 1 + + if lineCount != numLines: + print("ERROR: expected %d lines, but got %d lines" % (numLines, lineCount)) + errorCount += 1 + return errorCount + +if __name__ == "__main__": + import os + keepLogs = False + spid = str(os.getpid()) + if keepLogs: + spid = "" + logcfg = "/tmp/log" + spid + ".cfg" + errorLog = "/tmp/error" + spid + ".log" + metricsLog = "/tmp/metrics" + spid + ".log" + auditLog = "/tmp/audit" + spid + ".log" + debugLog = "/tmp/debug" + spid + ".log" + + import atexit + def cleanupTmps(): + for f in [ logcfg, errorLog, metricsLog, auditLog, debugLog ]: + try: + os.remove(f) + except: + pass + if not keepLogs: + atexit.register(cleanupTmps) + + with open(logcfg, "w") as o: + o.write("error = " + errorLog + "\n" + + "errorLogLevel = WARN\n" + + "metrics = " + metricsLog + "\n" + + "metricsLogLevel = INFO\n" + + "audit = " + auditLog + "\n" + + "auditLogLevel = INFO\n" + + "debug = " + debugLog + "\n" + + "debugLogLevel = DEBUG\n") + + import uuid + instanceUUID = uuid.uuid1() + serviceName = "testharness" + errorLogger = CommonLogger(logcfg, "error", style=CommonLogger.ErrorFile, instanceUUID=instanceUUID, serviceName=serviceName) + debugLogger = CommonLogger(logcfg, "debug", style=CommonLogger.DebugFile, instanceUUID=instanceUUID, serviceName=serviceName) + auditLogger = CommonLogger(logcfg, "audit", style=CommonLogger.AuditFile, instanceUUID=instanceUUID, serviceName=serviceName) + metricsLogger = CommonLogger(logcfg, "metrics", style=CommonLogger.MetricsFile, instanceUUID=instanceUUID, serviceName=serviceName) + + testsRun = 0 + errorCount = 0 + errorLogger.debug("error calling debug (should not appear)") + errorLogger.info("error calling info (should not appear)") + errorLogger.warn("error calling warn (single time)") + errorLogger.error("error calling error (single time)") + errorLogger.setStartRecordEvent() + time.sleep(1) + errorLogger.fatal("error calling fatal, after setStartRecordEvent and sleep (start should be ignored, single time)") + testsRun += 6 + errorCount += __checkLog(errorLog, 3, 10) + + auditLogger.debug("audit calling debug (should not appear)") + auditLogger.info("audit calling info (time should be the same)") + auditLogger.warn("audit calling warn (time should be the same)") + auditLogger.error("audit calling error (time should be the same)") + auditLogger.setStartRecordEvent() + time.sleep(1) + auditLogger.fatal("audit calling fatal, after setStartRecordEvent and sleep, time should be different)") + testsRun += 6 + errorCount += __checkLog(auditLog, 4, 25) + + debugLogger.debug("debug calling debug (single time)") + debugLogger.info("debug calling info (single time)") + debugLogger.warn("debug calling warn (single time)") + debugLogger.setStartRecordEvent() + time.sleep(1) + debugLogger.error("debug calling error, after SetStartRecordEvent and sleep (start should be ignored, single time)") + debugLogger.fatal("debug calling fatal (single time)") + errorCount += __checkLog(debugLog, 5, 13) + testsRun += 6 + + metricsLogger.debug("metrics calling debug (should not appear)") + metricsLogger.info("metrics calling info (time should be the same)") + metricsLogger.warn("metrics calling warn (time should be the same)") + metricsLogger.setStartRecordEvent() + time.sleep(1) + metricsLogger.error("metrics calling error, after SetStartRecordEvent and sleep, time should be different") + metricsLogger.fatal("metrics calling fatal (time should be the same)") + testsRun += 6 + errorCount += __checkLog(metricsLog, 4, 28) + + print("%d tests run, %d errors found" % (testsRun, errorCount)) diff --git a/pgaas/src/stage/opt/app/pgaas/lib/dcae_admin_db.json b/pgaas/src/stage/opt/app/pgaas/lib/dcae_admin_db.json new file mode 100644 index 0000000..1e869e8 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/lib/dcae_admin_db.json @@ -0,0 +1,42 @@ +{ + "version": "1.0", + + "global": + { + "state": "master", + "sendWalTo": + [ + { + + } + ] + }, + + "databases": + [ + { + "name": "ojcit", + "users": + [ + { + "id": "ojcitadmin", + "password": "bar", + "role": "admin", + "roleComment": "admin|writer|reader" + }, + { + "id": "ojcituser", + "password": "bar", + "role": "writer", + "roleComment": "admin|writer|reader" + }, + { + "id": "ojcitviewer", + "password": "bar", + "role": "reader", + "roleComment": "admin|writer|reader" + } + ] + } + ] +}
\ No newline at end of file diff --git a/pgaas/src/stage/opt/app/pgaas/lib/init-logger.cfg b/pgaas/src/stage/opt/app/pgaas/lib/init-logger.cfg new file mode 100644 index 0000000..0cfae0d --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/lib/init-logger.cfg @@ -0,0 +1,40 @@ +# You may change this file while your program is running and CommonLogger will automatically reconfigure accordingly. +# Changing these parameters may leave old log files lying around. + + +#--- Parameters that apply to all logs +# +# rotateMethod: time or size +#... Note: the following two parameters apply only when rotateMethod=time +# timeRotateIntervalType: S, M, H, D, W0 - W6, or midnight (seconds, minutes, hours, days, weekday (0=Monday), or midnight UTC) +# timeRotateInterval: >= 1 (1 means every timeRotateIntervalType, 2 every other, 3 every third, etc.) +#... Note: the following parameter applies only when rotateMethod=size +# sizeMaxBytes: >= 0 (0 means no limit, else maximum filesize in Bytes) +# backupCount: >= 0 (Number of rotated backup files to retain. If rotateMethod=time, 0 retains *all* backups. If rotateMethod=size, 0 retains *no* backups.) +# +rotateMethod = none +# timeRotateIntervalType = midnight +# timeRotateInterval = 1 +# sizeMaxBytes = 0 +# backupCount = 6 + + +#--- Parameters that define log filenames and their initial LogLevel threshold +#... Note: CommonLogger will exit if your process does not have permission to write to the file. +# + +error = /opt/app/log/postgresql/init/error.log +errorLogLevel = WARN +errorStyle = error + +metrics = /opt/app/log/postgresql/init/metrics.log +metricsLogLevel = INFO +metricsStyle = metrics + +audit = /opt/app/log/postgresql/init/audit.log +auditLogLevel = INFO +auditStyle = audit + +debug = /opt/app/log/postgresql/init/debug.log +debugLogLevel = DEBUG +debugStyle = debug diff --git a/pgaas/src/stage/opt/app/pgaas/lib/makefile b/pgaas/src/stage/opt/app/pgaas/lib/makefile new file mode 100644 index 0000000..a6f3266 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/lib/makefile @@ -0,0 +1,25 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +all: + +STAGEDIR=/dev/null +DISTPATH=/opt/app/pgaas + +CFGFILES= CommonLogger.py pgaas.cfg init-logger.cfg + +stage: + rm -rf $(STAGEDIR)/$(DISTPATH)/lib + mkdir -p $(STAGEDIR)/$(DISTPATH)/lib + cp $(CFGFILES) $(STAGEDIR)/$(DISTPATH)/lib diff --git a/pgaas/src/stage/opt/app/pgaas/lib/pgaas.cfg b/pgaas/src/stage/opt/app/pgaas/lib/pgaas.cfg new file mode 100644 index 0000000..0d4cc04 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/lib/pgaas.cfg @@ -0,0 +1,31 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +#### +#### PostgreSQL as a Service Configuration Parameters +#### + +db_directory=/dbroot/pgdata/main +# db_configuration= +# pg_bin_directory= + +dcae_admin_db_hostname=localhost +dcae_admin_db_databasename=postgres +dcae_admin_db_username=postgres +dcae_admin_db_verbosity=1 +dcae_admin_db_jsontop="['vmConfiguration']" +dcae_admin_db_errors_file=/opt/app/log/postgresql/init/error.log +dcae_admin_db_trace_file=/opt/app/log/postgresql/init/trace.log +dcae_admin_db_common_logger_config=/opt/app/pgaas/lib/init-logger.cfg +# skip_configuration_file= diff --git a/pgaas/src/stage/opt/app/pgaas/makefile b/pgaas/src/stage/opt/app/pgaas/makefile new file mode 100644 index 0000000..73232db --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/makefile @@ -0,0 +1,31 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +STAGEDIR=/dev/null +all: + +stage: + for i in bin lib etc man; do \ + ( cd $$i && $(MAKE) stage STAGEDIR=../$(STAGEDIR) ) \ + done + +clean: + for i in bin lib etc man; do \ + ( cd $$i && $(MAKE) clean ) \ + done + +clobber: + for i in bin lib etc man; do \ + ( cd $$i && $(MAKE) clobber ) \ + done diff --git a/pgaas/src/stage/opt/app/pgaas/man/.gitignore b/pgaas/src/stage/opt/app/pgaas/man/.gitignore new file mode 100644 index 0000000..f7e585b --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/.gitignore @@ -0,0 +1 @@ +*.1 diff --git a/pgaas/src/stage/opt/app/pgaas/man/check_cluster.man b/pgaas/src/stage/opt/app/pgaas/man/check_cluster.man new file mode 100644 index 0000000..dc6fcce --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/check_cluster.man @@ -0,0 +1,34 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH check_cluster 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +check_cluster - check the state of the cluster +.SH SYNOPSIS +check_cluster [-v] [-l] [-t timeout] +.HP 20 +-l +do not check localhost first (and restarting the service if necessary) +.HP 20 +-t timeout +set how long to wait when accessing the servers +.HP 20 +-v +verbose +.SH DESCRIPTION +Loop through the nodes in the cluster, using pgwget to determine how many are masters, secondaries, or not up. +Complain about certain situations. +If there are multiple masters, and this not the first master in the list, then: + +run pg_ctl_restart + +prevent /ro from returning true diff --git a/pgaas/src/stage/opt/app/pgaas/man/dcae_admin_db.man b/pgaas/src/stage/opt/app/pgaas/man/dcae_admin_db.man new file mode 100644 index 0000000..84b7cbb --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/dcae_admin_db.man @@ -0,0 +1,72 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH dcae_admin_db.py 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +dcae_admin_db.py - interface with DCAE controller +.SH SYNOPSIS +dcae_admin_db.py [options] configurationChanged json-file +.br +dcae_admin_db.py [options] suspend +.br +dcae_admin_db.py [options] resume +.br +dcae_admin_db.py [options] test +.SS OPTIONS +.HP 20 +-H / --dbhost= +host name, defaults to CFG['dcae_admin_db_hostname'] +.HP 20 +-d / --dbdir= +database directory path, defaults to CFG['db_directory'] +.HP 20 +-c / --dbconf= +database configuration path, defaults to CFG['db_configuration'] +.HP 20 +-D / --dbname= +database name, defaults to CFG['dcae_admin_db_databasename'] +.HP 20 +-U / --user= +user to login as, defaults to CFG['dcae_admin_db_username'] +.HP 20 +-P / --password= +password for user, defaults to CFG['dcae_admin_db_password'] +.HP 20 +-B / --bindir= +postgresql bin directory, defaults to CFG['pg_bin_directory'] +.HP 20 +-i / --ignorefile= +skip configuration if this file is present, defaults to CFG['skip_configuration_file'] +.HP 20 +-n / --nocreate +do not create the databases / users +.HP 20 +-I / --ignoredb +ignore current state of database +.HP 20 +-R / --remove +remove old databases / users +.HP 20 +-J / --jsontop= +top of json tree, as in \"['pgaas']\" +.HP 20 +-e / --errors= +where to redirect error output, defaults to CFG['dcae_admin_db_errors_file'] then stderr +.HP 20 +-t / --trace= +where to redirect trace output, defaults to CFG['dcae_admin_db_trace_file'] then stderr +.HP 20 +-v / --verbose +verbose, defaults to CFG['dcae_admin_db_verbosity'] +.SH DESCRIPTION +This program is intended to be executed by the DCAE controller manager. +Given a database description json file, update the current VM accordingly diff --git a/pgaas/src/stage/opt/app/pgaas/man/isrw.man b/pgaas/src/stage/opt/app/pgaas/man/isrw.man new file mode 100644 index 0000000..4c4207d --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/isrw.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH isrw 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +isrw - check if a running PostgreSQL is a master or secondary +.SH SYNOPSIS +isrw +.SH DESCRIPTION +Either prints Master or Secondary depending on the state of the running PostgreSQL server. diff --git a/pgaas/src/stage/opt/app/pgaas/man/list_masters.man b/pgaas/src/stage/opt/app/pgaas/man/list_masters.man new file mode 100644 index 0000000..be5b2bd --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/list_masters.man @@ -0,0 +1,30 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH list_masters 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +list_masters - loop through the nodes in the cluster, using a remote call to determine if any are a master. +.SH SYNOPSIS +list_masters +.SH DESCRIPTION +List_masters loops through the nodes in the cluster, using a remote call to the iDNS-responder running on each server to determine if any are a master. +The names of the masters are printed (there should only be one). +.SS Exit Codes +.HP 20 +0 +one master exists and is running +.HP 20 +1 +no masters exist +.HP 20 +2 +more than one master exist diff --git a/pgaas/src/stage/opt/app/pgaas/man/makefile b/pgaas/src/stage/opt/app/pgaas/man/makefile new file mode 100644 index 0000000..9cd2090 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/makefile @@ -0,0 +1,97 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +.SUFFIXES: .man .1 + +TR2MAN = +%.1 : %.man + $(TR2MAN) + +STAGEDIR=/dev/null +DISTPATH=/opt/app/pgaas + +TRFILES := $(wildcard *.man) +MANFILES := $(patsubst %.man,%.1,$(wildcard *.man)) + +all: $(MANFILES) + +stage: $(MANFILES) + rm -rf $(STAGEDIR)/$(DISTPATH)/man + mkdir -p $(STAGEDIR)/$(DISTPATH)/man + cp -p $(MANFILES) $(STAGEDIR)/$(DISTPATH)/man + +clean: + rm -f *~ + +clobber: clean + rm -f *.1 + +# really should not need to list each of the files individually here with how to make them + +check_cluster.1: check_cluster.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +dcae_admin_db.1: dcae_admin_db.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +isrw.1: isrw.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +list_masters.1: list_masters.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +pg_ctl_promote.1: pg_ctl_promote.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +pg_ctl_restart.1: pg_ctl_restart.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +pg_ctl_start.1: pg_ctl_start.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +pg_ctl_status.1: pg_ctl_status.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +pg_ctl_stop.1: pg_ctl_stop.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +repmgrc.1: repmgrc.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +repmgrdc.1: repmgrdc.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +runpsqll.1: runpsqll.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +runpsql.1: runpsql.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +setpropvalue.1: setpropvalue.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_is_in_recovery.1: show_pg_is_in_recovery.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_stat_activity.1: show_pg_stat_activity.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_stat_archiver.1: show_pg_stat_archiver.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_stat_bgwriter.1: show_pg_stat_bgwriter.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_stat_database_conflicts.1: show_pg_stat_database_conflicts.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_stat_database.1: show_pg_stat_database.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_statio_user_functions.1: show_pg_statio_user_functions.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_statio_user_indexes.1: show_pg_statio_user_indexes.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_statio_user_sequences.1: show_pg_statio_user_sequences.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_statio_user_tables.1: show_pg_statio_user_tables.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_stat_user_indexes.1: show_pg_stat_user_indexes.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +show_pg_stat_user_tables.1: show_pg_stat_user_tables.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +startpsql.1: startpsql.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ +update_var_run_isrw.1: update_var_run_isrw.man + DATE=$$(date +%Y-%m-%d); sed "1s/ {{DATE}} / $$DATE /" < $< | nroff -Tlp -man > $@ diff --git a/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_promote.man b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_promote.man new file mode 100644 index 0000000..21d7b82 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_promote.man @@ -0,0 +1,20 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH pg_ctl_promote 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +pg_ctl_promote - helper function to run pg_ctl promote +.SH SYNOPSIS +pg_ctl_promote +.SH DESCRIPTION +Finds the pg_ctl command, the location of the database and its configurations and runs "pg_ctl promote". + diff --git a/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_restart.man b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_restart.man new file mode 100644 index 0000000..e6bf6c9 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_restart.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH pg_ctl_restart 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +pg_ctl_restart - helper function to run pg_ctl restart +.SH SYNOPSIS +pg_ctl_restart +.SH DESCRIPTION +Finds the pg_ctl command, the location of the database and its configurations and runs "pg_ctl stop" and "pg_ctl start". diff --git a/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_start.man b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_start.man new file mode 100644 index 0000000..81f171d --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_start.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH pg_ctl_start 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +pg_ctl_start - helper function to run pg_ctl start +.SH SYNOPSIS +pg_ctl_start +.SH DESCRIPTION +Finds the pg_ctl command, the location of the database and its configurations and runs "pg_ctl start". diff --git a/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_status.man b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_status.man new file mode 100644 index 0000000..5a6256d --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_status.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH pg_ctl_status 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +pg_ctl_status - helper function to run pg_ctl status +.SH SYNOPSIS +pg_ctl_status +.SH DESCRIPTION +Finds the pg_ctl command, the location of the database and its configurations and runs "pg_ctl status". diff --git a/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_stop.man b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_stop.man new file mode 100644 index 0000000..ba4e71e --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/pg_ctl_stop.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH pg_ctl_stop 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +pg_ctl_stop - helper function to run pg_ctl stop +.SH SYNOPSIS +pg_ctl_stop +.SH DESCRIPTION +Finds the pg_ctl command, the location of the database and its configurations and runs "pg_ctl stop". diff --git a/pgaas/src/stage/opt/app/pgaas/man/repmgrc.man b/pgaas/src/stage/opt/app/pgaas/man/repmgrc.man new file mode 100644 index 0000000..18c8848 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/repmgrc.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH repmgr 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +repmgrc - helper function to run repmgr command +.SH SYNOPSIS +repmgrc +.SH DESCRIPTION +Finds the repmgr command and its configurations and runs "repmgr" appropriately. diff --git a/pgaas/src/stage/opt/app/pgaas/man/repmgrdc.man b/pgaas/src/stage/opt/app/pgaas/man/repmgrdc.man new file mode 100644 index 0000000..9d9617f --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/repmgrdc.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH repmgrd 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +repmgrdc - helper function to run repmgrd command +.SH SYNOPSIS +repmgrdc +.SH DESCRIPTION +Finds the repmgrd command and its configurations and runs "repmgrd" appropriately. diff --git a/pgaas/src/stage/opt/app/pgaas/man/runpsql.man b/pgaas/src/stage/opt/app/pgaas/man/runpsql.man new file mode 100644 index 0000000..1903d7a --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/runpsql.man @@ -0,0 +1,21 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH runpsql 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +runpsql - run the psql command that is given on the command line +.SH SYNOPSIS +runpsql sql command ... +.SH DESCRIPTION +Locate the psql command and run the command specified as the arguments on the command line. +.SH "SEE ALSO" +runpsqll, startpsql diff --git a/pgaas/src/stage/opt/app/pgaas/man/runpsqll.man b/pgaas/src/stage/opt/app/pgaas/man/runpsqll.man new file mode 100644 index 0000000..2098688 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/runpsqll.man @@ -0,0 +1,22 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH runpsqll 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +runpsqll - run the psql command that is given on the command line, no labels +.SH SYNOPSIS +runpsqll sql command ... +.SH DESCRIPTION +Locate the psql command and run the command specified as the arguments on the command line. +Only output the tuples. +.SH "SEE ALSO" +runpsql, startpsql diff --git a/pgaas/src/stage/opt/app/pgaas/man/setpropvalue.man b/pgaas/src/stage/opt/app/pgaas/man/setpropvalue.man new file mode 100644 index 0000000..d56a2e7 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/setpropvalue.man @@ -0,0 +1,26 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH setpropvalue 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +setpropvalue - set a property in the CDF property file +.SH SYNOPSIS +setpropvalue -n name -v value [-x] +.HP 20 +-n +name to configure +.HP 20 +-v +value to set +.HP 20 +-x +encrypt the value diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_is_in_recovery.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_is_in_recovery.man new file mode 100644 index 0000000..a4c9474 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_is_in_recovery.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_is_in_recovery 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_is_in_recovery - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_is_in_recovery +.SH DESCRIPTION +show_pg_is_in_recovery is a convenience program to extract the pg_is_in_recovery information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_activity.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_activity.man new file mode 100644 index 0000000..ef10efc --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_activity.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_stat_activity 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_stat_activity - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_stat_activity +.SH DESCRIPTION +show_pg_stat_activity is a convenience program to extract the pg_stat_activity information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_archiver.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_archiver.man new file mode 100644 index 0000000..f1044e2 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_archiver.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_stat_archiver 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_stat_archiver - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_stat_archiver +.SH DESCRIPTION +show_pg_stat_archiver is a convenience program to extract the pg_stat_archiver information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_bgwriter.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_bgwriter.man new file mode 100644 index 0000000..a05a18a --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_bgwriter.man @@ -0,0 +1,20 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_stat_bgwriter 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_stat_bgwriter - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_stat_bgwriter +.SH DESCRIPTION +show_pg_stat_bgwriter is a convenience program to extract the pg_stat_bgwriter information from the database. + diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_database.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_database.man new file mode 100644 index 0000000..5bbceb5 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_database.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_stat_database 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_stat_database - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_stat_database +.SH DESCRIPTION +show_pg_stat_database is a convenience program to extract the pg_stat_database information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_database_conflicts.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_database_conflicts.man new file mode 100644 index 0000000..d5b9b1f --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_database_conflicts.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_stat_database_conflicts 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_stat_database_conflicts - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_stat_database_conflicts +.SH DESCRIPTION +show_pg_stat_database_conflicts is a convenience program to extract the pg_stat_database_conflicts information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_user_indexes.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_user_indexes.man new file mode 100644 index 0000000..729e018 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_user_indexes.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_stat_user_indexes 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_stat_user_indexes - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_stat_user_indexes +.SH DESCRIPTION +show_pg_stat_user_indexes is a convenience program to extract the pg_stat_user_indexes information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_user_tables.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_user_tables.man new file mode 100644 index 0000000..39fa971 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_stat_user_tables.man @@ -0,0 +1,20 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_stat_user_indexes 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_stat_user_tables - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_stat_user_tables +.SH DESCRIPTION +show_pg_stat_user_tables is a convenience program to extract the pg_stat_user_tables information from the database. + diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_functions.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_functions.man new file mode 100644 index 0000000..171c161 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_functions.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_statio_user_functions 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_statio_user_functions - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_statio_user_functions +.SH DESCRIPTION +show_pg_statio_user_functions is a convenience program to extract the pg_statio_user_functions information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_indexes.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_indexes.man new file mode 100644 index 0000000..f54f0b5 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_indexes.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_statio_user_indexes 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_statio_user_indexes - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_statio_user_indexes +.SH DESCRIPTION +show_pg_statio_user_indexes is a convenience program to extract the pg_statio_user_indexes information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_sequences.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_sequences.man new file mode 100644 index 0000000..cc94f5a --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_sequences.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_statio_user_sequences 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_statio_user_sequences - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_statio_user_sequences +.SH DESCRIPTION +show_pg_statio_user_sequences is a convenience program to extract the pg_statio_user_sequences information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_tables.man b/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_tables.man new file mode 100644 index 0000000..14afc43 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/show_pg_statio_user_tables.man @@ -0,0 +1,19 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH show_pg_statio_user_tables 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +show_pg_statio_user_tables - convenience program to extract info from the database +.SH SYNOPSIS +show_pg_statio_user_tables +.SH DESCRIPTION +show_pg_statio_user_tables is a convenience program to extract the pg_statio_user_tables information from the database. diff --git a/pgaas/src/stage/opt/app/pgaas/man/startpsql.man b/pgaas/src/stage/opt/app/pgaas/man/startpsql.man new file mode 100644 index 0000000..dc792f6 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/startpsql.man @@ -0,0 +1,21 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH startpsq 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +startpsql - run the psql command +.SH SYNOPSIS +startpsql +.SH DESCRIPTION +Locate the psql command and run it. +.SH "SEE ALSO" +runpsql, runpsqll diff --git a/pgaas/src/stage/opt/app/pgaas/man/update_var_run_isrw.man b/pgaas/src/stage/opt/app/pgaas/man/update_var_run_isrw.man new file mode 100644 index 0000000..bc5ecc8 --- /dev/null +++ b/pgaas/src/stage/opt/app/pgaas/man/update_var_run_isrw.man @@ -0,0 +1,23 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH startpsq 1PG {{DATE}} OpenECOMP OpenECOMP +.SH NAME +update_var_run_isrw - update the /var/run/postgresql/isrw file +.SH SYNOPSIS +update_var_run_isrw +.SH DESCRIPTION +This program updates the /var/run/postgresql/isrw file using the output of the isrw(1PG) command. +The file is updated 6 times separated by 10 seconds. +It is meant to be run from the postgres cron. +.SH "SEE ALSO" +isrw diff --git a/postgresql-config/.gitignore b/postgresql-config/.gitignore new file mode 100644 index 0000000..7c32f55 --- /dev/null +++ b/postgresql-config/.gitignore @@ -0,0 +1 @@ +install diff --git a/postgresql-config/src/common/postinst b/postgresql-config/src/common/postinst new file mode 100755 index 0000000..b681058 --- /dev/null +++ b/postgresql-config/src/common/postinst @@ -0,0 +1,40 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +exec 1> /tmp/postgresql-config.out 2>&1 +set -x +if [ -d /opt/app/postgresql-9.5.2 ] +then export OPENECOMP=false NOTOPENECOMP=true +else export OPENECOMP=true NOTOPENECOMP=false +fi +id + +if $OPENECOMP +then INSTALL_ROOT= +fi + +echo STARTING $0 $(date) +umask 0 +echo STARTING $0 $(date) >> /tmp/pgaas.inst.report + +export CFGDIR=${INSTALL_ROOT}/opt/app/postgresql-config/ + +$CFGDIR/etc/do-post-install + +echo ENDING $0 $(date) +echo ENDING $0 $(date) >> /tmp/pgaas.inst.report +if $NOTOPENECOMP +then sed -n '/^STARTING/,/^ENDING/p' `dirname $0`/../../proc_out >> /tmp/pgaas.inst.report +fi diff --git a/postgresql-config/src/makefile b/postgresql-config/src/makefile new file mode 100644 index 0000000..bf6ae6b --- /dev/null +++ b/postgresql-config/src/makefile @@ -0,0 +1,39 @@ + +DEVBIN=../../bin +PKG=postgresql-config +REPACKAGESWMOPTS= +REPACKAGEDEBIANOPTS= + +INS= ../install +INSSTG= $(INS)/stage +INSCOM= $(INS)/common + +all: + +clean-stage: + rm -rf $(INSSTG) + +clean-common: + rm -rf $(INSCOM) + +clean: + rm -rf $(INS) + +build: + +stage: clean-stage clean-common + mkdir -p $(INS) + find stage ! -name makefile ! -name '*~' | cpio -pudmv $(INS) + find common ! -name makefile ! -name '*~' | cpio -pudmv $(INS) + chmod -R a+x $(INS)/stage/opt/app/postgresql-config/etc/* + cp -p repackage.* $(INS) + + +debian: stage + repackage -y repackage.json -b debian -d $(INS) -u + repackage -y repackage.json -b debian -d $(INS) -u -B LATEST + @echo debian built + +upload-javadocs: + @echo nothing to do here + diff --git a/postgresql-config/src/repackage.json b/postgresql-config/src/repackage.json new file mode 100644 index 0000000..d4376fe --- /dev/null +++ b/postgresql-config/src/repackage.json @@ -0,0 +1,25 @@ +{ + "debian": { + "replaces": [], + "conflicts": [], + "groupId": "org.openecomp.dcae.storage.pgaas", + "externalDependencies": [] + }, + "fileGroup": "postgres", + "version": "1.0.0", + "applicationName": "postgresql-config", + "internalDependencies": [], + "directoryTreeTops": { + "/opt": "/opt/app/postgresql-config" + }, + "executionUser": "postgres", + "maintainer": "OpenECOMP <dcae@lists.openecomp.org>", + "fileUser": "postgres", + "docker": { + "tag": "latest", + "externalDependencies": [] + }, + "groupId": "org.openecomp.dcae.storage.pgaas", + "description": " PostgreSQL as a Service main scripts ", + "executionGroup": "postgres" +}
\ No newline at end of file diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/common-db-tasks b/postgresql-config/src/stage/opt/app/postgresql-config/etc/common-db-tasks new file mode 100644 index 0000000..517fabd --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/common-db-tasks @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$DBROOT" ] || die "DBROOT is not set" +[ -n "$CFGDIR" ] || die "CFGDIR is not set" + +# set up ~/.pgpass +$CFGDIR/etc/gen-pgpass +# set up repmgr.conf +$CFGDIR/etc/gen-repmgr.conf + +# We don't really need to save pwd.cfg anymore since we are now forcing the password. +# PWDCFG=$DBROOT/../pgaas/pwd.cfg +# egrep '^Global_Title|^postgres|^repmgr' ${INSTALL_ROOT}/opt/app/cdf/lib/cdf.cfg > $PWDCFG + +cd $CFGDIR/main || die "Cannot cd $CFGDIR/main" + +sed -e "s!%CFGDIR%!$CFGDIR!" < postgresql.conf.orig > postgresql.conf || die "Cannot cp postgresql.conf" +sed -e "s!%CFGDIR%!$CFGDIR!" < pg_hba.conf.orig > pg_hba.conf || die "Cannot cp pg_hba.conf" + diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-cdf-master b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-cdf-master new file mode 100644 index 0000000..c079b51 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-cdf-master @@ -0,0 +1,44 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +DBROOT=/dbroot/pgdata/main +CDFCFG=${INSTALL_ROOT}/opt/app/cdf/lib/cdf.cfg + +# We don't really need to save pwd.cfg anymore since we are now forcing the password. +# PWDCFG=$DBROOT/../pgaas/pwd.cfg +# if the DB already exists in Cinder storage, grab the password from there for use elsewhere +# if [ -s $PWDCFG -a $( egrep '^Global_Title' < $PWDCFG ) -eq 1 ] +# then +# TMP=$(mktemp /tmp/tmp.ccm.XXXXXXXXXX) +# trap 'rm -f $TMP' 0 1 2 3 15 +# egrep -v '^Global_Title|^postgres|^repmgr' $CDFCFG > $TMP +# egrep '^Global_Title|^postgres|^repmgr' $PWDCFG | cat $TMP - > $CDFCFG +# fi + +# generate a 64 hex random value (256 bits of randomness) for the passwords +if grep '^postgres' $CDFCFG > /dev/null +then : +else + val2=$(dd if=/dev/urandom count=1 ibs=32 2>/dev/null | od -x -w1000 | sed -e 's/^0000000 //' -e 's/ //g' -e 1q) + echo "ENCRYPTME.AES.postgres=$val2" | ${INSTALL_ROOT}/opt/app/cdf/bin/setencryptedvalues >> $CDFCFG +fi +if grep '^repmgr' $CDFCFG > /dev/null +then : +else + val2=$(dd if=/dev/urandom count=1 ibs=32 2>/dev/null | od -x -w1000 | sed -e 's/^0000000 //' -e 's/ //g' -e 1q) + echo "ENCRYPTME.AES.repmgr=$val2" | ${INSTALL_ROOT}/opt/app/cdf/bin/setencryptedvalues >> $CDFCFG +fi diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-cdf-secondary b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-cdf-secondary new file mode 100644 index 0000000..034d897 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-cdf-secondary @@ -0,0 +1,62 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$MASTER" ] || die "MASTER is not set" + +umask 077 +TMP=$( mktemp /tmp/tmp.ccs1.XXXXXXXXXX ) +trap 'rm -f $TMP' 0 1 2 3 15 + +bwget() +{ + ${INSTALL_ROOT}/opt/app/postgresql-prep/bin/pgwget --progress=dot:giga "$@" +} + +done= +max=40 +for s in `seq $max` +do + echo "$s of $max: Waiting for master $MASTER to send cdf.cfg" + bwget -O$TMP http://$MASTER:8000/getcdf/`hostname -f` + ls -l $TMP + if [ -s $TMP ] + then + msg=$(cat $TMP) + case "$msg" in + OK* ) + echo "Received cdf.cfg" + done=yes + break + ;; + * ) echo "Received invalid cdf: $msg" + ;; + esac + fi + rm -f $TMP + sleep 15 +done +[ "$done" = "yes" ] || die "Master did not send cdf.cfg" + diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-backup b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-backup new file mode 100644 index 0000000..625ce57 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-backup @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /opt/app/postgresql-9.5.2 ] +then PGDIR=/opt/app/postgresql-9.5.2 +else PGDIR=/usr/lib/postgresql/9.5 +fi + +$PGDIR/bin/psql <<-EOF + SELECT pg_start_backup('backup'); +EOF + +cd /dbroot/pgdata && +{ + find main | grep -v main/pg_xlog/ + find main/pg_xlog -type d +} | cpio -oc | gzip > main.cpio.gz.$$ && mv main.cpio.gz.$$ main.cpio.gz + +$PGDIR/bin/psql <<-EOF + SELECT pg_stop_backup(); +EOF +echo /dbroot/pgdata/main.cpio.gz created diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-master b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-master new file mode 100644 index 0000000..f0081be --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-master @@ -0,0 +1,72 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# create a master database +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$PGDIR" ] || die "PGDIR is not set" +[ -n "$DBROOT" ] || die "DBROOT is not set" +[ -n "$CFGDIR" ] || die "CFGDIR is not set" + +cd $CFGDIR/main || die "Cannot cd $CFGDIR/main" + +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$CFGDIR/etc:$PGDIR/bin:$PATH + +umask 077 +TMP=$(mktemp /tmp/tmp.cdm.XXXXXXXXXX) +trap 'rm -f $TMP' 0 1 2 3 15 + +rm -rf $DBROOT/* # initdb fails if the directory is not totally empty +pswd=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -x -n postgres ) +echo "$pswd" > $TMP +$PGDIR/bin/initdb -D $DBROOT --pwfile=$TMP +rm -f $TMP + +$CFGDIR/etc/start-db +sleep 30 + +# create temporal tables and other extensions, if needed +$CFGDIR/etc/create-extensions + +# create repmgr user/db, if needed +$CFGDIR/etc/create-repmgr-user + +sleep 10 + +# register as master +repmgr -f $CFGDIR/main/repmgr.conf master register +echo repmgr ret=$? + +sleep 10 + +# start repmgrd +# start repmgrd (verbose logging for testing) +umask 07 +repmgrd -f $CFGDIR/main/repmgr.conf -d --verbose +echo repmgrd ret=$? +# start repmgrd (normal logging) +# repmgrd -f $CFGDIR/main/repmgr.conf -d + + +# NO LONGER NEEDED $CFGDIR/etc/create-db-backup diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-secondary b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-secondary new file mode 100644 index 0000000..3e2c304 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-db-secondary @@ -0,0 +1,122 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# create a secondary database +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$MASTER" ] || die "MASTER is not set" +[ -n "$PGDIR" ] || die "PGDIR is not set" +[ -n "$DBROOT" ] || die "DBROOT is not set" +[ -n "$CFGDIR" ] || die "CFGDIR is not set" + +cd $CFGDIR/main || die "Cannot cd $CFGDIR/main" + +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$CFGDIR/etc:$PGDIR/bin:$PATH + +bwget() +{ + ${INSTALL_ROOT}/opt/app/postgresql-prep/bin/pgwget --progress=dot:giga "$@" +} + +umask 077 +TMP=$(mktemp /tmp/tmp.cds1.XXXXXXXXXX) +TMP2=$(mktemp /tmp/tmp.cds2.XXXXXXXXXX) +trap 'rm -f $TMP $TMP2' 0 1 2 3 15 + +# wait until master DB is active and has repmgr available +max=40 +for s in `seq $max` +do + echo "$s of $max: Asking master $MASTER if repmgr is ready" + bwget -O$TMP http://$MASTER:8000/hasrepmgr + ls -l $TMP + if [ -s $TMP ] + then + msg=$(cat $TMP) + case $msg in + OK* ) + echo "Master has repmgr ready" + done=yes + break + ;; + * ) + echo "Master does not have repmgr ready, msg=$msg" + ;; + esac + fi + rm -f $TMP + sleep 15 +done +[ "$done" = "yes" ] || die "Master never had repmgr available" + + +# clone database from master + +# make sure /dbroot/pgdata/main is empty +mv $DBROOT $DBROOT-$(date +%Y%m%d%H%M%S) +mkdir -p $DBROOT +# rm -rf $DBROOT/* + +repmgr -v -h $MASTER -U repmgr -d repmgr -D $DBROOT -f $CFGDIR/main/repmgr.conf --ignore-external-config-files standby clone + +if [ ! -f $DBROOT/PG_VERSION ] +then + umask 022 + cat /opt/app/log/postgresql/server/repmgr.log >> /tmp/pgaas-failures + die repmgr clone failed +fi + +$CFGDIR/etc/start-db + +sleep 10 + +# register as standby +repmgr -f $CFGDIR/main/repmgr.conf standby register +echo repmgr ret=$? +sleep 10 + +# start repmgrd +# start repmgrd (verbose logging for testing) +umask 07 + +# wait until repmgrd starts up +max=20 +REPLOG=/opt/app/log/postgresql/server/repmgr.log +done=no +for s in `seq $max` +do + cat $REPLOG > $TMP + repmgrd -f $CFGDIR/main/repmgr.conf -d --verbose + # start repmgrd (normal logging) + # repmgrd -f $CFGDIR/main/repmgr.conf -d + echo repmgrd ret=$? + sleep 5 + diff $TMP $REPLOG | grep "ERROR.*terminating" > $TMP2 + if [ -s "$TMP2" ] + then cat "$TMP2" + else done=yes; break + fi + sleep 10 +done +[ "$done" = "yes" ] || die "Secondary never started repmgrd" diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-extensions b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-extensions new file mode 100644 index 0000000..09f0229 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-extensions @@ -0,0 +1,53 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$PGDIR" ] || die "PGDIR is not set" + +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$PGDIR/bin:$PATH + +TMP=$(mktemp /tmp/tmp.ce.XXXXXXXXXX) +trap 'rm -f $TMP' 0 1 2 3 15 + +echo "select datname from pg_database;" | psql --tuples-only | sed -e 's/^ *//' -e '/^$/d' -e '/^template0$/d' -e '/^repmgr$/d' > $TMP + +for db in $(< $TMP) +do + # enable temporal tables for use + if [ -f /opt/app/postgresql-9.5.2/lib/temporal_tables.so ] + then + psql --dbname=$db <<-EOF + CREATE EXTENSION temporal_tables; + EOF + else + echo "$0: temporal_tables extension is not installed" + fi + + # and other extensions + psql --dbname=$db <<-EOF + CREATE EXTENSION hstore; + CREATE EXTENSION pgcrypto; + EOF +done diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-repmgr-user b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-repmgr-user new file mode 100644 index 0000000..06b5a0a --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-repmgr-user @@ -0,0 +1,40 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$PGDIR" ] || die "PGDIR is not set" + +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$PGDIR/bin:$PATH +pswd=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -x -n repmgr ) + +# note: The "pgaas" in "repmgr_pgaas" must match the cluster name used in repmgr.conf + +psql <<-EOF + CREATE ROLE repmgr SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN; + DROP DATABASE repmgr; + CREATE DATABASE repmgr OWNER repmgr; + ALTER USER repmgr PASSWORD '$pswd'; + ALTER USER repmgr SET search_path TO repmgr_pgaas, "\$user", public; +EOF diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-ssh-master b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-ssh-master new file mode 100644 index 0000000..5565041 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-ssh-master @@ -0,0 +1,40 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +umask 077 +mkdir -p ~postgres/.ssh +chmod 700 ~postgres/.ssh + +PGAASDIR=/dbroot/pgdata/pgaas +if [ -f $PGAASDIR/id_rsa.pub -a -f $PGAASDIR/id_rsa -a $PGAASDIR/authorized_keys ] +then + cp -p $PGAASDIR/id_rsa.pub $PGAASDIR/id_rsa $PGAASDIR/authorized_keys ~postgres/.ssh +else + ssh-keygen -t rsa -N '' -f ~postgres/.ssh/id_rsa + cp -p ~postgres/.ssh/id_rsa.pub ~postgres/.ssh/authorized_keys + cp -p ~postgres/.ssh/id_rsa ~postgres/.ssh/id_rsa.pub ~postgres/.ssh/authorized_keys $PGAASDIR +fi + diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-ssh-secondary b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-ssh-secondary new file mode 100644 index 0000000..a5ee2d4 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/create-ssh-secondary @@ -0,0 +1,102 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +bwget() +{ + ${INSTALL_ROOT}/opt/app/postgresql-prep/bin/pgwget --progress=dot:giga "$@" +} + +[ -n "$MASTER" ] || die "MASTER is not set" + + + +umask 077 +TMP=$(mktemp /tmp/tmp.css1.XXXXXXXXXX) +TMP2=$(mktemp /tmp/tmp.css2.XXXXXXXXXX) +trap 'rm -f $TMP $TMP2' 0 1 2 3 15 + +# do we have the keys already? +if [ -f $PGAASDIR/id_rsa.pub -a -f $PGAASDIR/id_rsa -a $PGAASDIR/authorized_keys ] +then + mkdir -p ~postgres/.ssh + chmod 700 ~postgres/.ssh + cp -p $PGAASDIR/id_rsa.pub $PGAASDIR/id_rsa $PGAASDIR/authorized_keys ~postgres/.ssh +else + # no? copy them from the master + done= + max=40 + for s in `seq $max` + do + echo "$s of $max: Waiting for master $MASTER to come online and send its public key" + bwget -O$TMP http://$MASTER:8000/getpubkey + ls -l $TMP + if [ -s $TMP ] + then + msg=$(cat $TMP) + case "$msg" in + ssh-rsa* ) + echo "Received public key" + mkdir -p ~postgres/.ssh + chmod 700 ~postgres/.ssh + cp -p $TMP ~postgres/.ssh/authorized_keys + done=yes + break + ;; + * ) echo "Received invalid public key: $msg" + ;; + esac + else + echo "No key available yet" + fi + rm -f $TMP + sleep 15 + done + [ "$done" = "yes" ] || die "Unable to get key from $MASTER" + + done= + max=40 + for s in `seq $max` + do + echo "$s of $max: Asking for master $MASTER to send remaining ssh files" + bwget -O$TMP2 http://$MASTER:8000/getssh/`hostname -f` + ls -l $TMP2 + if [ -s $TMP2 ] + then + msg=$(cat $TMP2) + case "$msg" in + OK* ) echo "Master has sent the remaining ssh keys" + done=yes + break + ;; + * ) echo "No ssh keys yet: $msg" + ;; + esac + fi + rm -f $TMP2 + sleep 15 + done + [ "$done" = "yes" ] || die "Master did not send ssh keys" +fi diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/do-post-install b/postgresql-config/src/stage/opt/app/postgresql-config/etc/do-post-install new file mode 100644 index 0000000..9b25be8 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/do-post-install @@ -0,0 +1,124 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$CFGDIR" ] || die "CFGDIR is not set" +[ -n "$OPENECOMP" ] || die "OPENECOMP is not set" +[ -n "$NOTOPENECOMP" ] || die "NOTOPENECOMP is not set" + +if $OPENECOMP +then export PGDIR=${INSTALL_ROOT}/usr/lib/postgresql/9.5 +else export PGDIR=${INSTALL_ROOT}/opt/app/postgresql-9.5.2 +fi +export DBROOT=/dbroot/pgdata/main +export PATH=$PATH:${INSTALL_ROOT}/opt/app/postgresql-prep/bin + +$CFGDIR/etc/makecerts + +cat $CFGDIR/lib/profile.additions >> ~postgres/.profile + +# Determine which system is the master. +# For central, we look first in /tmp/postgres.conf. +# If we don't find that, we look at the pgnodes list and pick the first one. +# For edge, we ignore /tmp/postgres.conf and go directly to the pgnodes list. +# Each edge site has its own master. +clustertype=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -n cluster ) +ismaster=no + +case $clustertype in + central ) + CONF=/tmp/postgres.conf + if [ -f $CONF ] # OpenDCAE + then + umask 077 + TMP=$(mktemp /tmp/tmp.pi1.XXXXXXXXXX) + trap 'rm -f $TMP' 0 1 2 3 15 + sed -e 's/ *: */="/' -e 's/$/"/' -e 's/=""/="/' -e 's/""$/"/' < $CONF > $TMP + . $TMP + case `hostname` in + $master ) ismaster=yes ;; + *?* ) ismaster=no ;; + '' ) die "master is not set in $CONF" + esac + PGNODES=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -n pgnodes ) + export MASTER=$( gen-repmgr-info -n "$PGNODES" -M "$master" ) + [ -n "$MASTER" ] || die "Cannot determine master system. $CONF has '$master' (from env.yaml), which cannot be found in pgnodes." + + else + # not OpenDCAE + ismaster=yes + PGNODES=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -n pgnodes ) + export MASTER=$( gen-repmgr-info -n "$PGNODES" -m ) + fi + ;; + edge ) + host=$( hostname -f ) + PGNODES=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -n pgnodes ) + export MASTER=$( gen-repmgr-info -n "$PGNODES" -C $host ) + case $MASTER in + '' ) die "Cannot determine master system. Does cdf.cfg have pgnodes= in it? Is $host listed as a site?" ;; + DEFAULT ) ismaster=yes MASTER=$host ;; + esac + ;; +esac + +ssh_and_cdf_okay=no + +case $ismaster in + yes ) # master + $CFGDIR/etc/create-ssh-master && + $CFGDIR/etc/create-cdf-master && + ssh_and_cdf_okay=yes + ;; + + no ) # secondary + $CFGDIR/etc/create-ssh-secondary && + $CFGDIR/etc/create-cdf-secondary && + touch $CFGDIR/lib/ignore-database-reconfiguration # prevent dcae_admin_db.py from looking at json DB reconfigurations && + ssh_and_cdf_okay=yes + ;; +esac + +[ "$ssh_and_cdf_okay" = yes ] || die "Could not set up ssh or cdf" + +$CFGDIR/etc/common-db-tasks +# check if we have a database already +if [ ! -s $DBROOT/PG_VERSION ] +then + # need to create it + case $ismaster in + yes ) $CFGDIR/etc/create-db-master ;; + no ) $CFGDIR/etc/create-db-secondary ;; + esac +else + # need to update it + case $ismaster in + yes ) $CFGDIR/etc/update-db-master ;; + no ) + $CFGDIR/etc/create-db-secondary # use repmgr clone even if secondary previously existed + # $CFGDIR/etc/update-db-secondary + ;; + esac +fi diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-pgpass b/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-pgpass new file mode 100644 index 0000000..ca99a0f --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-pgpass @@ -0,0 +1,33 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# create ~postgres/.pgpass +postgrespswd=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -x -n postgres ) +repmgrpswd=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -x -n repmgr ) +umask 077 +if [ -f ~postgres/.pgpaas ] +then + ed ~postgres/.pgpaas <<-EOF + H + g/:postgres:/d + g/:repmgr:/d + w + q + EOF +fi + +echo "*:*:*:postgres:$postgrespswd" >> ~postgres/.pgpass +echo "*:*:*:repmgr:$repmgrpswd" >> ~postgres/.pgpass +chmod go-rwx ~postgres/.pgpass diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-recovery.conf b/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-recovery.conf new file mode 100644 index 0000000..fc80cb0 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-recovery.conf @@ -0,0 +1,51 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$DBROOT" ] || die "DBROOT is not set" + +cd $DBROOT || die "Cannot cd $DBROOT" +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$PATH + +PGNODES=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -n pgnodes ) +HOSTNAME=`hostname -f` +PGNODEVALUE=$( gen-repmgr-info -n "$PGNODES" -l "$HOSTNAME" ) + +# node_name from repmgr.conf => application_name in recovery.conf conninfo line +# "node" value from repmgr.conf => primary_slot_name in recovery.conf with the string "repmgr_slot_" prefixed +# node_name in repmgr.conf can be the $HOSTNAME value ? + +pswd=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -x -n repmgr ) + +appname=$HOSTNAME +PGNODEVALUE=$( gen-repmgr-info -n "$PGNODES" -l "$HOSTNAME" ) +umask 07 +cat <<-EOF > $DBROOT/recovery.conf + standby_mode = 'on' + primary_conninfo = 'user=repmgr password=$pswd host=$HOSTNAME port=5432 application_name=$HOSTNAME sslmode=prefer sslcompression=1' + recovery_target_timeline = 'latest' + primary_slot_name = repmgr_slot_$PGNODEVALUE +EOF + diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-repmgr.conf b/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-repmgr.conf new file mode 100755 index 0000000..ca595f6 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/gen-repmgr.conf @@ -0,0 +1,63 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$CFGDIR" ] || die "CFGDIR is not set" + +cd $CFGDIR/main || die "Cannot cd $CFGDIR/main" +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$PATH +LOGDIR=/opt/app/log/postgresql/server + +PGNODES=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -n pgnodes ) + +CLUSTER=pgaas +HOSTNAME=`hostname -f` +PGNODEVALUE=$( gen-repmgr-info -n "$PGNODES" -l "$HOSTNAME" ) +UPSTREAMPGNODE=$( gen-repmgr-info -n "$PGNODES" -c "$HOSTNAME" ) + +UPSTREAMTEXT="#upstream_node=" +case $UPSTREAMPGNODE in + DEFAULT ) ;; + * ) UPSTREAMTEXT="upstream_node=$UPSTREAMPGNODE" ;; +esac + +cat <<-EOF > repmgr.conf + cluster=$CLUSTER + node=$PGNODEVALUE + node_name=$HOSTNAME + conninfo='host=$HOSTNAME user=repmgr dbname=repmgr' + use_replication_slots=1 + $UPSTREAMTEXT + + failover=automatic + promote_command='repmgr standby promote -f $CFGDIR/main/repmgr.conf' + follow_command='repmgr standby follow -f $CFGDIR/main/repmgr.conf' + event_notification_command='/opt/app/postgresql-prep/bin/repmgrd-status-changes %n %e %s "%t" "%d"' + + #Log level: possible values are DEBUG, INFO, NOTICE, WARNING, ERR, ALERT, CRIT or EMERG + loglevel=INFO + logfile='$LOGDIR/repmgr.log' +EOF + diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/lock-and-create-db-backup b/postgresql-config/src/stage/opt/app/postgresql-config/etc/lock-and-create-db-backup new file mode 100644 index 0000000..ff942e6 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/lock-and-create-db-backup @@ -0,0 +1,33 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +if [ -d /opt/app/postgresql-9.5.2 ] +then PGDIR=${INSTALL_ROOT}/opt/app/postgresql-9.5.2 +else PGDIR=/usr/lib/postgresql/9.5 +fi + +export PATH=$PGDIR/bin:${INSTALL_ROOT}/opt/java/jdk/jdk170/bin:${INSTALL_ROOT}/opt/app/cdf/bin:${INSTALL_ROOT}/opt/app/pgaas/bin:$PATH + +LOCKFILE=/var/lock/create-db-backup + +testlock -s -t 0 -r 99 ${LOCKFILE} create-db-backup +retc=$? + +if [ $retc -eq 99 ] +then + echo Backup is already being created +fi +exit $retc + diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/makecerts b/postgresql-config/src/stage/opt/app/postgresql-config/etc/makecerts new file mode 100755 index 0000000..a272f7b --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/makecerts @@ -0,0 +1,97 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# NAME +# makecerts - Create elf-signed certificates for PostgreSQL +# +# USAGE +# makecerts [--force-overwrite] +# +# FILES +# /opt/app/postgresql-config/etc +# ssleay.cnf - template +# /opt/app/postgresql-config/lib +# ssl-cert-snakeoil.pem - public key +# ssl-cert-snakeoil.key - private key + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +if [ -d ${INSTALL_ROOT}/opt/app/postgresql-config ] +then dir=${INSTALL_ROOT}/opt/app/postgresql-config +else dir=${INSTALL_ROOT}/opt/app/postgresql-config-9.5.2 +fi +etcdir=$dir/etc +libdir=$dir/lib +template="$etcdir/ssleay.cnf" + +usage() +{ + exec 1>&2 + echo "Usage: $0 [--force-overwrite]" + echo "Create self-signed certificates for $dir" + exit 1 +} + +if [ -f "$libdir/ssl-cert-snakeoil.pem" ] && [ -f "$libdir/ssl-cert-snakeoil.key" ]; then + if [ "$1" != "--force-overwrite" ]; then + exit 0 + fi +fi + +# make_snakeoil + +if ! HostName="$(hostname -f)" ; then + HostName="$(hostname)" + echo "$0: Could not get FQDN, using \"$HostName\"." + echo "$0: You may want to fix your /etc/hosts and/or DNS setup and run" + echo "$0: '$0 --force-overwrite'" + echo "$0: again." +fi +if [ ${#HostName} -gt 64 ] ; then + AltName="DNS:$HostName" + HostName="$(hostname)" +fi + + +TMPFILE="$(mktemp /tmp/tmp.mc1.XXXXXXXXXX)" || die mktemp failed +TMPOUT="$(mktemp /tmp/tmp.mc2.XXXXXXXXXX)" || die mktemp failed + +trap "rm -f $TMPFILE $TMPOUT" EXIT 1 2 3 15 + +# create_temporary_cnf + sed -e s#@HostName@#"$HostName"# $template > $TMPFILE + [ -z "$AltName" ] || echo "subjectAltName=$AltName" >> $TMPFILE + +# create the certificate. + +if ! openssl req -config $TMPFILE -new -x509 -days 3650 -nodes \ + -out $libdir/ssl-cert-snakeoil.pem \ + -keyout $libdir/ssl-cert-snakeoil.key > $TMPOUT 2>&1 +then + echo Could not create certificate. Openssl output was: >&2 + cat $TMPOUT >&2 + die openssl failed +fi +chmod 644 $libdir/ssl-cert-snakeoil.pem +chmod 600 $libdir/ssl-cert-snakeoil.key +# hash symlink +ln -sf ssl-cert-snakeoil.pem $libdir/$(openssl x509 -hash -noout -in $libdir/ssl-cert-snakeoil.pem) diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/ssleay.cnf b/postgresql-config/src/stage/opt/app/postgresql-config/etc/ssleay.cnf new file mode 100644 index 0000000..2b665cc --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/ssleay.cnf @@ -0,0 +1,33 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +# +# SSLeay example configuration file. +# + +RANDFILE = /dev/urandom + +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +prompt = no +policy = policy_anything +req_extensions = v3_req +x509_extensions = v3_req + +[ req_distinguished_name ] +commonName = @HostName@ + +[ v3_req ] +basicConstraints = CA:FALSE diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/start-db b/postgresql-config/src/stage/opt/app/postgresql-config/etc/start-db new file mode 100644 index 0000000..2d50b40 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/start-db @@ -0,0 +1,32 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$PGDIR" ] || die "PGDIR is not set" +[ -n "$DBROOT" ] || die "DBROOT is not set" +[ -n "$CFGDIR" ] || die "CFGDIR is not set" + +rm -f $DBROOT/postmaster.pid +$PGDIR/bin/pg_ctl start -D $DBROOT -o "-c config_file=$CFGDIR/main/postgresql.conf" diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-db-master b/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-db-master new file mode 100644 index 0000000..0c0c238 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-db-master @@ -0,0 +1,63 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# update a master database +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$PGDIR" ] || die "PGDIR is not set" +[ -n "$DBROOT" ] || die "DBROOT is not set" +[ -n "$CFGDIR" ] || die "CFGDIR is not set" + +cd $CFGDIR/main || die "Cannot cd $CFGDIR/main" + +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$CFGDIR/etc:$PGDIR/bin:$PATH + +# update postgresql.conf - got new ones +# update pg_hba.conf - got new ones +# set up repmgr.conf - in common + +# start the DB +start-db +sleep 10 + +# make sure the postgres password is right +$CFGDIR/etc/update-postgres-user + +# create temporal tables and other extensions, if needed +$CFGDIR/etc/create-extensions + +# create repmgr user/db, if needed +$CFGDIR/etc/create-repmgr-user + +# register as master +repmgr -f $CFGDIR/main/repmgr.conf master register +echo repmgr ret=$? + +# start repmgrd +# start repmgrd (verbose logging for testing) +umask 07 +repmgrd -f $CFGDIR/main/repmgr.conf -d --verbose +echo repmgrd ret=$? +# start repmgrd (normal logging) +# repmgrd -f $CFGDIR/main/repmgr.conf -d diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-db-secondary b/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-db-secondary new file mode 100644 index 0000000..b819865 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-db-secondary @@ -0,0 +1,95 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# update a secondary database +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$MASTER" ] || die "MASTER is not set" +[ -n "$PGDIR" ] || die "PGDIR is not set" +[ -n "$DBROOT" ] || die "DBROOT is not set" +[ -n "$CFGDIR" ] || die "CFGDIR is not set" + +cd $CFGDIR/main || die "Cannot cd $CFGDIR/main" + +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$CFGDIR/etc:$PGDIR/bin:$PATH + +umask 077 +TMP=$(mktemp /tmp/tmp.uds1.XXXXXXXXXX) +trap 'rm -f $TMP' 0 1 2 3 15 + +# update postgresql.conf - got new ones +# update pg_hba.conf - got new ones +# set up repmgr.conf - in common + +# replace/update recovery.conf +if [ -f $DBROOT/recovery.conf ];then mv $DBROOT/recovery.conf $DBROOT/recovery.conf.upgraded;fi +$CFGDIR/etc/gen-recovery.conf + +# wait until master DB is active and has repmgr available +max=40 +for s in `seq $max` +do + echo "$s of $max: Asking master $MASTER if repmgr is ready" + pgwget --progress=dot:giga -O$TMP http://$MASTER:8000/hasrepmgr + if [ -s $TMP ] + then + msg=$(cat $TMP) + case $msg in + OK* ) + echo "Master has repmgr ready" + done=yes + break + ;; + * ) + echo "Master does not have repmgr ready, msg=$msg" + ;; + esac + fi + rm -f $TMP + sleep 15 +done +[ "$done" = "yes" ] || die "Master never had repmgr available" + + +$CFGDIR/etc/start-db + +sleep 10 + +# make sure the postgres password is right +$CFGDIR/etc/update-postgres-user + +# register as standby +repmgr -f $CFGDIR/main/repmgr.conf standby register +echo repmgr ret=$? +sleep 10 + +# start repmgrd +# start repmgrd (verbose logging for testing) +umask 07 +repmgrd -f $CFGDIR/main/repmgr.conf -d --verbose +echo repmgrd ret=$? +# start repmgrd (normal logging) +# repmgrd -f $CFGDIR/main/repmgr.conf -d + +chmod 600 recovery.conf diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-postgres-user b/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-postgres-user new file mode 100644 index 0000000..b7f3762 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/etc/update-postgres-user @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +set -x + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +[ -n "$PGDIR" ] || die "PGDIR is not set" + +PATH=${INSTALL_ROOT}/opt/app/postgresql-prep/bin:$PGDIR/bin:$PATH +pswd=$( ${INSTALL_ROOT}/opt/app/cdf/bin/getpropvalue -x -n postgres ) + +psql <<-EOF + ALTER USER postgres PASSWORD '$pswd'; + EOF diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/lib/profile.additions b/postgresql-config/src/stage/opt/app/postgresql-config/lib/profile.additions new file mode 100644 index 0000000..3ee8128 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/lib/profile.additions @@ -0,0 +1,6 @@ + +if [ -d /opt/app/postgresql-9.5.2 ] +then PGDIR=/opt/app/postgresql-9.5.2 +else PGDIR=/usr/lib/postgresql/9.5 +fi +export PATH="$PGDIR/bin:/opt/app/cdf/bin:/opt/app/pgaas/bin:/opt/app/postgresql-prep/bin:$PATH" diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/main/pg_hba.conf.orig b/postgresql-config/src/stage/opt/app/postgresql-config/main/pg_hba.conf.orig new file mode 100644 index 0000000..7bf51c1 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/main/pg_hba.conf.orig @@ -0,0 +1,127 @@ +# PostgreSQL Client Authentication Configuration File +# =================================================== +# +# Refer to the "Client Authentication" section in the PostgreSQL +# documentation for a complete description of this file. A short +# synopsis follows. +# +# This file controls: which hosts are allowed to connect, how clients +# are authenticated, which PostgreSQL user names they can use, which +# databases they can access. Records take one of these forms: +# +# local DATABASE USER METHOD [OPTIONS] +# host DATABASE USER ADDRESS METHOD [OPTIONS] +# hostssl DATABASE USER ADDRESS METHOD [OPTIONS] +# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] +# +# (The uppercase items must be replaced by actual values.) +# +# The first field is the connection type: "local" is a Unix-domain +# socket, "host" is either a plain or SSL-encrypted TCP/IP socket, +# "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a +# plain TCP/IP socket. +# +# DATABASE can be "all", "sameuser", "samerole", "replication", a +# database name, or a comma-separated list thereof. The "all" +# keyword does not match "replication". Access to replication +# must be enabled in a separate record (see example below). +# +# USER can be "all", a user name, a group name prefixed with "+", or a +# comma-separated list thereof. In both the DATABASE and USER fields +# you can also write a file name prefixed with "@" to include names +# from a separate file. +# +# ADDRESS specifies the set of hosts the record matches. It can be a +# host name, or it is made up of an IP address and a CIDR mask that is +# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that +# specifies the number of significant bits in the mask. A host name +# that starts with a dot (.) matches a suffix of the actual host name. +# Alternatively, you can write an IP address and netmask in separate +# columns to specify the set of hosts. Instead of a CIDR-address, you +# can write "samehost" to match any of the server's own IP addresses, +# or "samenet" to match any address in any subnet that the server is +# directly connected to. +# +# METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", +# "ident", "peer", "pam", "ldap", "radius" or "cert". Note that +# "password" sends passwords in clear text; "md5" is preferred since +# it sends encrypted passwords. +# +# OPTIONS are a set of options for the authentication in the format +# NAME=VALUE. The available options depend on the different +# authentication methods -- refer to the "Client Authentication" +# section in the documentation for a list of which options are +# available for which authentication methods. +# +# Database and user names containing spaces, commas, quotes and other +# special characters must be quoted. Quoting one of the keywords +# "all", "sameuser", "samerole" or "replication" makes the name lose +# its special character, and just match a database or username with +# that name. +# +# This file is read on server startup and when the postmaster receives +# a SIGHUP signal. If you edit the file on a running system, you have +# to SIGHUP the postmaster for the changes to take effect. You can +# use "pg_ctl reload" to do that. + +# Put your actual configuration here +# ---------------------------------- +# +# If you want to allow non-local connections, you need to add more +# "host" records. In that case you will also need to make PostgreSQL +# listen on a non-local interface via the listen_addresses +# configuration parameter, or via the -i or -h command line switches. + +### @authcomment@ +### +### # TYPE DATABASE USER ADDRESS METHOD +### +### @remove-line-for-nolocal@# "local" is for Unix domain socket connections only +### @remove-line-for-nolocal@local all all @authmethodlocal@ +### # IPv4 local connections: +### host all all 127.0.0.1/32 @authmethodhost@ +### # IPv6 local connections: +### host all all ::1/128 @authmethodhost@ +### # Allow replication connections from localhost, by a user with the +### # replication privilege. +### @remove-line-for-nolocal@#local replication @default_username@ @authmethodlocal@ +### #host replication @default_username@ 127.0.0.1/32 @authmethodhost@ +### #host replication @default_username@ ::1/128 @authmethodhost@ + +# DO NOT DISABLE! +# If you change this first entry you will need to make sure that the +# database superuser can access the database using some other method. +# Noninteractive access to all databases is required during automatic +# maintenance (custom daily cronjobs, replication, and similar tasks). +# +# Database administrative login by Unix domain socket +local all postgres peer + +# TYPE DATABASE USER ADDRESS METHOD + +# DCAE IPv4/IPv6 remote connections: +host all all 0.0.0.0/0 md5 +host all all ::/0 md5 + +# "local" is for Unix domain socket connections only +local all all peer +# IPv4 local connections: +host all all 127.0.0.1/32 md5 +# IPv6 local connections: +host all all ::1/128 md5 +# Allow replication connections from localhost, by a user with the +# replication privilege. +# local replication postgres peer +# host replication postgres 127.0.0.1/32 md5 +# host replication postgres 0.0.0.0/0 md5 +# host replication postgres ::1/128 md5 + +local replication repmgr md5 +host replication repmgr 127.0.0.1/32 md5 +host replication repmgr 0.0.0.0/0 md5 +host replication repmgr ::1/128 md5 + +local repmgr repmgr md5 +host repmgr repmgr 127.0.0.1/32 md5 +host repmgr repmgr 0.0.0.0/0 md5 +host repmgr repmgr ::1/128 md5 diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/main/pg_ident.conf b/postgresql-config/src/stage/opt/app/postgresql-config/main/pg_ident.conf new file mode 100644 index 0000000..a5870e6 --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/main/pg_ident.conf @@ -0,0 +1,42 @@ +# PostgreSQL User Name Maps +# ========================= +# +# Refer to the PostgreSQL documentation, chapter "Client +# Authentication" for a complete description. A short synopsis +# follows. +# +# This file controls PostgreSQL user name mapping. It maps external +# user names to their corresponding PostgreSQL user names. Records +# are of the form: +# +# MAPNAME SYSTEM-USERNAME PG-USERNAME +# +# (The uppercase quantities must be replaced by actual values.) +# +# MAPNAME is the (otherwise freely chosen) map name that was used in +# pg_hba.conf. SYSTEM-USERNAME is the detected user name of the +# client. PG-USERNAME is the requested PostgreSQL user name. The +# existence of a record specifies that SYSTEM-USERNAME may connect as +# PG-USERNAME. +# +# If SYSTEM-USERNAME starts with a slash (/), it will be treated as a +# regular expression. Optionally this can contain a capture (a +# parenthesized subexpression). The substring matching the capture +# will be substituted for \1 (backslash-one) if present in +# PG-USERNAME. +# +# Multiple maps may be specified in this file and used by pg_hba.conf. +# +# No map names are defined in the default configuration. If all +# system user names and PostgreSQL user names are the same, you don't +# need anything in this file. +# +# This file is read on server startup and when the postmaster receives +# a SIGHUP signal. If you edit the file on a running system, you have +# to SIGHUP the postmaster for the changes to take effect. You can +# use "pg_ctl reload" to do that. + +# Put your actual configuration here +# ---------------------------------- + +# MAPNAME SYSTEM-USERNAME PG-USERNAME diff --git a/postgresql-config/src/stage/opt/app/postgresql-config/main/postgresql.conf.orig b/postgresql-config/src/stage/opt/app/postgresql-config/main/postgresql.conf.orig new file mode 100644 index 0000000..b2587db --- /dev/null +++ b/postgresql-config/src/stage/opt/app/postgresql-config/main/postgresql.conf.orig @@ -0,0 +1,655 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, or use "pg_ctl reload". Some +# parameters, which are marked below, require a server shutdown and restart to +# take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: kB = kilobytes Time units: ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +data_directory = '/dbroot/pgdata/main' # for DCAE +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +hba_file = '%CFGDIR%/main/pg_hba.conf' # for DCAE +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +ident_file = '%CFGDIR%/main/pg_ident.conf' # for DCAE +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +external_pid_file = '/var/run/postgresql/9.5-main.pid' # for DCAE +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +# DCAE -- for 1607 IST30, set to '*' to allow remote connections +listen_addresses = '*' # for DCAE + +#listen_addresses = 'localhost' # what IP address(es) to listen on; + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +#max_connections = 100 # (change requires restart) +# Note: Increasing max_connections costs ~400 bytes of shared memory per +# connection slot, plus lock space (see max_locks_per_transaction). +#superuser_reserved_connections = 3 # (change requires restart) +unix_socket_directories = '/var/run/postgresql,/tmp' # for DCAE +#unix_socket_directories = '/tmp' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - Security and Authentication - + +#authentication_timeout = 1min # 1s-600s +ssl = true # for DCAE +#ssl = off # (change requires restart) +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers + # (change requires restart) +#ssl_prefer_server_ciphers = on # (change requires restart) +#ssl_ecdh_curve = 'prime256v1' # (change requires restart) +ssl_cert_file = '%CFGDIR%/lib/ssl-cert-snakeoil.pem' # for DCAE +#ssl_cert_file = 'server.crt' # (change requires restart) +ssl_key_file = '%CFGDIR%/lib/ssl-cert-snakeoil.key' # for DCAE +#ssl_key_file = 'server.key' # (change requires restart) +#ssl_ca_file = '' # (change requires restart) +#ssl_crl_file = '' # (change requires restart) +#password_encryption = on +#db_user_namespace = off +#row_security = on + +# GSSAPI using Kerberos +#krb_server_keyfile = '' +#krb_caseins_users = off + +# - TCP Keepalives - +# see "man 7 tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +#shared_buffers = 32MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Note: Increasing max_prepared_transactions costs ~600 bytes of shared memory +# per transaction slot, plus lock space (see max_locks_per_transaction). +# It is not advisable to set max_prepared_transactions nonzero unless you +# actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#maintenance_work_mem = 64MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#max_stack_depth = 2MB # min 100kB +#dynamic_shared_memory_type = posix # the default is the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # use none to disable dynamic shared memory + +# - Disk - + +#temp_file_limit = -1 # limits per-session temp file space + # in kB, or -1 for no limit + +# - Kernel Resource Usage - + +#max_files_per_process = 1000 # min 25 + # (change requires restart) +#shared_preload_libraries = '' # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 10 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multipler on buffers scanned/round + +# - Asynchronous Behavior - + +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 + + +#------------------------------------------------------------------------------ +# WRITE AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +# TLH - this is where we do WAL settings +wal_level = hot_standby # minimal, archive, hot_standby, or logical + # (change requires restart) +#fsync = on # turns forced synchronization on or off +#synchronous_commit = on # synchronization level; + # off, local, remote_write, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_compression = off # enable compression of full-page writes +wal_log_hints = on # also do full page writes of non-critical updates + # (change requires restart) +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1h +#max_wal_size = 1GB +#min_wal_size = 80MB +#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_warning = 30s # 0 disables + +# - Archiving - + +archive_mode = on # enables archiving; off, on, or always + # (change requires restart) +archive_command = 'test ! -f /dbroot/pglogs/main/%f && cp %p /dbroot/pglogs/main/%f' + # command to use to archive a logfile segment + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +archive_timeout = 0 # force a logfile segment switch after this + # number of seconds; 0 disables + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Server(s) - + +# Set these on the master and on any standby that will send replication data. + +max_wal_senders = 4 # max number of walsender processes + # (change requires restart) +#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables + +max_replication_slots = 5 # max number of replication slots + # (change requires restart) + # DCAE NOTE: if we ever grow our cluster, change this value to + # the number of nodes + 1 +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Master Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # comma-separated list of application_name + # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed + +# - Standby Servers - + +# These settings are ignored on a master server. + +hot_standby = on # "on" allows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from master + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_bitmapscan = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#effective_cache_size = 4GB + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses + + +#------------------------------------------------------------------------------ +# ERROR REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +logging_collector = on # for DCAE +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +log_directory = '/opt/app/log/postgresql/server' # for DCAE +#log_directory = 'pg_log' # directory where log files are written, + # can be absolute or relative to PGDATA +log_filename = 'error.log' # for DCAE +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +log_file_mode = 0666 # for DCAE +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. +log_rotation_age = 1d # for DCAE +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +log_rotation_size = 0 # for DCAE +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' + +# This is only relevant when logging to eventlog (win32): +#event_source = 'PostgreSQL' + +# - When to Log - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +log_line_prefix = '%t|%a|%u|%d|%i|%e|%r|%c|' # for DCAE +#log_line_prefix = '' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %p = process ID + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +#log_timezone = 'GMT' + + +# - Process Title - + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# RUNTIME STATISTICS +#------------------------------------------------------------------------------ + +# - Query/Index Statistics Collector - + +#track_activities = on +#track_counts = on +#track_io_timing = off +#track_functions = none # none, pl, all +#track_activity_query_size = 1024 # (change requires restart) +#stats_temp_directory = 'pg_stat_tmp' + + +# - Statistics Monitoring - + +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off +#log_statement_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM PARAMETERS +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#search_path = '"$user", public' # schema names +#default_tablespace = '' # a tablespace name, '' uses the default +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_min_age = 50000000 +#vacuum_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_freeze_table_age = 150000000 +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_fuzzy_search_limit = 0 +#gin_pending_list_limit = 4MB + +# - Locale and Formatting - + +#datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +#timezone = 'GMT' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 0 # min -15, max 3 +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +#lc_messages = 'C' # locale for system error message + # strings +#lc_monetary = 'C' # locale for monetary formatting +#lc_numeric = 'C' # locale for number formatting +#lc_time = 'C' # locale for time formatting + +# default configuration for text search +#default_text_search_config = 'pg_catalog.simple' + +# - Other Defaults - + +#dynamic_library_path = '$libdir' +#local_preload_libraries = '' +#session_preload_libraries = '' + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +# Note: Each lock table slot uses ~270 bytes of shared memory, and there are +# max_locks_per_transaction * (max_connections + max_prepared_transactions) +# lock table slots. +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) + + +#------------------------------------------------------------------------------ +# VERSION/PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#default_with_oids = off +#escape_string_warning = on +#lo_compat_privileges = off +#operator_precedence_warning = off +#quote_all_identifiers = off +#sql_inheritance = on +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. + +#include_dir = 'conf.d' # include files ending in '.conf' from + # directory 'conf.d' +#include_if_exists = 'exists.conf' # include file only if it exists +#include = 'special.conf' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here + +# repmgr / repmgrd +shared_preload_libraries = 'repmgr_funcs' diff --git a/postgresql-prep/.gitignore b/postgresql-prep/.gitignore new file mode 100644 index 0000000..7c32f55 --- /dev/null +++ b/postgresql-prep/.gitignore @@ -0,0 +1 @@ +install diff --git a/postgresql-prep/src/common/postinst b/postgresql-prep/src/common/postinst new file mode 100755 index 0000000..63a76f2 --- /dev/null +++ b/postgresql-prep/src/common/postinst @@ -0,0 +1,177 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +exec 1> /tmp/postgresql-prep.out 2>&1 +set -x +if [ -d /opt/app/postgresql-9.5.2 ] +then export OPENECOMP=false NOTOPENECOMP=true +else export OPENECOMP=true NOTOPENECOMP=false +fi + +echo STARTING $0 $(date) +umask 0 +echo STARTING $0 $(date) >> /tmp/pgaas.inst.report + +die() +{ + echo $0: "$@" 1>&2 + echo $0: "$@" + umask 022 + echo $0: "$@" >> /tmp/pgaas-failures + exit 1 +} + +id +umask 022 + +TMP=$( mktemp /tmp/pgprep.$$.XXXXXXXXXX ) +#### TODO remove comment trap 'rm -f $TMP' 0 1 2 3 15 + +if $OPENECOMP +then INSTALL_ROOT= +fi + +if [ -f /tmp/postgres.conf ] +then + # DRTR_NODE_KSTOREFILE: /opt/app/dcae-certificate/keystore.jks + # DRTR_NODE_KSTOREPASS: "No Certificate" + # DRTR_NODE_PVTKEYPASS: "No Certificate" + # PG_NODES : uiopmno1qwpstg00.research.example.com|asbczw1vepstg00.dcae.simpledemo.openecomp.org + # PG_JAVA_HOME : /opt/app/java/jdk/jdk170 + # PG_CLUSTER : global + sed -e 's/ *: */="/' -e 's/$/"/' -e 's/=""/="/' -e 's/""$/"/' < /tmp/postgres.conf > $TMP + . $TMP +fi + +[ -n "$PG_NODES" ] || die "PG_NODES is not set" +[ -n "$PG_CLUSTER" ] || die "PG_CLUSTER is not set" +[ -n "$DRTR_NODE_KSTOREFILE" ] || die "DRTR_NODE_KSTOREFILE is not set" +[ -n "$DRTR_NODE_KSTOREPASS" ] || die "DRTR_NODE_KSTOREPASS is not set" +[ -n "$DRTR_NODE_PVTKEYPASS" ] || die "DRTR_NODE_PVTKEYPASS is not set" + +# create various directories with proper permissions +mkdir -p ${INSTALL_ROOT}/dbroot/pgdata/main \ + ${INSTALL_ROOT}/dbroot/pgdata/pgaas \ + ${INSTALL_ROOT}/dbroot/pglogs/main \ + ${INSTALL_ROOT}/var/run/postgresql \ + ${INSTALL_ROOT}/opt/app/log/postgresql/init \ + ${INSTALL_ROOT}/opt/app/log/postgresql/server \ + ${INSTALL_ROOT}/opt/app/log/postgresql/idns +chmod 700 ${INSTALL_ROOT}/dbroot/pgdata/pgaas +chmod 700 ${INSTALL_ROOT}/dbroot/pglogs +chmod 700 ${INSTALL_ROOT}/dbroot/pgdata/main + +if $OPENECOMP +then + mv /var/lib/postgresql/9.5/main /var/lib/postgresql/9.5/main.sv + ln -s /dbroot/dbdata/main /var/lib/postgresql/9.5/main + + mv /etc/postgresql/9.5/main /etc/postgresql/9.5/main.sv + ln -s /opt/app/postgresql-config/main /etc/postgresql/9.5/main + +fi + +chown -R postgres:postgres ${INSTALL_ROOT}/dbroot ${INSTALL_ROOT}/var/run/postgresql ${INSTALL_ROOT}/opt/app/log/postgresql ${INSTALL_ROOT}/opt/app/log/postgresql/idns + +chmod 711 ~postgres + +# fix up the CDF package so that it works +ln -sf /opt/app/cdf /opt/cdf + +# and save some values within +( + echo "allpgnodes=\"$PG_NODES\"" + case "$PG_CLUSTER" in + global | central ) + cnodes=$( ${INSTALL_ROOT}/opt/app/postgresql-prep/bin/gen-repmgr-info -n "$PG_NODES" -p ) + echo "pgnodes=\"$cnodes\"" + echo "cluster=central" + shanodes=$( ${INSTALL_ROOT}/opt/app/postgresql-prep/bin/gen-repmgr-info -n "$PG_NODES" -P ) + ;; + site | edge ) + HOSTNAME=$( hostname -f ) + lnodes=$( ${INSTALL_ROOT}/opt/app/postgresql-prep/bin/gen-repmgr-info -n "$PG_NODES" -e $HOSTNAME ) + echo "pgnodes=\"$lnodes\"" + if [ -z "$lnodes" ] + then die "Cannot determine the name of the system. hostname -f ($HOSTNAME) is not found in PG_NODES ($PG_NODES)" + fi + shanodes=$( ${INSTALL_ROOT}/opt/app/postgresql-prep/bin/gen-repmgr-info -n "$PG_NODES" -E $HOSTNAME ) + echo "cluster=edge" + ;; + * ) die "Cannot determine what type of cluster this is. PG_CLUSTER should be either 'global/central' or 'site/edge'" ;; + esac + + echo "drtr_node_kstorefile=$DRTR_NODE_KSTOREFILE" + echo "ENCRYPTME.AES.drtr_node_kstorepass='$DRTR_NODE_KSTOREPASS'" | ${INSTALL_ROOT}/opt/app/cdf/bin/setencryptedvalues + echo "ENCRYPTME.AES.drtr_node_pvtkeypass='$DRTR_NODE_PVTKEYPASS'" | ${INSTALL_ROOT}/opt/app/cdf/bin/setencryptedvalues + echo "ENCRYPTME.AES.wgetpswd=$shanodes" | ${INSTALL_ROOT}/opt/app/cdf/bin/setencryptedvalues +) >> ${INSTALL_ROOT}/opt/app/cdf/lib/cdf.cfg + +# install the init scripts for postgresql +# init.d-pgaas init-pgaas-idns.conf init-pgaas-init.conf logrotate + +INIT=${INSTALL_ROOT}/opt/app/postgresql-prep/init + +if $OPENECOMP +then + su postgres -c "crontab $INIT/pglogs.cron" + + # no need to create the /var/run directory because postgresql package already does it + + # install the init script for iDNS + cp $INIT/systemd-pgaas-idns.service /lib/systemd/system/pgaas-idns.service + systemctl stop pgaas-idns + sleep 1 + systemctl start pgaas-idns + +else + INITDEST=${INSTALL_ROOT}/opt/app/platform/init.d/pgaas + cp $INIT/init.d-pgaas $INITDEST + chown postgres:postgres $INITDEST + chmod 755 $INITDEST + + cd ${INSTALL_ROOT}/opt/app/platform/rc.d + ln -sf ../init.d/pgaas K20pgaas + ln -sf ../init.d/pgaas S20pgaas + + CRONDIR=${INSTALL_ROOT}/opt/app/platform/cron/postgres + mkdir $CRONDIR + chown postgres:postgres $CRONDIR + chmod 755 $CRONDIR + cp $INIT/pglogs.cron $CRONDIR/pglogs.cron + su postgres -c "sh -x ${INSTALL_ROOT}/opt/app/platform/bin/mergeCron" + + # install the init script for init + cp $INIT/init-pgaas-init.conf ${INSTALL_ROOT}/etc/init/pgaas-init.conf + service pgaas-init stop + sleep 1 + service pgaas-init start + + # install the init script for iDNS + cp $INIT/init-pgaas-idns.conf ${INSTALL_ROOT}/etc/init/pgaas-idns.conf + service pgaas-idns stop + sleep 1 + service pgaas-idns start +fi + +cp $INIT/logrotate ${INSTALL_ROOT}/etc/logrotate.d/pgaas +chown root:root ${INSTALL_ROOT}/etc/logrotate.d/pgaas +chmod 644 ${INSTALL_ROOT}/etc/logrotate.d/pgaas + +echo ENDING $0 $(date) +echo ENDING $0 $(date) >> /tmp/pgaas.inst.report +if $NOTOPENECOMP +then sed -n '/^STARTING/,/^ENDING/p' `dirname $0`/../../proc_out >> /tmp/pgaas.inst.report +fi diff --git a/postgresql-prep/src/common/prerm b/postgresql-prep/src/common/prerm new file mode 100755 index 0000000..a03a13b --- /dev/null +++ b/postgresql-prep/src/common/prerm @@ -0,0 +1,17 @@ +echo STARTING $0 $(date) + +set -x +id + +if [ -d /opt/app/postgresql-9.5.2 ] +then + rm -f $INSTALL_ROOT/opt/app/platform/postgres/pglogs.cron + su postgres -c "$INSTALL_ROOT/opt/app/platform/bin/mergeCron" + rmdir $INSTALL_ROOT/opt/app/platform/postgres + rm -f $INSTALL_ROOT/opt/app/platform/init.d/pgaas + rm -f $INSTALL_ROOT/opt/app/platform/rc.d/K20pgaas + rm -f $INSTALL_ROOT/opt/app/platform/rc.d/S20pgaas +fi + +rm -f $INSTALL_ROOT/etc/init/pgaas-idns.conf $INSTALL_ROOT/etc/init/pgaas-init.conf +rm -f $INSTALL_ROOT/etc/logrotate.d/pgaas diff --git a/postgresql-prep/src/makefile b/postgresql-prep/src/makefile new file mode 100644 index 0000000..1266aa6 --- /dev/null +++ b/postgresql-prep/src/makefile @@ -0,0 +1,43 @@ + +DEVBIN=../../bin +PKG=postgresql-prep +REPACKAGESWMOPTS= +REPACKAGEDEBIANOPTS= + +INS= ../install +INSSTG= $(INS)/stage +INSCOM= $(INS)/common + +all: + +clean-stage: + rm -rf $(INSSTG) + +clean-common: + rm -rf $(INSCOM) + +clean: + rm -rf $(INS) testlock/testlock + +testlock/testlock: + cd testlock && make testlock + +build: testlock/testlock + +stage: clean-stage clean-common testlock/testlock + mkdir -p $(INS) + find stage ! -name makefile ! -name '*~' | cpio -pudmv $(INS) + find common ! -name makefile ! -name '*~' | cpio -pudmv $(INS) + cp -p testlock/testlock $(INSSTG)/opt/app/postgresql-prep/bin/testlock + chmod a+x $(INSSTG)/opt/app/postgresql-prep/bin/* + cp -p repackage.* $(INS) + + +debian: stage + repackage -y repackage.json -b debian -d $(INS) -u + repackage -y repackage.json -b debian -d $(INS) -u -B LATEST + @echo debian built + +upload-javadocs: + @echo nothing to do here + diff --git a/postgresql-prep/src/repackage.json b/postgresql-prep/src/repackage.json new file mode 100644 index 0000000..358fe31 --- /dev/null +++ b/postgresql-prep/src/repackage.json @@ -0,0 +1,29 @@ +{ + "description": " PostgreSQL as a Service main scripts ", + "fileGroup": "root", + "groupId": "org.openecomp.dcae.storage.pgaas", + "fileUser": "root", + "executionGroup": "root", + "executionUser": "root", + "internalDependencies": [], + "maintainer": "OpenECOMP <dcae@lists.openecomp.org>", + "docker": { + "tag": "latest", + "externalDependencies": [] + }, + "applicationName": "postgresql-prep", + "debian": { + "groupId": "org.openecomp.dcae.storage.pgaas", + "externalDependencies": [ + { + "cdf": ">= 1.0.0" + } + ], + "replaces": [], + "conflicts": [] + }, + "version": "1.0.0", + "directoryTreeTops": { + "/opt": "/opt/app/postgresql-prep" + } +}
\ No newline at end of file diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/gen-repmgr-info b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/gen-repmgr-info new file mode 100755 index 0000000..b210e58 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/gen-repmgr-info @@ -0,0 +1,247 @@ +#!/usr/bin/perl +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# NAME +# gen-repmgr-info - extract information about the system appropriate for use with repmgr +# +# DESCRIPTION +# gen-repmgr-info -n hosts options... +# +# for a list of hosts such as "uiopzxc5pepstg00.grant.example.com|uiopzxc5pepstg01.grant.example.com|uiopmtc6njpstg00.grant.example.com|uiopmtc6njpstg01.grant.example.com" +# extract various pieces of information about the list. For example, generate a list of repmgr node numbers like this: +# uiopzxc5pepstg00.grant.example.com 100 +# uiopzxc5pepstg01.grant.example.com 101 +# uiopmtc6njpstg00.grant.example.com 200 +# uiopmtc6njpstg01.grant.example.com 201 + +use strict vars; + +use Getopt::Std; +use Digest::SHA qw(sha256_hex); + +sub usage { + my $msg = shift; + print "$msg\n" if $msg; + print "Usage: $0 -n 'node|node|node|...' [-S] [-s site] [-L] [-l node] [-c node] [-C node] [-m] [-M node] [-e] [-v] [-p]\n"; + print "-n list of nodes (FQDNs), |-separated\n"; + print "-S show list of all sites and their node # values\n"; + print "-s site\tshow the node # value for a given site\n"; + print "-L show list of all nodes and their node # values\n"; + print "-l node\tshow the node # value for a given node\n"; + print "-C node\tshow the machine name that a given node should cascade from, or DEFAULT\n"; + print "-c node\tshow the machine node # that a given node should cascade from, or DEFAULT\n"; + print "-e node\tshow the list of nodes on the same site as the given node, |-separated\n"; + print "-m\twhich system is the 'master'\n"; + print "-M node\twhich system matches the given node, taking FQDN into consideration\n"; + print "-v\tverbose\n"; + print "-p\tprint the node names in sorted order\n"; + exit 1; +} + + +my %optargs; +getopts('C:c:e:E:Ll:M:mn:pPSs:v', \%optargs) or usage(); +my $verbose = $optargs{v}; + +my $pgnodes = $optargs{n} or usage("-n is required"); + +# generate the data about the nodes +my @pgnodes = sort split(/[|]/, $pgnodes); + +# @sites contains the list of all site names. +# For uiopzxc5pepstg01.grant.example.com, the sitename will be uiopzxc5pepstg. +my @sites = genSites(); +my %pgnodesToSite = genPgnodeToSites(); + +# The %siteValues contains 100, 200, etc for each site name +# print "\nsites=" . join("\n", @sites); +my %siteValues = genSiteValues(); + +# The %pgnodeValues contains 100, 101, 200, 201, etc for each node name +my %pgnodeValues = genPgnodeValues(); +# The %valuesToPgnode contains node names for each value +my %valuesToPgnode = genValuesToPgnodes(); + +if ($optargs{L}) { + for my $pgnode (@pgnodes) { + print "$pgnode $pgnodeValues{$pgnode}\n"; + } +} + +if ($optargs{S}) { + for my $site (@sites) { + print "$site $siteValues{$site}\n"; + } +} + +if ($optargs{s}) { + for my $site (@sites) { + print "$siteValues{$site}\n" if $site eq $optargs{s}; + } +} + +if ($optargs{l}) { + for my $pgnode (@pgnodes) { + print "$pgnodeValues{$pgnode}\n" if $pgnode eq $optargs{l}; + } +} + +if ($optargs{c}) { + my $pgnode = $optargs{c}; + my $pgnodeValue = $pgnodeValues{$pgnode}; + my $masterValue = int($pgnodeValue / 100) * 100; + if (($masterValue > 100) && (($masterValue % 100) > 0)) { + print "$masterValue\n"; + } else { + print "DEFAULT\n"; + } +} + +if ($optargs{C}) { + my $pgnode = $optargs{C}; + my $pgnodeValue = $pgnodeValues{$pgnode}; + my $masterValue = int($pgnodeValue / 100) * 100; + # print "pgnode=$pgnode, pgnodeValue=$pgnodeValue, masterValue=$masterValue\n"; + if (($pgnodeValue % 100) > 0) { + print "$valuesToPgnode{$masterValue}\n"; + } else { + print "DEFAULT\n"; + } +} + +sub enodes { + my $pgnodeSearch = $optargs{e}; + my $siteSearch = $pgnodesToSite{$pgnodeSearch}; + my $ret = ""; + # print "looking for $pgnodeSearch in $siteSearch\n"; + my $sep = ""; + for my $pgnode (@pgnodes) { + my $site = $pgnodesToSite{$pgnode}; + # print "looking at $pgnode in $site\n"; + if ($site eq $siteSearch) { + $ret .= "$sep$pgnode"; + $sep = "|"; + } + } + return $ret; +} + +if ($optargs{e}) { + my $ret = enodes(); + print "$ret\n"; +} + +if ($optargs{E}) { + print sha256_hex(enodes()) . "\n"; +} + +if ($optargs{m}) { + print "$pgnodes[0]\n"; +} + +if ($optargs{M}) { + my $node = $optargs{M}; + if ($node =~ /[.]/) { + print "$node\n"; + } else { + my $found; + for my $pgnode (@pgnodes) { + if ($pgnode =~ /^$node[.]/) { + print $node; + $found = 1; + last; + } + } + } +} + +sub pnodes { + return join("|", @pgnodes); +} + +if ($optargs{p}) { + print pnodes() . "\n"; +} + +if ($optargs{P}) { + print sha256_hex(pnodes()) . "\n"; +} + +# for a given node name uiopzxc5pepstg01.grant.example.com, the return uiopzxc5pepstg. +sub nodeToSite { + my $site = shift; + $site =~ s/[.].*//; + $site =~ s/\d*$//; + return $site; +} + +# from a list of nodes, generate the sorted list of sites +sub genSites { + my %sites = (); + # print "pgnodes=" . join("\n", @pgnodes); + for my $pgnode (@pgnodes) { + my $site = nodeToSite($pgnode); + $sites{$site} = $site; + } + my @sites = sort keys %sites; + return @sites; +} + +# from a list of nodes, generate a mapping from them to their sites +sub genPgnodeToSites { + my %sites = (); + for my $pgnode (@pgnodes) { + $sites{$pgnode} = nodeToSite($pgnode); + } + return %sites; +} + +# generate the 100, 200, etc for each site name +sub genSiteValues { + my %siteValues; + for (my $i = 0; $i <= $#sites; $i++) { + $siteValues{$sites[$i]} = ($i+1) * 100; + } + # print "\nsiteValues=\n"; for my $site (@sites) { print "$site $siteValues{$site}\n"; } + return %siteValues; +} + +sub genPgnodeValues { + my %pgnodeValues; + my $i = 0; + my $lastSite = ''; + for my $pgnode (@pgnodes) { + my $thisSite = nodeToSite($pgnode); + if ($thisSite eq $lastSite) { + $i++; + } else { + $i = 0; + } + $lastSite = $thisSite; + $pgnodeValues{$pgnode} = $siteValues{$thisSite} + $i; + } + # print "\nnodeValues=\n"; for my $pgnode (@pgnodes) { print "$pgnode $pgnodeValues{$pgnode}\n"; } + return %pgnodeValues; +} + +sub genValuesToPgnodes { + my %valuesToPgnode; + for my $pgnode (keys %pgnodeValues) { + my $value = $pgnodeValues{$pgnode}; + $valuesToPgnode{$value} = $pgnode; + } + return %valuesToPgnode; +} + diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/iDNS-responder.py b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/iDNS-responder.py new file mode 100755 index 0000000..b6c5174 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/iDNS-responder.py @@ -0,0 +1,1025 @@ +#!/usr/bin/env python3 +# -*- indent-tabs-mode: nil -*- +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +import http.server +import time, os, sys, re, subprocess, traceback, html, base64 +import psycopg2 +# TODO - move lots of code to a common library to share with other python modules +# sys.path.append("/opt/app/postgres-prep/lib") +# import dbtools + +getLogDict = { } +def openLogFile(fname): + """ + Open a log file for append and remember the file descriptor. + Remember its inode/dev pair. + If either changes, reopen it. + """ + reopen = False + try: + curstat = os.stat(fname) + except: + reopen = True + global getLogDict + # print("top: reopen(%s)=%s" % (fname, reopen)) + if not reopen and getLogDict.get(fname): + # print("found getLogDict.get(" + fname + ")") + d = getLogDict[fname] + fd = d["fd"] if "fd" in d else None + oldstat = d["stat"] if "stat" in d else None + if fd is None: + reopen = True + elif oldstat is None: + reopen = True + elif oldstat.st_ino != curstat.st_ino or oldstat.st_dev != curstat.st_dev: + reopen = True + if reopen or not getLogDict.get(fname): + # print("closing old fd") + oldd = getLogDict.get(fname) + if oldd is not None: + oldfd = oldd.get("fd") + if oldfd is not None: + oldfd.close() + # print("reopening " + fname) + fd = open(fname, "a") + st = os.stat(fname) + getLogDict[fname] = { "fd": fd, "stat": st } + return getLogDict[fname]["fd"] + +debugOn = False +testOn = False + +if len(sys.argv) > 1: + debugOn = True + testOn = True + +else: + sys.stderr = openLogFile("/opt/app/log/postgresql/idns/error.log") + +HOST_NAME = os.popen("hostname -f").readlines()[0].strip() +PORT_NUMBER = 8000 + +validPerDbTables = [ "pg_tables", "pg_indexes", "pg_views" ] +topButton = " <font size='1'><a href='#'>^</a></font>" + +def traceMsg(msg): + """ print a trace message. By default, this goes to trace.out """ + file = sys.stderr if testOn else openLogFile("/opt/app/log/postgresql/idns/trace.log") + print(time.asctime(), msg, file=file) + file.flush() + +def errTrace(msg): + """ print an error message. By default, sys.stderr is rerouted to error.log """ + file = sys.stderr if testOn else openLogFile("/opt/app/log/postgresql/idns/error.log") + sys.stderr = file + print(time.asctime(), msg, file=file) + file.flush() + +def debugTrace(msg): + """ print a debug message. By default, this goes to debug.log """ + if debugOn: + file = sys.stderr if testOn else openLogFile("/opt/app/log/postgresql/idns/debug.log") + print(time.asctime(), msg, file=file) + file.flush() + +def readFile(file, defStr = None, mode = "r"): + """ read a file and return its contents """ + ret = defStr + try: + with open(file, mode) as f: + ret = f.read() + except Exception as e: + if defStr is not None: + ret = defStr + pass + else: + raise e + return ret + +def readFileBinary(file, defStr = None): + return readFile(file, defStr = defStr, mode = "rb") + +def readFileHtml(file, defStr = None): + """ read a file and return its contents, escaping anything important to HTML """ + return html.escape(readFile(file, defStr)) + +def readPipe(cmd, ignoreError = False): + """ read a pipe and return its contents """ + ret = "" + try: + with os.popen(cmd) as p: + ret = p.read() + except Exception as e: + if ignoreError: + pass + else: + raise e + return ret + +def readPipeHtml(file, defStr = None): + """ read a pipe and return its contents, escaping anything important to HTML """ + return html.escape(readPipe(file, defStr)) + +def readFileOrGz(file, defStr = None): + """ read a file and return its contents. If the file ends in .gz, use gunzip on it """ + if file.endswith(".gz"): + return readPipe("gunzip < '" + file + "'", defStr) + else: + return readFile(file, defStr) + +def readFileOrGzHtml(file, defStr = None): + """ read a file and return its contents, escaping anything important to HTML. If the file ends in .gz, use gunzip on it """ + return html.escape(readFileOrGz(file, defStr)) + +def getCdfPropValue(nm, encrypted=False, cfg="/opt/app/cdf/lib/cdf.cfg", dflt=None): + """ + Return a value from the configuration file /opt/app/cdf/lib/cdf.cfg + """ + return getPropValue(nm=nm, encrypted=encrypted, cfg=cfg, dflt=dflt) + +def getPgaasPropValue(nm, encrypted=False, cfg="/opt/app/pgaas/lib/pgaas.cfg", dflt=None): + """ + Return a value from the configuration file /opt/app/pgaas/lib/pgaas.cfg + """ + return getPropValue(nm=nm, encrypted=encrypted, cfg=cfg, dflt=dflt) + +getPropDict = { } + +def getPropValue(nm, encrypted=False, cfg=None, dflt=None): + """ + Return a value from the specified configuration file + """ + if cfg is None: + return None + global getPropDict + if getPropDict.get(cfg): + savedDate = getPropDict[cfg] + debugTrace("getPropValue: savedDate[" + cfg + "]=" + str(savedDate)) + cfgDate = os.path.getmtime(cfg) + debugTrace("getPropValue: cfgDate=" + str(cfgDate)) + if float(savedDate) >= float(cfgDate): # cfg has not changed + val = getPropDict.get(cfg + ":" + nm) + debugTrace("getPropValue: val=" + str(val)) + if val is not None: + debugTrace("getPropValue: getPropValue(saved) => '%s'" % str(val)) + return val + else: # clear out any previously saved keys + cfgcolon = cfg + ":" + for k in list(getPropDict.keys()): + if re.match(cfgcolon, k): + del getPropDict[k] + getPropValueProgram = '/opt/app/cdf/bin/getpropvalue' + if encrypted: + cmd = [getPropValueProgram, "-f", cfg, "-x", "-n", nm] + else: + cmd = [getPropValueProgram, "-f", cfg, "-n", nm] + debugTrace("getPropValue: cmd=%s" % str(cmd)) + + try: + with subprocess.Popen(cmd,shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p: + (origString, stderrString) = p.communicate() + except Exception as e: + traceback.print_exc() + errTrace("Error decoding string because {0}".format(e)) + return None + else: + if stderrString: + if not re.search("Configuration property .* must be defined", stderrString.decode('utf-8')): # and dflt is not None: + errTrace("Error decoding string because: {0} ".format(stderrString)) + return dflt + else: + debugTrace("getPropValue() => '%s'" % str(origString)) + getPropDict[cfg] = os.path.getmtime(cfg) + val = origString.decode('utf-8').rstrip('\n') + debugTrace("getPropValue() => '%s'" % val) + getPropDict[cfg + ":" + nm] = val + return val + +def checkFileAge(full_path,number_of_days): + """ + return True if the file is >= number_of_days old from right now + """ + time_n_days_ago = time.time() - (number_of_days * 24 * 60 * 60) + stat = os.stat(full_path) + return time_n_days_ago >= stat.st_mtime + +def jumpTable(prefix, *args): + """ + Return a string consisting of a series of <a href='#prefix-xxx'>xxx</a>. + Include <font size='1'></font> around all of it. + """ + header = "<font size='1'>" + sep = "" + for table in args: + header = header + sep + "<a href='#" + prefix + "-" + table + "'>" + table + "</a> " + sep = " | " + header = header + "</font>" + return header + +def addFilenameHrefs(prefix, str): + """ + for each line in a list of filenames, change the last two elements of the path to an anchor. + """ + ret = "" + for line in str.splitlines(): + line = re.sub("/([^/]+)/([^/]+)$", '/\g<1>' + "/<a href='" + prefix + '\g<1>/\g<2>' + "'>" + '\g<2>' + "</a>", line) + ret = ret + line + "\n" + return ret + +def ifEmpty(str, defStr): + """ if a string is empty, return the defStr in its place """ + if str is None or str == "": + str = defStr + return str + +def isExe(fname): + """ check if a path exists and is executable """ + return os.path.exists(fname) and os.access(fname, os.X_OK) + +class MyHandler(http.server.BaseHTTPRequestHandler): + + def isServerUp(self): + """ + Check if the postgres server is up and running by calling pg_ctl and + looking for "server is running" (or "no server running"). + Then call ps -fu postgres and make sure we're not waiting on a master: + postgres 20815 20812 0 15:52 ? 00:00:00 postgres: startup process waiting for 000000010000000000000001 + """ + PGCTLPATH1 = "/usr/lib/postgresql/9.5/bin/pg_ctl" + PGCTLPATH2 = "/opt/app/postgresql-9.5.2/bin/pg_ctl" + if isExe(PGCTLPATH1): + statusLines = readPipe(PGCTLPATH1 + " status -D /dbroot/pgdata/main/") + else: + statusLines = readPipe(PGCTLPATH2 + " status -D /dbroot/pgdata/main/") + debugTrace("isServerUp(): statusLines = %s" % statusLines) + psLines = readPipe("ps -fu postgres") + debugTrace("isServerUp(): ps -fu postgres = %s" % psLines) + ret = len(statusLines) > 0 and re.search("server is running", statusLines, re.MULTILINE) and not re.search("startup process\\s+waiting", psLines, re.MULTILINE) + debugTrace("isServerUp(): returning = %s" % ret) + return ret + + def isRepmgrdUp(self): + """ + Check if the repmgrd server is up and running by calling "pgrep repmgrd" and + looking for a process id. + """ + statusLines = readPipe("pgrep repmgrd") + debugTrace("isServerUp(): statusLines = %s" % statusLines) + ret = len(statusLines) > 0 and re.search("[0-9]+", statusLines, re.MULTILINE) != None + debugTrace("isServerUp(): returning = %s" % ret) + return ret + + def isMaster(self): + """ + Check if the postgresql server is a master by asking the server if it is in recovery (meaning not a master) + """ + ret = None + con = None + try: + pwd = getCdfPropValue("postgres", True) + # debugTrace("using pwd=%s" % pwd) + con = psycopg2.connect(database = "postgres", user="postgres", password=pwd, host= HOST_NAME) + str = dbGetFirstRowOneValue(con, "select pg_is_in_recovery()") + debugTrace("pg_is_in_recovery() <= %s" % str) + ret = not str + + except psycopg2.DatabaseError as e: + errTrace('Database Error %s' % e) + + except Exception as e: + traceback.print_exc() + errTrace(str(e)) + + finally: + if con is not None: + con.close() + + debugTrace("isMaster(): returning = %s" % ret) + return ret + + def hasRepmgr(self): + """ + Check if the postgresql server is a master by asking the server if it is in recovery (meaning not a master) + """ + ret = None + con = None + try: + pwd = getCdfPropValue("postgres", True) + # debugTrace("using pwd=%s" % pwd) + con = psycopg2.connect(database = "postgres", user="postgres", password=pwd, host= HOST_NAME) + str = dbGetFirstRowOneValue(con, "select * from pg_database where datname = 'repmgr'") + debugTrace("repmgr database check() <= %s" % str) + ret = str + + except psycopg2.DatabaseError as e: + errTrace('Database Error %s' % e) + + except Exception as e: + traceback.print_exc() + errTrace(str(e)) + + finally: + if con is not None: + con.close() + + debugTrace("isMaster(): returning = %s" % ret) + return ret + + def isValidPgHost(self, host): + """ + Check a hostname against the list of nodes stored in the pgnodes CDF configuration file. + """ + pgnodes = getCdfPropValue("pgnodes", "").split('|') + ret = host in pgnodes + debugTrace("isValidPgHost(): looking for host='%s' in pgnodes='%s' => %s" % (host, str(pgnodes), ret)) + return ret + + def checkAuth(self): + """ + HTTP/1.1 401 Unauthorized + Date: Mon, 04 Feb 2014 16:50:53 GMT + WWW-Authenticate: Basic realm="WallyWorld" + + Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + """ + pswd = getCdfPropValue("wgetpswd", True) + b64pswd = base64.b64encode(("pgaas:" + pswd).encode("ascii")) + basicPlusPswd = "Basic %s" % b64pswd.decode("ascii") + + if self.headers['Authorization'] == None: + return False + elif self.headers['Authorization'] == basicPlusPswd: + return True + else: + return False + + def pgStatus(self, *pgargs): + """ return a table(s), using the system database of postgres """ + return self.pgStatusDBx("postgres", *pgargs) + + def pgStatusDB(self, DB, *pgargs): + """ return a table(s), using the given database """ + return self.pgStatusDBx(DB, *pgargs) + + def pgStatusDBx(self, DB, *pgargs): + """ return a table(s), using the given database """ + debugTrace("pgStatusDBx(DB=" + DB + ")") + con = None + ret = None + try: + con = psycopg2.connect(database = DB, user="postgres", password=getCdfPropValue("postgres", True), host= HOST_NAME) + ret = getTableHtmls(con, DB, pgargs) + + except psycopg2.DatabaseError as e: + errTrace('Database Error %s' % e) + + except Exception as e: + traceback.print_exc() + errTrace(str(e)) + + finally: + if con is not None: + con.close() + + return ret + + def do_HEAD(self): + """Respond to a HEAD request.""" + self.doHEADandGET(False) + + def do_GET(self): + """Respond to a GET request.""" + self.doHEADandGET(True) + + def doHEADandGET(self, sendMsg): + resp = 400 + msg = "" + sendBinary = False + contentType = "text/plain" + global debugOn + + if self.path == "/statusall": + self.path = "/all/status/pgstatus" + elif self.path == "/pgstatusall": + self.path = "/pgstatus" + + if self.path == '/ro': + if os.path.isfile("/var/run/postgresql/force-ro-off"): + isrw = "FORCE-RO-OFF" + elif os.path.isfile("/var/run/postgresql/force-ro-on"): + isrw = "Secondary" + else: + isrw = readFile("/var/run/postgresql/isrw", "n/a") + debugTrace("/ro: isrw returns %s" % isrw) + if re.match("Secondary", isrw) or re.match("Master", isrw): + resp = 200 + msg = "server is up" + else: + msg = "server is not up " + isrw + errTrace("/ro: isrw returns %s" % isrw) + + elif self.path == '/rw': + isrw = readFile("/var/run/postgresql/isrw", "n/a") + debugTrace("/rw: isrw returns %s" % isrw) + if re.match("Master", isrw): + resp = 200 + msg = "master server is up" + elif re.match("Secondary", isrw): + msg = "non-master server is up" + else: + msg = "server is not up " + isrw + errTrace("/ro: isrw returns %s" % isrw) + + elif self.path == '/isrw': + isrw = readFile("/var/run/postgresql/isrw", "n/a") + debugTrace("/rw: isrw returns %s" % isrw) + resp = 200 + msg = isrw + + elif not self.checkAuth(): + resp = 401 + msg = "not authenticated" + + elif self.path == '/ismaster': + masterYes = self.isMaster() + msg = "" + if masterYes: + resp = 200 + msg = "master server" + else: + msg = "non-master server" + + elif self.path == '/issecondary': + masterYes = self.isMaster() + msg = "" + if not masterYes: + resp = 200 + msg = "secondary server" + else: + msg = "non-secondary server" + + elif self.path == '/getpubkey': + try: + resp = 200 + msg = readFile("/home/postgres/.ssh/id_rsa.pub") + except: + traceback.print_exc() + resp = 404 + msg = "key does not exist" + + elif re.match("/getssh/", self.path): + # getssh/hostname - push ssh pub/private keys across + host = re.sub("^/getssh/", "", self.path) + debugTrace("#1: /getssh/ host='%s'" % host) + host = re.sub("[^a-zA-Z0-9_.-]", "", host) + debugTrace("#2: /getssh/ host='%s'" % host) + if self.isValidPgHost(host): + p = readPipe("scp -o StrictHostKeyChecking=no ~postgres/.ssh/id_rsa* " + host + ":.ssh/ 2>&1") + debugTrace("#3: /getssh/ to '%s' returns '%s'" % (host, p)) + msg = "OK " + p + resp = 200 + else: + msg = "NOT OK INVALID HOST" + resp = 404 + + elif re.match("/getcdf/", self.path): + # getcdf/hostname - push cdf.cfg file across + fi = "/opt/app/cdf/lib/cdf.cfg" + # make sure that the file exists and contains the encrypted postgres password + if re.search("postgres.x", readFile(fi, "n/a")) and re.search("repmgr.x", readFile(fi, "n/a")): + host = re.sub("^/getcdf/", "", self.path) + debugTrace("#1: /getcdf/ host='%s'" % host) + host = re.sub("[^a-zA-Z0-9_.-]", "", host) + debugTrace("#2: /getcdf/ host='%s'" % host) + if self.isValidPgHost(host): + p = readPipe("scp -o StrictHostKeyChecking=no " + fi + " " + host + ":/opt/app/cdf/lib/cdf.cfg 2>&1") + debugTrace("#3: /getcdf/ to '%s' returns '%s'" % (host, p)) + msg = "OK " + p + resp = 200 + else: + msg = "NOT OK INVALID HOST" + resp = 404 + else: + msg = "NOT OK YET" + resp = 404 + + elif self.path == '/hasrepmgr': + repmgrYes = self.hasRepmgr() + msg = "" + if repmgrYes: + resp = 200 + msg = "OK" + else: + msg = "NOT OK YET" + + elif self.path == '/status': + resp = 200 + contentType = "text/html" + isServerUp = self.isServerUp() + isRepmgrdUp = self.isRepmgrdUp() + isMaster = self.isMaster() + color = "green" if (isServerUp and isRepmgrdUp) else "yellow" if (isServerUp or isRepmgrdUp) else "red" + dashed = "solid" if isMaster else "dashed" + jump = jumpTable("status", "ps", "repmgr", "df", "uptime", "loadavg", "cpuinfo", "meminfo", "pgaas-failures", "pgaas-inst-report", "nslookup", "ip-addr-show", "who-br") + + msg = """<table style='border: 10px %s %s' width='100%%'><tr><td> + <b>isServerUp</b> %s + <b>isRepmgrdUp</b> %s + <b>isMaster</b> %s + <b>isrw</b> %s %s\n<br/> + %s + <h2><a name='status-ps'>ps</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-repmgr'>repmgr cluster show</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-df'>df</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-uptime'>uptime</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2>/proc/uptime%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-loadavg'>loadavg</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-cpuinfo'>cpuinfo</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-meminfo'>meminfo</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-pgaas-failures'>pgaas-failures</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-pgaas-inst-report'>pgaas.inst.report</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-nslookup'>nslookup</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-ip-addr-show'>ip addr</a>%s</h2>\n<pre>\n%s\n</pre>\n + <h2><a name='status-who-br'>who -br</a>%s</h2>\n<pre>\n%s\n</pre>\n + </td></tr></table>""" % (color, dashed, isServerUp, isRepmgrdUp, isMaster, + readFileHtml("/var/run/postgresql/isrw", "n/a"), + readPipeHtml("hostname -f"), jump, + topButton, readPipeHtml("ps -fu postgres"), + topButton, readPipeHtml("/opt/app/pgaas/bin/repmgrc cluster show"), + topButton, readPipeHtml("df -h"), + topButton, readPipeHtml("uptime", defStr="n/a"), + topButton, readFileHtml("/proc/uptime", defStr="n/a"), + topButton, readFileHtml("/proc/loadavg", defStr="n/a"), + topButton, readFileHtml("/proc/cpuinfo", defStr="n/a"), + topButton, readFileHtml("/proc/meminfo", defStr="n/a"), + topButton, readFileHtml("/tmp/pgaas-failures", defStr="n/a"), + topButton, readFileHtml("/tmp/pgaas.inst.report", defStr="n/a"), + topButton, readPipeHtml("nslookup $(hostname -f)", defStr="n/a"), + topButton, readPipeHtml("ip addr show", defStr="n/a"), + topButton, readPipeHtml("who -br", defStr="n/a")) + + elif self.path == '/stoplight': + isServerUp = self.isServerUp() + isRepmgrdUp = self.isRepmgrdUp() + isMaster = self.isMaster() + color = "green" if (isServerUp and isRepmgrdUp) else "yellow" if (isServerUp or isRepmgrdUp) else "red" + masterSecondary = "master" if isMaster else "secondary" + sendBinary = True + contentType = "image/gif" + msg = readFileBinary("/opt/app/postgresql-prep/lib/stoplight-" + masterSecondary + "-" + color + ".gif", "") + + elif re.match("/perdb-", self.path): + # /perdb- + rest = re.sub("^/perdb-", "", self.path) + debugTrace("#1: /perdb- others='%s'" % rest) + rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) + debugTrace("#2: /perdb- rest='%s'" % rest) + pgothers = [ x for x in rest.split('-') if x in validPerDbTables ] + resp = 200 + contentType = "text/html" + con = None + try: + pwd = getCdfPropValue("postgres", True) + con = psycopg2.connect(database = "postgres", user="postgres", password=pwd, host= HOST_NAME) + databases = dbGetFirstColumn(con, "select datname from pg_database") + debugTrace("after select datname from pg_database") + databases[:] = [DB for DB in databases if not re.match("template[0-9]", DB)] + msg = msg + jumpTable("db", *databases) + "<br/>" + for DB in databases: + debugTrace("looking at DB=" + DB) + msg = msg + "<h1><a name='db-" + DB + "'>" + DB + "</a>" + topButton + "</h1>\n" + msg = msg + jumpTable(DB + "-table", *pgothers) + msg = msg + self.pgStatusDB(DB, *pgothers) + + except psycopg2.DatabaseError as e: + errTrace('Database Error %s' % e) + msg = "DB is down" + + except Exception as e: + traceback.print_exc() + errTrace(str(e)) + + finally: + if con is not None: + con.close() + + elif self.path == '/pgstatus': + tables = [ "pg_stat_activity", "pg_stat_archiver", "pg_stat_bgwriter", "pg_stat_database", "pg_stat_database_conflicts", "pg_stat_user_tables", "pg_stat_user_indexes", "pg_statio_user_tables", "pg_statio_user_indexes", "pg_statio_user_sequences", "pg_roles", "pg_database", "pg_tables", "pg_namespace", "pg_roles", "pg_group" ] + header = jumpTable("postgres-table", *tables) + msg = self.pgStatus(*tables) + if msg is not None: + contentType = "text/html" + resp = 200 + msg = header + msg + + elif self.path == '/pg_stat_activity': + msg = self.pgStatus("pg_stat_activity") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_stat_archiver': + msg = self.pgStatus("pg_stat_archiver") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_stat_bgwriter': + msg = self.pgStatus("pg_stat_bgwriter") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_stat_database': + msg = self.pgStatus("pg_stat_database") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_stat_database_conflicts': + msg = self.pgStatus("pg_stat_database_conflicts") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_stat_user_tables': + msg = self.pgStatus("pg_stat_user_tables") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_stat_user_indexes': + msg = self.pgStatus("pg_stat_user_indexes") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_statio_user_tables': + msg = self.pgStatus("pg_statio_user_tables") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_statio_user_indexes': + msg = self.pgStatus("pg_statio_user_indexes") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_statio_user_sequences': + msg = self.pgStatus("pg_statio_user_sequences") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_roles': + msg = self.pgStatus("pg_roles") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_database': + msg = self.pgStatus("pg_database") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_tables': + msg = self.pgStatus("pg_tables") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_namespace': + msg = self.pgStatus("pg_namespace") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif self.path == '/pg_group': + msg = self.pgStatus("pg_group") + if msg is not None: + contentType = "text/html" + resp = 200 + + elif re.match("/all/", self.path) or re.match("/small/", self.path): + if re.match("/small/", self.path): + height = 40 + else: + height = 400 + # /all/others + rest = re.sub("^/all/", "", self.path) + rest = re.sub("^/small/", "", self.path) + rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) + debugTrace("/all/ rest='%s'" % rest) + others = rest.split('/') + try: + resp = 200 + contentType = "text/html" + pgnodes = getCdfPropValue("pgnodes", "").split('|') + msg = msg + jumpTable("node", *pgnodes) + for node in pgnodes: + hnode = html.escape(node) + msg = msg + "<h2><a name='node-" + hnode + "'>" + hnode + "</a>" + topButton + "</h2>\n" + msg = msg + jumpTable(hnode + "-other", *others) + for other in others: + msg = msg + "<h3><a name='" + hnode + "-other-" + other + "'>" + other + "</a>" + topButton + "</h3>\n" + msg = msg + "<iframe src='http://" + hnode + ":" + str(PORT_NUMBER) + "/" + other + "' frameborder='1' scrolling='yes' height='" + str(height) + "' width='1200'></iframe>\n" + except Exception as e: + traceback.print_exc() + errTrace(str(e)) + + + elif self.path == '/debugon': + msg = "ON" + resp = 200 + debugOn = True + + elif self.path == '/debugoff': + msg = "OFF" + resp = 200 + debugOn = False + + elif self.path == '/log' or self.path == '/log/': + msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (self.path, addFilenameHrefs("/log/", readPipeHtml("ls -l /opt/app/log/postgresql/*/*"))) + resp = 200 + contentType = "text/html" + + elif self.path == '/mlog' or self.path == '/mlog/': + # /opt/app/dcae-controller-service-common-vm-manager/logs + msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (self.path, addFilenameHrefs("/mlog/", readPipeHtml("ls -l /opt/app/dcae-controller-service-common-vm-manager/logs/*"))) + resp = 200 + contentType = "text/html" + + elif self.path == '/tmp' or self.path == '/tmp/': + msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (self.path, addFilenameHrefs("/tmp/", readPipeHtml("ls -l /tmp/*"))) + resp = 200 + contentType = "text/html" + + elif re.match("/log/", self.path): + # /log/dir/path + rest = re.sub("^/log/", "", self.path) + debugTrace("#1: /log/ others='%s'" % rest) + rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) + rest = re.sub("/[.][.]/", "", rest) + debugTrace("#2: /log/ rest='%s'" % rest) + msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (rest, ifEmpty(readFileOrGzHtml("/opt/app/log/postgresql/" + rest, "n/a"), "<i>empty</i>")) + resp = 200 + contentType = "text/html" + + elif re.match("/mlog/", self.path): + # /log/dir/path + rest = re.sub("^/mlog/", "", self.path) + debugTrace("#1: /mlog/ others='%s'" % rest) + rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) + rest = re.sub("/[.][.]/", "", rest) + rest = re.sub("^logs/", "", rest) + debugTrace("#2: /mlog/ rest='%s'" % rest) + msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (rest, ifEmpty(readFileOrGzHtml("/opt/app/dcae-controller-service-common-vm-manager/logs/" + rest, "n/a"), "<i>empty</i>")) + resp = 200 + contentType = "text/html" + + elif re.match("/tmp/", self.path): + # /log/dir/path + rest = re.sub("^/tmp/", "", self.path) + debugTrace("#1: /tmp/ others='%s'" % rest) + rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) + rest = re.sub("/[.][.]/", "", rest) + rest = re.sub("^tmp/", "", rest) + debugTrace("#2: /tmp/ rest='%s'" % rest) + msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (rest, ifEmpty(readFileOrGzHtml("/tmp/" + rest, "n/a"), "<i>empty</i>")) + resp = 200 + contentType = "text/html" + + elif self.path == '/oldro': + serverYes = self.isServerUp() + if serverYes: + resp = 200 + msg = "server is up" + else: + msg = "server is not up" + + elif self.path == '/oldrw': + serverYes = self.isServerUp() + masterYes = self.isMaster() + msg = "" + if serverYes: + if masterYes: + resp = 200 + msg = "master server is up" + elif masterYes is not None: + msg = "non-master server is up" + else: + msg = "master status is up, but not answering" + else: + if masterYes: + msg = "status is down, but responded as master server" + elif masterYes is not None: + msg = "status is down, but responded as non-master" + else: + msg = "status is down, server is not up" + + elif self.path == "/help": + resp = 200 + contentType = "text/html" + msg = """<pre> + <a href='/statusall'>statusall</a> == all/status/pgstatus + <a href='/ro'>ro</a> == iDNS readonly + <a href='/rw'>rw</a> == iDNS readwrite + <a href='/isrw'>isrw</a> == /var/run/postgresql/isrw + <a href='/ismaster'>ismaster</a> == is master + <a href='/issecondary'>issecondary</a> == is secondary + <a href='/getpubkey'>getpubkey</a> == retrieve public key + <a href='/hasrepmgr'>hasrepmgr</a> == repmgr id and database are set up + <a href='/status'>status</a> == lots of info + <a href='/perdb-pg_tables-pg_indexes-pg_views'>perdb</a>-{<a href='/perdb-pg_tables'>pg_tables</a>-<a href='/perdb-pg_indexes'>pg_indexes</a>-<a href='/perdb-pg_views'>pg_views</a>} == info per DB + <a href='/pgstatus'>pgstatus</a> == lots of database info + <a href='/pg_stat_activity'>pg_stat_activity</a>, <a href='/pg_stat_archiver'>pg_stat_archiver</a>, <a href='/pg_stat_bgwriter'>pg_stat_bgwriter</a>, + <a href='/pg_stat_database'>pg_stat_database</a>, <a href='/pg_stat_database_conflicts'>pg_stat_database_conflicts</a>, <a href='/pg_stat_user_tables'>pg_stat_user_tables</a>, + <a href='/pg_stat_user_indexes'>pg_stat_user_indexes</a>, <a href='/pg_statio_user_tables'>pg_statio_user_tables</a>, <a href='/pg_statio_user_indexes'>pg_statio_user_indexes</a>, + <a href='/pg_statio_user_sequences'>pg_statio_user_sequences</a>, + <a href='/pg_roles'>pg_roles</a>, <a href='/pg_database'>pg_database</a>, + <a href='/pg_tables'>pg_tables</a>, <a href='/pg_namespace'>pg_namespace</a>, + <a href='/pg_group'>pg_group</a>, + <a href='/swmstatus'>swm proc_out files</a> + <a href='/log'>log</a> == ls -l logs + log/foo == log foo + <a href='/mlog'>mlog</a> == ls -l manager logs + mlog/foo == mlog foo + <a href='/tmp'>tmp</a> == ls -l /tmp + </pre>""" + else: + resp = 404 + msg = "path does not exist" + + # TODO == encode msg when binary + if sendBinary: + debugTrace("%s: Returning %d/%d/%s" % (self.path, resp, len(msg), "--binary--")) + else: + debugTrace("%s: Returning %d/%d/%s" % (self.path, resp, len(msg), msg)) + traceMsg("- %s - \"%s %s %s\" %d %d" % (self.client_address[0], self.command, self.path, self.request_version, resp, len(msg))) + self.send_response(resp) + if resp == 401: + self.send_header('WWW-Authenticate', 'Basic realm="PGaaS"') + self.send_header("Content-type", contentType) + self.end_headers() + if sendMsg: + if msg is None: + msg = "" + if sendBinary: + self.wfile.write(msg) + else: + self.wfile.write((msg + "\n").encode("utf-8")) + sys.stderr.flush() + +""" +database utility functions +""" + +# def dbGetMap(con, cmd, args=[], skipTrace=False): +# def dbGetOneRowMap(con, cmd, args=[], skipTrace=False): + +def dbGetFirstRowOneValue(con, cmd, args=[], skipTrace=False): + """ + Do a select and return a single value from the first row + """ + row = dbGetFirstRow(con, cmd, args, skipTrace) + debugTrace("row=" + str(row)) + if row is not None and len(row) > 0: + return row[0] + return None + +def dbGetFirstRow(con, cmd, args=[], skipTrace=False): + """ + Do a select and return the values from the first row + """ + cursor = dbExecute(con, cmd, args, skipTrace) + return cursor.fetchone() + +def dbGetFirstColumn(con, cmd, args=[], skipTrace=False): + """ + Do a select and return the first column's value from each row + """ + ret = [] + cursor = dbExecute(con, cmd, args, skipTrace) + for row in cursor: + for col in row: + ret.append(col) + break + return ret + +def dbGetFirstColumnAsMap(con, cmd, args=[], skipTrace=False, val=1): + """ + Do a select and return the first column's value from each row + """ + ret = {} + cursor = dbExecute(con, cmd, args, skipTrace) + for row in cursor: + for col in row: + ret[col] = val + break + return ret + +def dumpTable(con, tableName, max=-1): + """ + If being extra verbose, print out the entire table + """ + if verbose < 2: + return + traceOutput = sys.stderr if testOn else openLogFile("/opt/app/log/postgresql/idns/debug.log") + print("================ " + tableName + " ================", file=traceOutput) + + cols = dbGetFirstColumn(con, "select column_name from information_schema.columns where table_name='" + tableName + "'", skipTrace=True) + print("num", end="|", file=traceOutput) + for col in cols: + print(col, end="|", file=traceOutput) + print("", file=traceOutput) + + if max > -1: + cursor = dbExecute(con, "select * from " + tableName + " limit " + str(max), skipTrace=True) + else: + cursor = dbExecute(con, "select * from " + tableName, skipTrace=True) + i = 0 + for row in cursor: + print("%d" % i, end="|", file=traceOutput) + i += 1 + for col in row: + print("%s" % (col), end="|", file=traceOutput) + print("", file=traceOutput) + print("================================================", file=traceOutput) + +def getTableHtmls(con, DB, tableNames): + """ + Retrieve a dump of all specified tables, in HTML format + """ + ret = "" + for tn in tableNames: + ret = ret + getTableHtml(con, DB, tn) + return ret + +def getTableHtml(con, DB, tableName, max=-1): + """ + Retrieve a dump of a given table, in HTML format + """ + # errTrace("getting %s" % str(tableName)) + ret = "<h2><a name='" + DB + "-table-" + tableName + "'>" + DB + " " + tableName + "</a>" + topButton + "</h2>\n" + ret = ret + "<table border='1'>\n" + # ret = ret + "<tr><th colspan='" + str(len(cols)+1) + "'>" + tableName + "</th></tr>\n" + cols = dbGetFirstColumn(con, "select column_name from information_schema.columns where table_name='" + tableName + "'", skipTrace=True) + + ret = ret + "<tr><th>num</th>" + for col in cols: + ret = ret + "<th>" + str(col) + "</th>" + ret = ret + "</tr>\n" + + if max > -1: + cursor = dbExecute(con, "select * from " + tableName + " limit " + str(max), skipTrace=True) + else: + cursor = dbExecute(con, "select * from " + tableName, skipTrace=True) + i = 0 + for row in cursor: + ret = ret + "<tr><th>" + str(i) + "</th>" + i = i + 1 + for col in row: + ret = ret + "<td>" + str(col) + "</td>" + ret = ret + "</tr>\n" + ret = ret + "</table>\n" + return ret + +def dbExecute(con, statement, args=[], skipTrace=False): + """ + Create a cursor, instantiate the arguments into a statement, trace print the statement, and execute the statement. + Return the cursor + """ + cursor = con.cursor() + stmt = cursor.mogrify(statement, args); + if not skipTrace: + debugTrace("executing:" + str(stmt)) + cursor.execute(stmt) + if not skipTrace: + debugTrace("statusmessage=" + cursor.statusmessage + ", rowcount=" + str(cursor.rowcount)) + return cursor + +if __name__ == '__main__': + server_class = http.server.HTTPServer + httpd = server_class(("0.0.0.0", PORT_NUMBER), MyHandler) + errTrace("Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)) + try: + httpd.serve_forever() + except KeyboardInterrupt: + pass + httpd.server_close() + errTrace("Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)) diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/makefile b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/makefile new file mode 100644 index 0000000..2a94789 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/makefile @@ -0,0 +1,22 @@ +all: + +NODES="uiopmno5qwpstg01.grant.example.com|uiopmno5qwpstg00.grant.example.com|uiopmno6qwpstg00.grant.example.com|uiopmno6qwpstg01.grant.example.com" + +test: + ./gen-repmgr-info -n $(NODES) + ./gen-repmgr-info -S -L -n $(NODES) + ./gen-repmgr-info -s uiopmno6qwpstg -n $(NODES) + ./gen-repmgr-info -l uiopmno6qwpstg01.grant.example.com -n $(NODES) + ./gen-repmgr-info -c uiopmno5qwpstg00.grant.example.com -n $(NODES) + ./gen-repmgr-info -c uiopmno5qwpstg01.grant.example.com -n $(NODES) + ./gen-repmgr-info -c uiopmno6qwpstg00.grant.example.com -n $(NODES) + ./gen-repmgr-info -c uiopmno6qwpstg01.grant.example.com -n $(NODES) + ./gen-repmgr-info -e uiopmno5qwpstg01.grant.example.com -n $(NODES) + ./gen-repmgr-info -e uiopmno6qwpstg01.grant.example.com -n $(NODES) + ./gen-repmgr-info -m -n $(NODES) + ./gen-repmgr-info -C uiopmno5qwpstg00.grant.example.com -n $(NODES) + ./gen-repmgr-info -C uiopmno5qwpstg01.grant.example.com -n $(NODES) + ./gen-repmgr-info -C uiopmno6qwpstg00.grant.example.com -n $(NODES) + ./gen-repmgr-info -C uiopmno6qwpstg01.grant.example.com -n $(NODES) + ./gen-repmgr-info -p -n $(NODES) + diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/pgwget b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/pgwget new file mode 100644 index 0000000..d1d5f98 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/pgwget @@ -0,0 +1,4 @@ +# this command is used to access the iDNS status server running on a PGaaS instance + +wgetpswd=`/opt/app/cdf/bin/getpropvalue -x -n wgetpswd` +wget --http-user=pgaas --http-password=$wgetpswd "$@" diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/repmgrd-status-changes b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/repmgrd-status-changes new file mode 100644 index 0000000..083b96f --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/bin/repmgrd-status-changes @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# %n - node ID +# %e - event type +# %s - success (1 or 0) +# %t - timestamp +# %d - details + +nodeID="$1" +eventType="$2" +success="$3" +timestamp="$4" +details="$5" + +LOG=/opt/app/log/postgresql/server/repmgrstatus.log +PROMOTIONLOG=/var/run/postgresql/repmgr-promotion +echo `date` "$@" >> $LOG + +# The following event types are available: +# master_register +# standby_register +# standby_unregister +# standby_clone +# standby_promote +# standby_follow +# standby_switchover +# standby_disconnect_manual +# witness_create +# witness_register +# witness_unregister +# repmgrd_start +# repmgrd_shutdown +# repmgrd_failover_promote +# repmgrd_failover_follow + +case "$eventType" in + standby_promote ) + if [ "$success" -eq 1 ] + then echo $(date +%Y%m%d%H%M%S) "$@" >> $PROMOTIONLOG + fi +esac diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/init/init-pgaas-idns.conf b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/init-pgaas-idns.conf new file mode 100644 index 0000000..8ed39fb --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/init-pgaas-idns.conf @@ -0,0 +1,21 @@ +# PGaaS - PostgreSQL as a Service +# +# The PGaaS iDNS server provides information on the system, primarily for the iDNS system + +description "PGaaS iDNS server" + +start on runlevel [2345] +stop on runlevel [!2345] + +respawn +respawn limit 10 5 +umask 022 +setuid postgres + +pre-start script + test -x /opt/app/postgresql-prep/bin/iDNS-responder.py || { stop; exit 0; } +end script + +script + /opt/app/postgresql-prep/bin/iDNS-responder.py +end script diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/init/init-pgaas-init.conf b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/init-pgaas-init.conf new file mode 100644 index 0000000..4564129 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/init-pgaas-init.conf @@ -0,0 +1,24 @@ +# PGaaS - PostgreSQL as a Service +# +# The PGaaS init process needs a directory to be created on reboot +# +# The best way to do this is to have a file in /usr/lib/tmpfiles.d/pgaas: +# d /var/run/postgresql 0755 postgres postgres - +# +# This is a workaround because systemd-tmpfiles is not present. + +description "PGaaS init setup" + +start on runlevel [2345] +stop on runlevel [!2345] + +umask 022 + +pre-start script + mkdir /var/run/postgresql + chown postgres:postgres /var/run/postgresql +end script + +script + : +end script diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/init/logrotate b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/logrotate new file mode 100644 index 0000000..aaa5e35 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/logrotate @@ -0,0 +1,24 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +# rotate PGaaS log files + +/opt/app/log/postgresql/init/*.log /opt/app/log/postgresql/idns/*.log /opt/app/log/postgresql/server/*.log { + missingok + compress + daily + rotate 60 + create + dateext +} diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/init/pglogs.cron b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/pglogs.cron new file mode 100644 index 0000000..e905633 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/pglogs.cron @@ -0,0 +1,4 @@ +30 * * * * ( date ; find /dbroot/pglogs -type f -mtime +3 -exec rm {} + ) >> /tmp/cleanpglogs.log 2>&1 +50 1 * * * ( mv -f /tmp/cleanpglogs.log /tmp/cleanpglogs.log.old ) > /dev/null 2>&1 +* * * * * [ -x /opt/app/pgaas/bin/update_var_run_isrw ] && /opt/app/pgaas/bin/update_var_run_isrw +* * * * * [ -x /opt/app/pgaas/bin/check_cluster ] && /opt/app/pgaas/bin/check_cluster >> /opt/app/log/postgresql/idns/cluster.log diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/init/systemd-pgaas-idns.service b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/systemd-pgaas-idns.service new file mode 100644 index 0000000..bbc28ea --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/systemd-pgaas-idns.service @@ -0,0 +1,12 @@ +# PGaaS - PostgreSQL as a Service +# +# The PGaaS iDNS server provides information on the system, primarily for the iDNS system + +[Unit] +Description=PGaaS iDNS server + +[Service] +ExecStart= /opt/app/postgresql-prep/bin/iDNS-responder.py + +[Install] +WantedBy=multi-user.target diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/init/tmpfiles-pgaas-init.conf b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/tmpfiles-pgaas-init.conf new file mode 100644 index 0000000..9341a95 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/init/tmpfiles-pgaas-init.conf @@ -0,0 +1,7 @@ +# PGaaS - PostgreSQL as a Service +# +# The PGaaS init process needs a directory to be created on reboot +# + +d /var/run/postgresql 0755 postgres postgres - + diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/green-flasher-12x10.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/green-flasher-12x10.gif Binary files differnew file mode 100644 index 0000000..c6d33b5 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/green-flasher-12x10.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/grey-flasher-12x10.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/grey-flasher-12x10.gif Binary files differnew file mode 100644 index 0000000..dfa81a2 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/grey-flasher-12x10.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/red-flasher-12x10.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/red-flasher-12x10.gif Binary files differnew file mode 100644 index 0000000..ab16a81 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/red-flasher-12x10.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-G-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-G-12x25.gif Binary files differnew file mode 100644 index 0000000..0ce2785 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-G-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-R-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-R-12x25.gif Binary files differnew file mode 100644 index 0000000..0750594 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-R-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-Y-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-Y-12x25.gif Binary files differnew file mode 100644 index 0000000..4f95bf0 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-Y-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-g-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-g-12x25.gif Binary files differnew file mode 100644 index 0000000..0ce2785 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-g-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-g2-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-g2-12x25.gif Binary files differnew file mode 100644 index 0000000..1be7fa1 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-g2-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-green.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-green.gif Binary files differnew file mode 100644 index 0000000..1be7fa1 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-green.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-red.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-red.gif Binary files differnew file mode 100644 index 0000000..daa6960 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-red.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-yellow.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-yellow.gif Binary files differnew file mode 100644 index 0000000..1bd8731 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-master-yellow.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r-12x25.gif Binary files differnew file mode 100644 index 0000000..0750594 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r2-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r2-12x25.gif Binary files differnew file mode 100644 index 0000000..ff26986 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r2-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r3-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r3-12x25.gif Binary files differnew file mode 100644 index 0000000..daa6960 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-r3-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-green.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-green.gif Binary files differnew file mode 100644 index 0000000..c6d33b5 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-green.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-red.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-red.gif Binary files differnew file mode 100644 index 0000000..ab16a81 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-red.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-yellow.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-yellow.gif Binary files differnew file mode 100644 index 0000000..5e16750 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-secondary-yellow.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y-12x25.gif Binary files differnew file mode 100644 index 0000000..4f95bf0 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y2-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y2-12x25.gif Binary files differnew file mode 100644 index 0000000..886e548 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y2-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y3-12x25.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y3-12x25.gif Binary files differnew file mode 100644 index 0000000..1bd8731 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/stoplight-y3-12x25.gif diff --git a/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/yellow-flasher-12x10.gif b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/yellow-flasher-12x10.gif Binary files differnew file mode 100644 index 0000000..5e16750 --- /dev/null +++ b/postgresql-prep/src/stage/opt/app/postgresql-prep/lib/yellow-flasher-12x10.gif diff --git a/postgresql-prep/src/testlock/.gitignore b/postgresql-prep/src/testlock/.gitignore new file mode 100644 index 0000000..0618485 --- /dev/null +++ b/postgresql-prep/src/testlock/.gitignore @@ -0,0 +1 @@ +testlock diff --git a/postgresql-prep/src/testlock/makefile b/postgresql-prep/src/testlock/makefile new file mode 100644 index 0000000..c3bbb7a --- /dev/null +++ b/postgresql-prep/src/testlock/makefile @@ -0,0 +1,56 @@ +# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this code except in compliance +# with the License. You may obtain a copy of the License +# at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + + +all: testlock + +TESTLOCK=./testlock + +ttestlock: testlock + @echo;echo should print usage list + -$(TESTLOCK) + @echo;echo should print usage list + -$(TESTLOCK) -? + @echo;echo should print missing lock filename + -$(TESTLOCK) -t 0 + @echo;echo should print missing command + -$(TESTLOCK) -t 0 /var/tmp/tl + @echo;echo should run immediately + $(TESTLOCK) -t 0 /var/tmp/tl /bin/echo hello + @echo;echo grab lock, done SHOULD run after lock becomes available + date;$(TESTLOCK) /var/tmp/tl sleep 5 & sleep 1; $(TESTLOCK) /var/tmp/tl /bin/echo done;date + $(TESTLOCK) /var/tmp/tl true # cleanup + @echo;echo grab lock, not waiting should NOT run + date;$(TESTLOCK) /var/tmp/tl sleep 5 & sleep 1; $(TESTLOCK) -t 0 /var/tmp/tl /bin/echo not waiting;date + $(TESTLOCK) /var/tmp/tl true # cleanup + @echo;echo grab lock, echo should NOT run because lock does not become available in 4 seconds + date;$(TESTLOCK) /var/tmp/tl sleep 5 & sleep 1; $(TESTLOCK) -t 3 /var/tmp/tl /bin/echo waiting up to 3 seconds;date + $(TESTLOCK) /var/tmp/tl true # cleanup + @echo;echo grab lock, echo should SILENTLY NOT run because lock does not become available in 4 seconds + date;$(TESTLOCK) /var/tmp/tl sleep 5 & sleep 1; $(TESTLOCK) -t 3 -s /var/tmp/tl /bin/echo waiting up to 3 seconds;date + $(TESTLOCK) /var/tmp/tl true # cleanup + @echo;echo grab lock, echo SHOULD run after lock becomes available in 5 seconds + date;$(TESTLOCK) /var/tmp/tl sleep 5 & sleep 1; $(TESTLOCK) -t 10 /var/tmp/tl /bin/echo waiting up to 10 seconds;date + $(TESTLOCK) /var/tmp/tl true # cleanup + +testlock: testlock.c + gcc -o testlock testlock.c + +clean: + rm -f *~ + +clobber: clean + rm -f testlock + +stage: testlock + cp -p testlock ../postgresql-prep/Linux/dist_files/opt/app/postgresql-prep/bin/ diff --git a/postgresql-prep/src/testlock/testlock.1 b/postgresql-prep/src/testlock/testlock.1 new file mode 100644 index 0000000..12dbb9d --- /dev/null +++ b/postgresql-prep/src/testlock/testlock.1 @@ -0,0 +1,38 @@ +'\" Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +'\" +'\" Licensed under the Apache License, Version 2.0 (the "License"); +'\" you may not use this code except in compliance +'\" with the License. You may obtain a copy of the License +'\" at http://www.apache.org/licenses/LICENSE-2.0 +'\" +'\" Unless required by applicable law or agreed to in writing, software +'\" distributed under the License is distributed on an "AS IS" BASIS, +'\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +'\" implied. See the License for the specific language governing +'\" permissions and limitations under the License. +.TH testlock 1 "April 26 2006" "" "" +.SH NAME +testlock \- lock a file and run a command with the lock held +.SH SYNOPSIS +testlock [-v] [-t timeout] [-s] [-r exittcode] filename command args ... +.SH DESCRIPTION +.PP +Testlock will acquire a file lock and then execute a command while the lock is held. +If no timeout is provided, testlock will wait indefinitely until the file can be locked, +and then execute the command. +If a timeout is given, it will stop waiting after that many seconds have passed. +.SS Options +.IP -t +Abort if the lock cannot be acquired after +.I timeout +seconds. +If +.I timeout +is 0, the lock will be totally non-blocking. +.IP -s +Silently ignore errors with locking. +(Other errors will still be reported.) +.IP -r exitcode +If the lock cannot be acquired, use this exit code instead of the default exit code of 99. +.SH AUTHOR +Tony Hansen. diff --git a/postgresql-prep/src/testlock/testlock.c b/postgresql-prep/src/testlock/testlock.c new file mode 100644 index 0000000..41bea59 --- /dev/null +++ b/postgresql-prep/src/testlock/testlock.c @@ -0,0 +1,110 @@ +/* + Usage: testlock [-t timeout] [-s] filename command args ... + + Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this code except in compliance + with the License. You may obtain a copy of the License + at http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +*/ + +#include <sys/file.h> +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> + +void usage(const char *prog0, const char *msg) +{ + if (msg) fprintf(stderr, "%s\n", msg); + fprintf(stderr, "Usage: %s [-v] [-t timeout] [-s] [-r retcode] lock-filename command args ...\n", prog0); + fprintf(stderr, "-t ##\thow long to wait for a lock to be freed. 0 means exit immediately\n"); + fprintf(stderr, "-s\tsilently ignore errors with locking\n"); + fprintf(stderr, "-r ##\texit with this code when the lock fails\n"); + fprintf(stderr, "-v\tbe verbose\n"); + fprintf(stderr, "Note:\tlock-filename is created (if it does not exist) and truncated before locking.\n"); + fprintf(stderr, "\tlock-filename is not removed after the command finishes\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + const char *prog0 = argv[0]; + + int c; + int timeout = -1; + bool silent = false; + bool verbose = false; + int nolockret = 99; + + while ((c = getopt(argc, argv, "t:r:sv?")) != -1) { + switch (c) { + case 's': silent = true; break; + case 't': timeout = atoi(optarg); break; + case 'r': nolockret = atoi(optarg); break; + case 'v': verbose = true; break; + default: usage(prog0, NULL); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(prog0, "Missing lock filename"); + } else if (argc < 2) { + usage(prog0, "Missing command to run"); + } + + const char *lockFilename = *argv++; + if (verbose) printf("lockfilename=%s\n", lockFilename); + + int lockfd = creat(lockFilename, 0666); + if (lockfd < 0) { + fprintf(stderr, "Cannot open %s: %s\n", lockFilename, strerror(errno)); + exit(2); + } + + if (timeout < 0) { + /* wait forever */ + lockf(lockfd, F_LOCK, 0); + } else { + /* try each second (for up to timeout seconds) to get the lock */ + int lockret = lockf(lockfd, F_TLOCK, 0); + int count = 0; + while ((lockret < 0) && (count++ < timeout)) { + sleep(1); + lockret = lockf(lockfd, F_TLOCK, 0); + } + if (lockret < 0) { + if (!silent) { + fprintf(stderr, "Cannot lock %s: %s\n", lockFilename, strerror(errno)); + } + exit(nolockret); + } + } + + /* now execute the given command */ + if (verbose) { + char **a = argv; + printf("calling program '%s'\n", *a); + while (*++a) { + printf("with argument '%s'\n", *a); + } + } + execvp(argv[0], argv); +} diff --git a/postgresql-prep/src/testlock/testlock.md b/postgresql-prep/src/testlock/testlock.md new file mode 100644 index 0000000..8ec2a48 --- /dev/null +++ b/postgresql-prep/src/testlock/testlock.md @@ -0,0 +1,27 @@ +# testlock 1 "April 26 2006" "" "" +## NAME +testlock \- lock a file and run a command with the lock held +## SYNOPSIS +testlock [-v] [-t timeout] [-s] [-r exittcode] filename command args ... +## DESCRIPTION + +Testlock will acquire a file lock and then execute a command while the lock is held. +If no timeout is provided, testlock will wait indefinitely until the file can be locked, +and then execute the command. +If a timeout is given, it will stop waiting after that many seconds have passed. + +### Options + +-t +Abort if the lock cannot be acquired after _timeout_ seconds. +If _timeout_ is 0, the lock will be totally non-blocking. + +-s +Silently ignore errors with locking. +(Other errors will still be reported.) + +-r exitcode +If the lock cannot be acquired, use this exit code instead of the default exit code of 99. + +## AUTHOR +Tony Hansen. |