[deployment handler] Refactor the old installer_handler
[releng.git] / modules / opnfv / deployment / manager.py
1 ##############################################################################
2 # Copyright (c) 2017 Ericsson AB and others.
3 # Author: Jose Lausuch (jose.lausuch@ericsson.com)
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 from abc import abstractmethod
11 import os
12
13
14 from opnfv.utils import opnfv_logger as logger
15 from opnfv.utils import ssh_utils
16
17 logger = logger.Logger(__name__).getLogger()
18
19
20 class Deployment(object):
21
22     def __init__(self,
23                  installer,
24                  installer_ip,
25                  scenario,
26                  pod,
27                  status,
28                  openstack_version,
29                  sdn_controller,
30                  nodes=[]):
31
32         self.deployment_info = {
33             'installer': installer,
34             'installer_ip': installer_ip,
35             'scenario': scenario,
36             'pod': pod,
37             'status': status,
38             'openstack_version': openstack_version,
39             'sdn_controller': sdn_controller,
40             'nodes': nodes
41         }
42
43     def _get_openstack_release(self):
44         '''
45         Translates an openstack version into the release name
46         '''
47         os_versions = {
48             '12': 'Liberty',
49             '13': 'Mitaka',
50             '14': 'Newton',
51             '15': 'Ocata',
52             '16': 'Pike',
53             '17': 'Queens'
54         }
55         try:
56             version = self.deployment_info['openstack_version'].split('.')[0]
57             name = os_versions[version]
58             return name
59         except Exception as e:
60             return 'Unknown release'
61
62     def get_dict(self):
63         '''
64         Returns a dictionary will all the attributes
65         '''
66         return self.deployment_info
67
68     def __str__(self):
69         '''
70         Override of the str method
71         '''
72         s = '''
73         INSTALLER:    {installer}
74         SCENARIO:     {scenario}
75         INSTALLER IP: {installer_ip}
76         POD:          {pod}
77         STATUS:       {status}
78         OPENSTACK:    {openstack_version} ({openstack_release})
79         SDN:          {sdn_controller}
80         NODES:
81     '''.format(installer=self.deployment_info['installer'],
82                scenario=self.deployment_info['scenario'],
83                installer_ip=self.deployment_info['installer_ip'],
84                pod=self.deployment_info['pod'],
85                status=self.deployment_info['status'],
86                openstack_version=self.deployment_info[
87             'openstack_version'],
88             openstack_release=self._get_openstack_release(),
89             sdn_controller=self.deployment_info['sdn_controller'])
90
91         for node in self.deployment_info['nodes']:
92             s += '\t\t{node_object}\n'.format(node_object=node)
93
94         return s
95
96
97 class Node(object):
98
99     STATUS_OK = 'active'
100     STATUS_INACTIVE = 'inactive'
101     STATUS_OFFLINE = 'offline'
102     STATUS_FAILED = 'failed'
103
104     def __init__(self,
105                  id,
106                  ip,
107                  name,
108                  status,
109                  roles,
110                  ssh_client,
111                  info={}):
112         self.id = id
113         self.ip = ip
114         self.name = name
115         self.status = status
116         self.ssh_client = ssh_client
117         self.roles = roles
118         self.info = info
119
120     def get_file(self, src, dest):
121         '''
122         SCP file from a node
123         '''
124         if self.status is not Node.STATUS_OK:
125             logger.info("The node %s is not active" % self.ip)
126             return 1
127         logger.info("Fetching %s from %s" % (src, self.ip))
128         get_file_result = ssh_utils.get_file(self.ssh_client, src, dest)
129         if get_file_result is None:
130             logger.error("SFTP failed to retrieve the file.")
131         else:
132             logger.info("Successfully copied %s:%s to %s" %
133                         (self.ip, src, dest))
134         return get_file_result
135
136     def put_file(self, src, dest):
137         '''
138         SCP file to a node
139         '''
140         if self.status is not Node.STATUS_OK:
141             logger.info("The node %s is not active" % self.ip)
142             return 1
143         logger.info("Copying %s to %s" % (src, self.ip))
144         put_file_result = ssh_utils.put_file(self.ssh_client, src, dest)
145         if put_file_result is None:
146             logger.error("SFTP failed to retrieve the file.")
147         else:
148             logger.info("Successfully copied %s to %s:%s" %
149                         (src, dest, self.ip))
150         return put_file_result
151
152     def run_cmd(self, cmd):
153         '''
154         Run command remotely on a node
155         '''
156         if self.status is not Node.STATUS_OK:
157             logger.info("The node %s is not active" % self.ip)
158             return 1
159         _, stdout, stderr = (self.ssh_client.exec_command(cmd))
160         error = stderr.readlines()
161         if len(error) > 0:
162             logger.error("error %s" % ''.join(error))
163             return error
164         output = ''.join(stdout.readlines()).rstrip()
165         return output
166
167     def get_dict(self):
168         '''
169         Returns a dictionary with all the attributes
170         '''
171         return {
172             'id': self.id,
173             'ip': self.ip,
174             'name': self.name,
175             'status': self.status,
176             'roles': self.roles,
177             'info': self.info
178         }
179
180     def get_attribute(self, attribute):
181         '''
182         Returns an attribute given the name
183         '''
184         return self.get_dict()[attribute]
185
186     def is_controller(self):
187         '''
188         Returns if the node is a controller
189         '''
190         if 'controller' in self.get_attribute('roles'):
191             return True
192         return False
193
194     def is_compute(self):
195         '''
196         Returns if the node is a compute
197         '''
198         if 'compute' in self.get_attribute('roles'):
199             return True
200         return False
201
202     def __str__(self):
203         return str(self.get_dict())
204
205
206 class DeploymentHandler(object):
207
208     EX_OK = os.EX_OK
209     EX_ERROR = os.EX_SOFTWARE
210     FUNCTION_NOT_IMPLEMENTED = "Function not implemented by adapter!"
211
212     def __init__(self,
213                  installer,
214                  installer_ip,
215                  installer_user,
216                  installer_pwd=None,
217                  pkey_file=None):
218
219         self.installer = installer.lower()
220         self.installer_ip = installer_ip
221         self.installer_user = installer_user
222         self.installer_pwd = installer_pwd
223         self.pkey_file = pkey_file
224
225         if pkey_file is not None and not os.path.isfile(pkey_file):
226             raise Exception(
227                 'The private key file %s does not exist!' % pkey_file)
228
229         self.installer_connection = ssh_utils.get_ssh_client(
230             hostname=self.installer_ip,
231             username=self.installer_user,
232             password=self.installer_pwd,
233             pkey_file=self.pkey_file)
234
235         if self.installer_connection:
236             self.installer_node = Node(id='',
237                                        ip=installer_ip,
238                                        name=installer,
239                                        status='active',
240                                        ssh_client=self.installer_connection,
241                                        roles='installer node')
242         else:
243             raise Exception(
244                 'Cannot establish connection to the installer node!')
245
246         self.nodes = self.nodes()
247
248     @abstractmethod
249     def get_openstack_version(self):
250         '''
251         Returns a string of the openstack version (nova-compute)
252         '''
253         raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
254
255     @abstractmethod
256     def get_sdn_version(self):
257         '''
258         Returns a string of the sdn controller and its version, if exists
259         '''
260         raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
261
262     @abstractmethod
263     def get_deployment_status(self):
264         '''
265         Returns a string of the status of the deployment
266         '''
267         raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
268
269     @abstractmethod
270     def nodes(self, options=None):
271         '''
272             Generates a list of all the nodes in the deployment
273         '''
274         raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
275
276     def get_nodes(self, options=None):
277         '''
278             Returns the list of Node objects
279         '''
280         return self.nodes
281
282     def get_installer_node(self):
283         '''
284             Returns the installer node object
285         '''
286         return self.installer_node
287
288     def get_deployment_info(self):
289         '''
290             Returns an object of type Deployment
291         '''
292         return Deployment(installer=self.installer,
293                           installer_ip=self.installer_ip,
294                           scenario=os.getenv('DEPLOY_SCENARIO', 'Unknown'),
295                           status=self.get_deployment_status(),
296                           pod=os.getenv('NODE_NAME', 'Unknown'),
297                           openstack_version=self.get_openstack_version(),
298                           sdn_controller=self.get_sdn_version(),
299                           nodes=self.nodes)