05f9f36b9a09d56a0fcc4d3a326fdb29a99653e8
[ta/config-manager.git] / cmactivator.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 import os
15 import subprocess
16 import pwd
17 import logging
18
19 from cmframework.apis import cmerror
20
21
22 class CMActivator(object):
23     ansible_bin = '/usr/local/bin/openstack-ansible'
24     admin_user_file = '/etc/admin_user'
25
26     def __init__(self):
27         self.plugin_client = None
28         try:
29             with open(CMActivator.admin_user_file, 'r') as f:
30                 self.admin_user = f.read()
31         except IOError:
32             pass
33
34     # pylint: disable=no-self-use
35     def get_subscription_info(self):
36         """get the subscription filter
37
38            This API is used to get the re for matching the properties which the
39            activation plugin is concerned about.
40
41            Return:
42
43            A string representing the regular expression used to match the
44            properties which the activation plugin is concerned about.
45
46            Raise:
47
48            CMError can be raised in-case of a failure.
49         """
50         raise cmerror.CMError('Not implemented')
51
52     # pylint: disable=no-self-use, unused-argument
53     def activate_set(self, props):
54         """activate a configuration data addition/update
55
56            Arguments:
57
58            props: A dictionary of name-value pairs representing the changed
59            properties.
60
61            Raise:
62
63            CMError can be raised in-case of an error
64         """
65         raise cmerror.CMError('Not implemented')
66
67     # pylint: disable=no-self-use, unused-argument
68     def activate_delete(self, props):
69         """activate a configuration data deletion
70
71            Arguments:
72
73            props: A list of deleted property names.
74
75            Raise:
76
77            CMError can be raised in-case of an error
78         """
79         raise cmerror.CMError('Not implemented')
80
81     # pylint: disable=no-self-use, unused-argument
82     def activate_full(self, target):
83         """perform a full activation
84
85            Arguments:
86
87            target: None if activating all nodes
88                    Node name string if activating only one node
89
90            Raise:
91
92            CMError can be raised in-case of an error
93         """
94         raise cmerror.CMError('Not implemented')
95
96     # pylint: disable=no-self-use
97     def get_plugin_client(self):
98         """get the plugin client object
99
100            This API can be used by the plugin to get the client object which the
101            plugin can use to access the configuration data. Notice that the data
102            accessed by this is what is stored in the backend. The changed data
103            is passed as argument to the different validate functions.
104
105            Return:
106
107            The plugin client object
108         """
109         return self.plugin_client
110
111     def run_playbook(self, playbook, target=None):
112         playbook_dir = os.path.dirname(playbook)
113
114         arguments = []
115         arguments.append('-b')
116         arguments.append('-u {}'.format(self.admin_user))
117         if target:
118             arguments.append('--limit {}'.format(target))
119         arguments.append(playbook)
120
121         cmd = '{} {}'.format(CMActivator.ansible_bin, ' '.join(arguments))
122         out, result = self._run_cmd_as_user(cmd, playbook_dir, self.admin_user)
123         if result != 0:
124             raise cmerror.CMError('Playbook {} failed: {}'.format(playbook, out))
125         logging.debug('Playbook out: %s', out)
126
127     def _run_cmd_as_user(self, cmd, cwd, user):
128         pw_record = pwd.getpwnam(user)
129         user_name = pw_record.pw_name
130         user_home_dir = pw_record.pw_dir
131         user_uid = pw_record.pw_uid
132         user_gid = pw_record.pw_gid
133         env = os.environ.copy()
134         env['CONFIG_PHASE'] = 'postconfig'
135         env['HOME'] = user_home_dir
136         env['LOGNAME'] = user_name
137         env['PWD'] = cwd
138         env['USER'] = user_name
139         p = subprocess.Popen(cmd.split(), preexec_fn=self._demote(user_uid, user_gid),
140                              cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
141         out, _ = p.communicate()
142         return (out, p.returncode)
143
144     def _demote(self, user_uid, user_gid):
145         def result():
146             os.setgid(user_gid)
147             os.setuid(user_uid)
148
149         return result
150
151
152 class CMGlobalActivator(CMActivator):
153     def __init__(self):
154         super(CMGlobalActivator, self).__init__()
155
156
157 class CMLocalActivator(CMActivator):
158     def __init__(self):
159         super(CMLocalActivator, self).__init__()
160         self.hostname = None
161
162     def get_hostname(self):
163         """get the node name
164
165            This API is used to get the name of the node where activation is
166            ongoing.
167
168            Return:
169
170            The node name
171         """
172         return self.hostname
173
174
175 def main():
176     def print_type(activator):
177         if isinstance(activator, CMLocalActivator):
178             print 'Local activator'
179         elif isinstance(activator, CMActivator):
180             print 'Activator'
181         else:
182             print 'Unknown'
183
184     activator = CMActivator()
185     localactivator = CMLocalActivator()
186
187     x = 100
188
189     print_type(activator)
190     print_type(localactivator)
191     print_type(x)
192
193
194 if __name__ == '__main__':
195     main()