4242import org .apache .cloudstack .agent .directdownload .SetupDirectDownloadCertificate ;
4343import org .apache .cloudstack .agent .lb .SetupMSListAnswer ;
4444import org .apache .cloudstack .agent .lb .SetupMSListCommand ;
45+ import org .apache .cloudstack .ca .PostCertificateRenewalCommand ;
4546import org .apache .cloudstack .ca .SetupCertificateAnswer ;
4647import org .apache .cloudstack .ca .SetupCertificateCommand ;
4748import org .apache .cloudstack .ca .SetupKeyStoreCommand ;
6869import com .cloud .agent .transport .Request ;
6970import com .cloud .agent .transport .Response ;
7071import com .cloud .exception .AgentControlChannelException ;
72+ import com .cloud .host .Host ;
7173import com .cloud .resource .ServerResource ;
7274import com .cloud .utils .PropertiesUtil ;
7375import com .cloud .utils .StringUtils ;
@@ -127,6 +129,7 @@ public int value() {
127129 Long _id ;
128130
129131 Timer _timer = new Timer ("Agent Timer" );
132+ Timer certTimer ;
130133 Timer hostLBTimer ;
131134
132135 List <WatchTask > _watchList = new ArrayList <WatchTask >();
@@ -140,9 +143,11 @@ public int value() {
140143 long _startupWait = _startupWaitDefault ;
141144 boolean _reconnectAllowed = true ;
142145 //For time sentitive task, e.g. PingTask
143- private final ThreadPoolExecutor _ugentTaskPool ;
146+ ThreadPoolExecutor _ugentTaskPool ;
144147 ExecutorService _executor ;
145148
149+ Thread _shutdownThread = new ShutdownThread (this );
150+
146151 private String _keystoreSetupPath ;
147152 private String _keystoreCertImportPath ;
148153
@@ -153,7 +158,7 @@ public Agent(final IAgentShell shell) {
153158
154159 _connection = new NioClient ("Agent" , _shell .getNextHost (), _shell .getPort (), _shell .getWorkers (), this );
155160
156- Runtime .getRuntime ().addShutdownHook (new ShutdownThread ( this ) );
161+ Runtime .getRuntime ().addShutdownHook (_shutdownThread );
157162
158163 _ugentTaskPool =
159164 new ThreadPoolExecutor (shell .getPingRetries (), 2 * shell .getPingRetries (), 10 , TimeUnit .MINUTES , new SynchronousQueue <Runnable >(), new NamedThreadFactory (
@@ -192,7 +197,7 @@ public Agent(final IAgentShell shell, final int localAgentId, final ServerResour
192197 // ((NioClient)_connection).setBindAddress(_shell.getPrivateIp());
193198
194199 s_logger .debug ("Adding shutdown hook" );
195- Runtime .getRuntime ().addShutdownHook (new ShutdownThread ( this ) );
200+ Runtime .getRuntime ().addShutdownHook (_shutdownThread );
196201
197202 _ugentTaskPool =
198203 new ThreadPoolExecutor (shell .getPingRetries (), 2 * shell .getPingRetries (), 10 , TimeUnit .MINUTES , new SynchronousQueue <Runnable >(), new NamedThreadFactory (
@@ -239,20 +244,39 @@ public String getResourceName() {
239244 return _resource .getClass ().getSimpleName ();
240245 }
241246
247+ /**
248+ * In case of a software based agent restart, this method
249+ * can help to perform explicit garbage collection of any old
250+ * agent instances and its inner objects.
251+ */
252+ private void scavengeOldAgentObjects () {
253+ _executor .submit (new Runnable () {
254+ @ Override
255+ public void run () {
256+ try {
257+ Thread .sleep (2000L );
258+ } catch (final InterruptedException ignored ) {
259+ } finally {
260+ System .gc ();
261+ }
262+ }
263+ });
264+ }
265+
242266 public void start () {
243267 if (!_resource .start ()) {
244268 s_logger .error ("Unable to start the resource: " + _resource .getName ());
245269 throw new CloudRuntimeException ("Unable to start the resource: " + _resource .getName ());
246270 }
247271
248- _keystoreSetupPath = Script .findScript ("scripts/util/" , KeyStoreUtils .keyStoreSetupScript );
272+ _keystoreSetupPath = Script .findScript ("scripts/util/" , KeyStoreUtils .KS_SETUP_SCRIPT );
249273 if (_keystoreSetupPath == null ) {
250- throw new CloudRuntimeException (String .format ("Unable to find the '%s' script" , KeyStoreUtils .keyStoreSetupScript ));
274+ throw new CloudRuntimeException (String .format ("Unable to find the '%s' script" , KeyStoreUtils .KS_SETUP_SCRIPT ));
251275 }
252276
253- _keystoreCertImportPath = Script .findScript ("scripts/util/" , KeyStoreUtils .keyStoreImportScript );
277+ _keystoreCertImportPath = Script .findScript ("scripts/util/" , KeyStoreUtils .KS_IMPORT_SCRIPT );
254278 if (_keystoreCertImportPath == null ) {
255- throw new CloudRuntimeException (String .format ("Unable to find the '%s' script" , KeyStoreUtils .keyStoreImportScript ));
279+ throw new CloudRuntimeException (String .format ("Unable to find the '%s' script" , KeyStoreUtils .KS_IMPORT_SCRIPT ));
256280 }
257281
258282 try {
@@ -274,6 +298,7 @@ public void start() {
274298 }
275299 }
276300 _shell .updateConnectedHost ();
301+ scavengeOldAgentObjects ();
277302 }
278303
279304 public void stop (final String reason , final String detail ) {
@@ -298,14 +323,42 @@ public void stop(final String reason, final String detail) {
298323 }
299324 _connection .stop ();
300325 _connection = null ;
326+ _link = null ;
301327 }
302328
303329 if (_resource != null ) {
304330 _resource .stop ();
305331 _resource = null ;
306332 }
307333
308- _ugentTaskPool .shutdownNow ();
334+ if (_startup != null ) {
335+ _startup = null ;
336+ }
337+
338+ if (_ugentTaskPool != null ) {
339+ _ugentTaskPool .shutdownNow ();
340+ _ugentTaskPool = null ;
341+ }
342+
343+ if (_executor != null ) {
344+ _executor .shutdown ();
345+ _executor = null ;
346+ }
347+
348+ if (_timer != null ) {
349+ _timer .cancel ();
350+ _timer = null ;
351+ }
352+
353+ if (hostLBTimer != null ) {
354+ hostLBTimer .cancel ();
355+ hostLBTimer = null ;
356+ }
357+
358+ if (certTimer != null ) {
359+ certTimer .cancel ();
360+ certTimer = null ;
361+ }
309362 }
310363
311364 public Long getId () {
@@ -318,6 +371,15 @@ public void setId(final Long id) {
318371 _shell .setPersistentProperty (getResourceName (), "id" , Long .toString (id ));
319372 }
320373
374+ private synchronized void scheduleServicesRestartTask () {
375+ if (certTimer != null ) {
376+ certTimer .cancel ();
377+ certTimer .purge ();
378+ }
379+ certTimer = new Timer ("Certificate Renewal Timer" );
380+ certTimer .schedule (new PostCertificateRenewalTask (this ), 5000L );
381+ }
382+
321383 private synchronized void scheduleHostLBCheckerTask (final long checkInterval ) {
322384 if (hostLBTimer != null ) {
323385 hostLBTimer .cancel ();
@@ -578,6 +640,9 @@ protected void processRequest(final Request request, final Link link) {
578640 answer = setupAgentKeystore ((SetupKeyStoreCommand ) cmd );
579641 } else if (cmd instanceof SetupCertificateCommand && ((SetupCertificateCommand ) cmd ).isHandleByAgent ()) {
580642 answer = setupAgentCertificate ((SetupCertificateCommand ) cmd );
643+ if (Host .Type .Routing .equals (_resource .getType ())) {
644+ scheduleServicesRestartTask ();
645+ }
581646 } else if (cmd instanceof SetupDirectDownloadCertificate ) {
582647 answer = setupDirectDownloadCertificate ((SetupDirectDownloadCertificate ) cmd );
583648 } else if (cmd instanceof SetupMSListCommand ) {
@@ -641,7 +706,7 @@ private Answer setupDirectDownloadCertificate(SetupDirectDownloadCertificate cmd
641706 return new Answer (cmd , false , "Failed to find agent.properties file" );
642707 }
643708
644- final String keyStoreFile = agentFile .getParent () + "/" + KeyStoreUtils .defaultKeystoreFile ;
709+ final String keyStoreFile = agentFile .getParent () + "/" + KeyStoreUtils .KS_FILENAME ;
645710
646711 String cerFile = agentFile .getParent () + "/" + certificateName + ".cer" ;
647712 Script .runSimpleBashScript (String .format ("echo '%s' > %s" , certificate , cerFile ));
@@ -666,13 +731,13 @@ public Answer setupAgentKeystore(final SetupKeyStoreCommand cmd) {
666731 if (agentFile == null ) {
667732 return new Answer (cmd , false , "Failed to find agent.properties file" );
668733 }
669- final String keyStoreFile = agentFile .getParent () + "/" + KeyStoreUtils .defaultKeystoreFile ;
670- final String csrFile = agentFile .getParent () + "/" + KeyStoreUtils .defaultCsrFile ;
734+ final String keyStoreFile = agentFile .getParent () + "/" + KeyStoreUtils .KS_FILENAME ;
735+ final String csrFile = agentFile .getParent () + "/" + KeyStoreUtils .CSR_FILENAME ;
671736
672- String storedPassword = _shell .getPersistentProperty (null , KeyStoreUtils .passphrasePropertyName );
737+ String storedPassword = _shell .getPersistentProperty (null , KeyStoreUtils .KS_PASSPHRASE_PROPERTY );
673738 if (Strings .isNullOrEmpty (storedPassword )) {
674739 storedPassword = keyStorePassword ;
675- _shell .setPersistentProperty (null , KeyStoreUtils .passphrasePropertyName , storedPassword );
740+ _shell .setPersistentProperty (null , KeyStoreUtils .KS_PASSPHRASE_PROPERTY , storedPassword );
676741 }
677742
678743 Script script = new Script (true , _keystoreSetupPath , 60000 , s_logger );
@@ -706,10 +771,10 @@ private Answer setupAgentCertificate(final SetupCertificateCommand cmd) {
706771 if (agentFile == null ) {
707772 return new Answer (cmd , false , "Failed to find agent.properties file" );
708773 }
709- final String keyStoreFile = agentFile .getParent () + "/" + KeyStoreUtils .defaultKeystoreFile ;
710- final String certFile = agentFile .getParent () + "/" + KeyStoreUtils .defaultCertFile ;
711- final String privateKeyFile = agentFile .getParent () + "/" + KeyStoreUtils .defaultPrivateKeyFile ;
712- final String caCertFile = agentFile .getParent () + "/" + KeyStoreUtils .defaultCaCertFile ;
774+ final String keyStoreFile = agentFile .getParent () + "/" + KeyStoreUtils .KS_FILENAME ;
775+ final String certFile = agentFile .getParent () + "/" + KeyStoreUtils .CERT_FILENAME ;
776+ final String privateKeyFile = agentFile .getParent () + "/" + KeyStoreUtils .PKEY_FILENAME ;
777+ final String caCertFile = agentFile .getParent () + "/" + KeyStoreUtils .CACERT_FILENAME ;
713778
714779 try {
715780 FileUtils .writeStringToFile (new File (certFile ), certificate , Charset .defaultCharset ());
@@ -722,7 +787,7 @@ private Answer setupAgentCertificate(final SetupCertificateCommand cmd) {
722787 Script script = new Script (true , _keystoreCertImportPath , 60000 , s_logger );
723788 script .add (agentFile .getAbsolutePath ());
724789 script .add (keyStoreFile );
725- script .add (KeyStoreUtils .agentMode );
790+ script .add (KeyStoreUtils .AGENT_MODE );
726791 script .add (certFile );
727792 script .add ("" );
728793 script .add (caCertFile );
@@ -1072,6 +1137,60 @@ public void doTask(final Task task) throws TaskExecutionException {
10721137 }
10731138 }
10741139
1140+ /**
1141+ * Task stops the current agent and launches a new agent
1142+ * when there are no outstanding jobs in the agent's task queue
1143+ */
1144+ public class PostCertificateRenewalTask extends ManagedContextTimerTask {
1145+
1146+ private Agent agent ;
1147+
1148+ public PostCertificateRenewalTask (final Agent agent ) {
1149+ this .agent = agent ;
1150+ }
1151+
1152+ @ Override
1153+ protected void runInContext () {
1154+ while (true ) {
1155+ try {
1156+ if (_inProgress .get () == 0 ) {
1157+ s_logger .debug ("Running post certificate renewal task to restart services." );
1158+
1159+ // Let the resource perform any post certificate renewal cleanups
1160+ _resource .executeRequest (new PostCertificateRenewalCommand ());
1161+
1162+ IAgentShell shell = agent ._shell ;
1163+ ServerResource resource = agent ._resource .getClass ().newInstance ();
1164+
1165+ // Stop current agent
1166+ agent .cancelTasks ();
1167+ agent ._reconnectAllowed = false ;
1168+ Runtime .getRuntime ().removeShutdownHook (agent ._shutdownThread );
1169+ agent .stop (ShutdownCommand .Requested , "Restarting due to new X509 certificates" );
1170+
1171+ // Nullify references for GC
1172+ agent ._shell = null ;
1173+ agent ._watchList = null ;
1174+ agent ._shutdownThread = null ;
1175+ agent ._controlListeners = null ;
1176+ agent = null ;
1177+
1178+ // Start a new agent instance
1179+ shell .launchNewAgent (resource );
1180+ return ;
1181+ }
1182+ if (s_logger .isTraceEnabled ()) {
1183+ s_logger .debug ("Other tasks are in progress, will retry post certificate renewal command after few seconds" );
1184+ }
1185+ Thread .sleep (5000 );
1186+ } catch (final Exception e ) {
1187+ s_logger .warn ("Failed to execute post certificate renewal command:" , e );
1188+ break ;
1189+ }
1190+ }
1191+ }
1192+ }
1193+
10751194 public class PreferredHostCheckerTask extends ManagedContextTimerTask {
10761195
10771196 @ Override
0 commit comments