diff --git a/packaging/systemd/cloudstack-usage.default b/packaging/systemd/cloudstack-usage.default index 26f552859f37..77483d0411f1 100644 --- a/packaging/systemd/cloudstack-usage.default +++ b/packaging/systemd/cloudstack-usage.default @@ -15,8 +15,18 @@ # specific language governing permissions and limitations # under the License. -JAVA_OPTS="-Dpid=$$ -Xms256m -Xmx2048m" +JAVA_OPTS="-Xms256m -Xmx2048m" CLASSPATH="/usr/share/cloudstack-usage/*:/usr/share/cloudstack-usage/lib/*:/usr/share/cloudstack-mysql-ha/lib/*:/etc/cloudstack/usage:/usr/share/java/mysql-connector-java.jar" JAVA_CLASS=com.cloud.usage.UsageServer + +################################################################################################ +#You can uncomment one of these options if you want to enable Java remote debugging. # +#You can change the parameters at your will. The 'address' field defines the port to be used. # +################################################################################################ +# This option here should be used with 'systemmd' based operating systems such as CentOS7, Ubuntu 16, and so on. +#JAVA_DEBUG="-agentlib:jdwp=transport=dt_socket,address=8001,server=y,suspend=n" + +# On the other hand, this option is used by CentOS6. +#JAVA_DEBUG="-Xrunjdwp:transport=dt_socket,address=8001,server=y,suspend=n" diff --git a/packaging/systemd/cloudstack-usage.service b/packaging/systemd/cloudstack-usage.service index f8874867c69a..f7313a8fc139 100644 --- a/packaging/systemd/cloudstack-usage.service +++ b/packaging/systemd/cloudstack-usage.service @@ -24,7 +24,9 @@ After=network.target network-online.target [Service] Type=simple EnvironmentFile=/etc/default/cloudstack-usage -ExecStart=/usr/bin/java $JAVA_OPTS -cp $CLASSPATH $JAVA_CLASS +ExecStart=/usr/bin/java $JAVA_DEBUG $JAVA_OPTS -cp $CLASSPATH $JAVA_CLASS +ExecStartPost=/bin/sh -c "systemctl show -p MainPID cloudstack-usage.service 2>/dev/null | cut -d= -f2 > /var/run/cloudstack-usage.service.pid" + Restart=always RestartSec=10s diff --git a/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java b/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java index d70910249b2f..ed32a0315772 100644 --- a/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java +++ b/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java @@ -16,7 +16,10 @@ // under the License. package com.cloud.usage; +import java.io.File; +import java.io.IOException; import java.net.InetAddress; +import java.nio.charset.Charset; import java.sql.SQLException; import java.util.ArrayList; import java.util.Calendar; @@ -33,15 +36,17 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.quota.QuotaAlertManager; import org.apache.cloudstack.quota.QuotaManager; import org.apache.cloudstack.quota.QuotaStatement; +import org.apache.cloudstack.usage.UsageTypes; import org.apache.cloudstack.utils.usage.UsageUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.managed.context.ManagedContextRunnable; -import org.apache.cloudstack.usage.UsageTypes; import com.cloud.alert.AlertManager; import com.cloud.event.EventTypes; @@ -57,10 +62,10 @@ import com.cloud.usage.dao.UsageNetworkOfferingDao; import com.cloud.usage.dao.UsagePortForwardingRuleDao; import com.cloud.usage.dao.UsageSecurityGroupDao; -import com.cloud.usage.dao.UsageVMSnapshotOnPrimaryDao; import com.cloud.usage.dao.UsageStorageDao; import com.cloud.usage.dao.UsageVMInstanceDao; import com.cloud.usage.dao.UsageVMSnapshotDao; +import com.cloud.usage.dao.UsageVMSnapshotOnPrimaryDao; import com.cloud.usage.dao.UsageVPNUserDao; import com.cloud.usage.dao.UsageVmDiskDao; import com.cloud.usage.dao.UsageVolumeDao; @@ -72,11 +77,11 @@ import com.cloud.usage.parser.SecurityGroupUsageParser; import com.cloud.usage.parser.StorageUsageParser; import com.cloud.usage.parser.VMInstanceUsageParser; +import com.cloud.usage.parser.VMSanpshotOnPrimaryParser; import com.cloud.usage.parser.VMSnapshotUsageParser; import com.cloud.usage.parser.VPNUserUsageParser; import com.cloud.usage.parser.VmDiskUsageParser; import com.cloud.usage.parser.VolumeUsageParser; -import com.cloud.usage.parser.VMSanpshotOnPrimaryParser; import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.UserStatisticsVO; @@ -92,6 +97,7 @@ import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; @Component public class UsageManagerImpl extends ManagerBase implements UsageManager, Runnable { @@ -176,6 +182,11 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna private Future _sanity = null; private boolean usageSnapshotSelection = false; + /** + * File "/var/run/cloudstack-usage.service.pid" that stores a string corresponding to the cloudstack-usage.service process identified (PID). + */ + private static final String CLOUDSTACK_USAGE_SERVER_SERVICE_PID_FILE = "/var/run/cloudstack-usage.service.pid"; + public UsageManagerImpl() { } @@ -271,10 +282,42 @@ public boolean configure(String name, Map params) throws Configu s_logger.error("Unhandled exception configuring UsageManger", e); throw new ConfigurationException("Unhandled exception configuring UsageManager " + e.toString()); } - _pid = Integer.parseInt(System.getProperty("pid")); + + _pid = retrieveUsageManagerServicePid(); return true; } + /** + * Retrieves the cloudstack-usage.service PID according to the file /var/run/cloudstack-usage.service.pid.
+ * It throws a CloudRuntimeException in the following cases: + * + */ + protected int retrieveUsageManagerServicePid() { + File usageServicePid = new File(CLOUDSTACK_USAGE_SERVER_SERVICE_PID_FILE); + if (!usageServicePid.exists()) { + throw new CloudRuntimeException(String.format("Cannot find file [%s].", CLOUDSTACK_USAGE_SERVER_SERVICE_PID_FILE)); + } + if (!usageServicePid.canRead()) { + throw new CloudRuntimeException(String.format("Cannot read file [%s].", CLOUDSTACK_USAGE_SERVER_SERVICE_PID_FILE)); + } + try { + String pidAsString = FileUtils.readFileToString(usageServicePid, Charset.defaultCharset()); + String pidAsStringWithoutLineSeparator = pidAsString.trim(); + int pid = NumberUtils.toInt(pidAsStringWithoutLineSeparator); + if (pid == 0) { + throw new CloudRuntimeException( + String.format("Failed to retrieve a valid cloudstack-usage.service pid [file:%s, pid at file:%s]", CLOUDSTACK_USAGE_SERVER_SERVICE_PID_FILE, pidAsString)); + } + return pid; + } catch (IOException e) { + throw new CloudRuntimeException(e); + } + } + @Override public boolean start() { if (s_logger.isInfoEnabled()) { diff --git a/usage/src/main/java/com/cloud/usage/UsageServer.java b/usage/src/main/java/com/cloud/usage/UsageServer.java index df3f374cc308..5e8a69d17cb2 100644 --- a/usage/src/main/java/com/cloud/usage/UsageServer.java +++ b/usage/src/main/java/com/cloud/usage/UsageServer.java @@ -51,11 +51,7 @@ public void start() { appContext = new ClassPathXmlApplicationContext("usageApplicationContext.xml"); - try { - ComponentContext.initComponentsLifeCycle(); - } catch (Exception e) { - e.printStackTrace(); - } + ComponentContext.initComponentsLifeCycle(); mgr = appContext.getBean(UsageManager.class); diff --git a/usage/src/test/java/com/cloud/usage/UsageManagerImplTest.java b/usage/src/test/java/com/cloud/usage/UsageManagerImplTest.java new file mode 100644 index 000000000000..07b09d2c78fc --- /dev/null +++ b/usage/src/test/java/com/cloud/usage/UsageManagerImplTest.java @@ -0,0 +1,84 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// 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 com.cloud.usage; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({FileUtils.class, UsageManagerImpl.class, NumberUtils.class}) +public class UsageManagerImplTest { + + private static final String CLOUDSTACK_USAGE_SERVER_SERVICE_PID_FILE = "/var/run/cloudstack-usage.service.pid"; + private UsageManagerImpl usageManagerImpl = new UsageManagerImpl(); + + @Test + public void retrieveUsageManagerServicePidTestCompleteExecutionFlow() throws Exception { + prepareAndVerifyTestRetrieveUsageManagerServicePidTest(true, true, 1, 1234, "1234" + System.getProperty("line.separator")); + } + + @Test(expected=CloudRuntimeException.class) + public void retrieveUsageManagerServicePidTestFileDoesNotExist() throws Exception { + prepareAndVerifyTestRetrieveUsageManagerServicePidTest(false, true, 0, 1234, "1234" + System.getProperty("line.separator")); + } + + @Test(expected = CloudRuntimeException.class) + public void retrieveUsageManagerServicePidTestCannotReadFile() throws Exception { + prepareAndVerifyTestRetrieveUsageManagerServicePidTest(true, false, 1, 1234, "1234" + System.getProperty("line.separator")); + } + + @Test(expected = CloudRuntimeException.class) + public void retrieveUsageManagerServicePidTestPidEqualsToZero() throws Exception { + prepareAndVerifyTestRetrieveUsageManagerServicePidTest(true, true, 1, 0, "abc"); + } + + private void prepareAndVerifyTestRetrieveUsageManagerServicePidTest(boolean fileExists, boolean canReadFile, int timesCanReadIsExecuted, int expectedPid, + String readedStringFromFile) + throws Exception, IOException { + File usageServicePid = Mockito.mock(File.class); + PowerMockito.whenNew(File.class).withArguments(CLOUDSTACK_USAGE_SERVER_SERVICE_PID_FILE).thenReturn(usageServicePid); + + Mockito.when(usageServicePid.exists()).thenReturn(fileExists); + Mockito.when(usageServicePid.canRead()).thenReturn(canReadFile); + + PowerMockito.mockStatic(FileUtils.class); + Mockito.when(FileUtils.readFileToString(usageServicePid, Charset.defaultCharset())).thenReturn(readedStringFromFile); + + int result = usageManagerImpl.retrieveUsageManagerServicePid(); + + InOrder inOrder = Mockito.inOrder(usageServicePid); + inOrder.verify(usageServicePid).exists(); + inOrder.verify(usageServicePid, Mockito.times(timesCanReadIsExecuted)).canRead(); + + Assert.assertEquals(expectedPid, result); + } + +} diff --git a/usage/src/test/java/com/cloud/usage/UsageManagerTest.java b/usage/src/test/java/com/cloud/usage/UsageManagerTest.java deleted file mode 100644 index 139b65d42dba..000000000000 --- a/usage/src/test/java/com/cloud/usage/UsageManagerTest.java +++ /dev/null @@ -1,113 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you 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. -package com.cloud.usage; - -import java.util.Date; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import junit.framework.TestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.cloud.usage.parser.IPAddressUsageParser; -import com.cloud.usage.parser.LoadBalancerUsageParser; -import com.cloud.usage.parser.NetworkOfferingUsageParser; -import com.cloud.usage.parser.NetworkUsageParser; -import com.cloud.usage.parser.PortForwardingUsageParser; -import com.cloud.usage.parser.SecurityGroupUsageParser; -import com.cloud.usage.parser.StorageUsageParser; -import com.cloud.usage.parser.VMInstanceUsageParser; -import com.cloud.usage.parser.VPNUserUsageParser; -import com.cloud.usage.parser.VmDiskUsageParser; -import com.cloud.usage.parser.VolumeUsageParser; -import com.cloud.user.AccountVO; -import com.cloud.utils.component.ComponentContext; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "classpath:/UsageManagerTestContext.xml") -public class UsageManagerTest extends TestCase { - @Inject - UsageManagerImpl _usageMgr = null; - @Inject - VMInstanceUsageParser vmParser = null; - @Inject - IPAddressUsageParser ipParser = null; - @Inject - LoadBalancerUsageParser lbParser = null; - @Inject - NetworkOfferingUsageParser noParser = null; - @Inject - NetworkUsageParser netParser = null; - @Inject - VmDiskUsageParser vmdiskParser = null; - @Inject - PortForwardingUsageParser pfParser = null; - @Inject - SecurityGroupUsageParser sgParser = null; - @Inject - StorageUsageParser stParser = null; - @Inject - VolumeUsageParser volParser = null; - @Inject - VPNUserUsageParser vpnParser = null; - - Date startDate = null; - Date endDate = null; - - @Before - public void setup() throws Exception { - System.setProperty("pid", "5678"); - ComponentContext.initComponentsLifeCycle(); - startDate = new Date(); - endDate = new Date(100000L + System.currentTimeMillis()); - } - - @Test - public void testParse() throws ConfigurationException { - UsageJobVO job = new UsageJobVO(); - _usageMgr.parse(job, System.currentTimeMillis(), 100000L + System.currentTimeMillis()); - } - - @Test - public void testSchedule() throws ConfigurationException { - _usageMgr.scheduleParse(); - } - - @Test - public void testParsers() throws ConfigurationException { - AccountVO account = new AccountVO(); - account.setId(2L); - VMInstanceUsageParser.parse(account, startDate, endDate); - IPAddressUsageParser.parse(account, startDate, endDate); - LoadBalancerUsageParser.parse(account, startDate, endDate); - NetworkOfferingUsageParser.parse(account, startDate, endDate); - NetworkUsageParser.parse(account, startDate, endDate); - VmDiskUsageParser.parse(account, startDate, endDate); - PortForwardingUsageParser.parse(account, startDate, endDate); - SecurityGroupUsageParser.parse(account, startDate, endDate); - StorageUsageParser.parse(account, startDate, endDate); - VolumeUsageParser.parse(account, startDate, endDate); - VPNUserUsageParser.parse(account, startDate, endDate); - } - -} diff --git a/usage/src/test/resources/UsageManagerTestContext.xml b/usage/src/test/resources/UsageManagerTestContext.xml deleted file mode 100644 index 152de68a5802..000000000000 --- a/usage/src/test/resources/UsageManagerTestContext.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -