blob: f87669c51220612e686ea675857007690b5bcb0e [file] [log] [blame]
tyagi2cbc0a82015-05-21 02:53:14 -07001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
rabi2bde2782017-01-10 15:57:35 +053013import re
14import subprocess
Oleksii Chuprykov4be023a2015-07-08 07:17:08 -040015import time
16
tyagi2cbc0a82015-05-21 02:53:14 -070017import eventlet
18
19from oslo_concurrency import processutils
20from six.moves import configparser
21
Rabi Mishra477efc92015-07-31 13:01:45 +053022from heat_integrationtests.functional import functional_base
tyagi2cbc0a82015-05-21 02:53:14 -070023
24
Rabi Mishra477efc92015-07-31 13:01:45 +053025class ReloadOnSighupTest(functional_base.FunctionalTestsBase):
tyagi2cbc0a82015-05-21 02:53:14 -070026
27 def setUp(self):
28 self.config_file = "/etc/heat/heat.conf"
29 super(ReloadOnSighupTest, self).setUp()
30
rabi2bde2782017-01-10 15:57:35 +053031 def _is_mod_wsgi_daemon(self, service):
rabi4e7db092017-02-07 09:29:59 +053032 process = ''.join(['wsgi:',
33 service[:9]]).replace('_', '-').encode('utf-8')
rabi2bde2782017-01-10 15:57:35 +053034 s = subprocess.Popen(["ps", "ax"], stdout=subprocess.PIPE)
35 for x in s.stdout:
36 if re.search(process, x):
37 return True
38
tyagi2cbc0a82015-05-21 02:53:14 -070039 def _set_config_value(self, service, key, value):
40 config = configparser.ConfigParser()
Peter Razumovsky483e64b2016-03-04 17:04:28 +030041
42 # NOTE(prazumovsky): If there are several workers, there can be
43 # situation, when one thread opens self.config_file for writing
44 # (so config_file erases with opening), in that moment other thread
45 # intercepts to this file and try to set config option value, i.e.
46 # write to file, which is already erased by first thread, so,
47 # NoSectionError raised. So, should wait until first thread writes to
48 # config_file.
49 retries_count = self.conf.sighup_config_edit_retries
50 while True:
51 config.read(self.config_file)
52 try:
Thomas Herve4ebfbef2017-02-01 13:58:13 +010053 config.set(service, key, str(value))
Peter Razumovsky483e64b2016-03-04 17:04:28 +030054 except configparser.NoSectionError:
55 if retries_count <= 0:
56 raise
57 retries_count -= 1
58 eventlet.sleep(1)
59 else:
60 break
61
Thomas Herve4ebfbef2017-02-01 13:58:13 +010062 with open(self.config_file, 'w') as f:
tyagi2cbc0a82015-05-21 02:53:14 -070063 config.write(f)
64
65 def _get_config_value(self, service, key):
66 config = configparser.ConfigParser()
67 config.read(self.config_file)
68 val = config.get(service, key)
69 return val
70
71 def _get_heat_api_pids(self, service):
72 # get the pids of all heat-api processes
73 if service == "heat_api":
74 process = "heat-api|grep -Ev 'grep|cloudwatch|cfn'"
75 else:
76 process = "%s|grep -Ev 'grep'" % service.replace('_', '-')
77 cmd = "ps -ef|grep %s|awk '{print $2}'" % process
78 out, err = processutils.execute(cmd, shell=True)
79 self.assertIsNotNone(out, "heat-api service not running. %s" % err)
80 pids = filter(None, out.split('\n'))
81
82 # get the parent pids of all heat-api processes
83 cmd = "ps -ef|grep %s|awk '{print $3}'" % process
84 out, _ = processutils.execute(cmd, shell=True)
85 parent_pids = filter(None, out.split('\n'))
86
87 heat_api_parent = list(set(pids) & set(parent_pids))[0]
88 heat_api_children = list(set(pids) - set(parent_pids))
89
90 return heat_api_parent, heat_api_children
91
92 def _change_config(self, service, old_workers, new_workers):
93 pre_reload_parent, pre_reload_children = self._get_heat_api_pids(
94 service)
95 self.assertEqual(old_workers, len(pre_reload_children))
96
97 # change the config values
98 self._set_config_value(service, 'workers', new_workers)
99 cmd = "kill -HUP %s" % pre_reload_parent
100 processutils.execute(cmd, shell=True)
tyagi2cbc0a82015-05-21 02:53:14 -0700101
Oleksii Chuprykov4be023a2015-07-08 07:17:08 -0400102 # wait till heat-api reloads
103 start_time = time.time()
104 while time.time() - start_time < self.conf.sighup_timeout:
105 post_reload_parent, post_reload_children = self._get_heat_api_pids(
106 service)
107 intersect = set(post_reload_children) & set(pre_reload_children)
108 if (new_workers == len(post_reload_children)
109 and pre_reload_parent == post_reload_parent
110 and intersect == set()):
111 break
112 eventlet.sleep(1)
tyagi2cbc0a82015-05-21 02:53:14 -0700113 self.assertEqual(pre_reload_parent, post_reload_parent)
114 self.assertEqual(new_workers, len(post_reload_children))
115 # test if all child processes are newly created
116 self.assertEqual(set(post_reload_children) & set(pre_reload_children),
117 set())
118
119 def _reload(self, service):
120 old_workers = int(self._get_config_value(service, 'workers'))
121 new_workers = old_workers + 1
122 self.addCleanup(self._set_config_value, service, 'workers',
123 old_workers)
124
125 self._change_config(service, old_workers, new_workers)
126 # revert all the changes made
127 self._change_config(service, new_workers, old_workers)
128
rabi2bde2782017-01-10 15:57:35 +0530129 def _reload_on_sighup(self, service):
130 if not self._is_mod_wsgi_daemon(service):
131 self._reload(service)
132 else:
133 self.skipTest('Skipping Test, Service running under httpd.')
134
tyagi2cbc0a82015-05-21 02:53:14 -0700135 def test_api_reload_on_sighup(self):
rabi2bde2782017-01-10 15:57:35 +0530136 self._reload_on_sighup('heat_api')
tyagi2cbc0a82015-05-21 02:53:14 -0700137
138 def test_api_cfn_reload_on_sighup(self):
rabi2bde2782017-01-10 15:57:35 +0530139 self._reload_on_sighup('heat_api_cfn')
tyagi2cbc0a82015-05-21 02:53:14 -0700140
141 def test_api_cloudwatch_on_sighup(self):
rabi2bde2782017-01-10 15:57:35 +0530142 self._reload_on_sighup('heat_api_cloudwatch')