Merge "implement delete and related unittest"
[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:
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 += '{node_object}\n'.format(node_object=node)
93
94         return s
95
96
97 class Role():
98     CONTROLLER = 'controller'
99     COMPUTE = 'compute'
100     ODL = 'opendaylight'
101     ONOS = 'onos'
102
103
104 class NodeStatus():
105     STATUS_OK = 'active'
106     STATUS_INACTIVE = 'inactive'
107     STATUS_OFFLINE = 'offline'
108     STATUS_ERROR = 'error'
109     STATUS_UNUSED = 'unused'
110
111
112 class Node(object):
113
114     def __init__(self,
115                  id,
116                  ip,
117                  name,
118                  status,
119                  roles=[],
120                  ssh_client=None,
121                  info={}):
122         self.id = id
123         self.ip = ip
124         self.name = name
125         self.status = status
126         self.ssh_client = ssh_client
127         self.roles = roles
128         self.info = info
129
130     def get_file(self, src, dest):
131         '''
132         SCP file from a node
133         '''
134         if self.status is not NodeStatus.STATUS_OK:
135             logger.info("The node %s is not active" % self.ip)
136             return 1
137         logger.info("Fetching %s from %s" % (src, self.ip))
138         get_file_result = ssh_utils.get_file(self.ssh_client, src, dest)
139         if get_file_result is None:
140             logger.error("SFTP failed to retrieve the file.")
141         else:
142             logger.info("Successfully copied %s:%s to %s" %
143                         (self.ip, src, dest))
144         return get_file_result
145
146     def put_file(self, src, dest):
147         '''
148         SCP file to a node
149         '''
150         if self.status is not NodeStatus.STATUS_OK:
151             logger.info("The node %s is not active" % self.ip)
152             return 1
153         logger.info("Copying %s to %s" % (src, self.ip))
154         put_file_result = ssh_utils.put_file(self.ssh_client, src, dest)
155         if put_file_result is None:
156             logger.error("SFTP failed to retrieve the file.")
157         else:
158             logger.info("Successfully copied %s to %s:%s" %
159                         (src, dest, self.ip))
160         return put_file_result
161
162     def run_cmd(self, cmd):
163         '''
164         Run command remotely on a node
165         '''
166         if self.status is not NodeStatus.STATUS_OK:
167             logger.error("The node %s is not active" % self.ip)
168             return None
169         _, stdout, stderr = (self.ssh_client.exec_command(cmd))
170         error = stderr.readlines()
171         if len(error) > 0:
172             logger.error("error %s" % ''.join(error))
173             return error
174         output = ''.join(stdout.readlines()).rstrip()
175         return output
176
177     def get_dict(self):
178         '''
179         Returns a dictionary with all the attributes
180         '''
181         return {
182             'id': self.id,
183             'ip': self.ip,
184             'name': self.name,
185             'status': self.status,
186             'roles': self.roles,
187             'info': self.info
188         }
189
190     def get_attribute(self, attribute):
191         '''
192         Returns an attribute given the name
193         '''
194         return self.get_dict()[attribute]
195
196     def is_controller(self):
197         '''
198         Returns if the node is a controller
199         '''
200         if 'controller' in self.roles:
201             return True
202         return False
203
204     def is_compute(self):
205         '''
206         Returns if the node is a compute
207         '''
208         if 'compute' in self.roles:
209             return True
210         return False
211
212     def get_ovs_info(self):
213         '''
214         Returns the ovs version installed
215         '''
216         cmd = "ovs-vsctl --version|head -1| sed 's/^.*) //'"
217         return self.run_cmd(cmd)
218
219     def __str__(self):
220         return '''
221             name:   {name}
222             id:     {id}
223             ip:     {ip}
224             status: {status}
225             roles:  {roles}
226             ovs:    {ovs}
227             info:   {info}'''.format(name=self.name,
228                                      id=self.id,
229                                      ip=self.ip,
230                                      status=self.status,
231                                      roles=self.roles,
232                                      ovs=self.get_ovs_info(),
233                                      info=self.info)
234
235
236 class DeploymentHandler(object):
237
238     EX_OK = os.EX_OK
239     EX_ERROR = os.EX_SOFTWARE
240     FUNCTION_NOT_IMPLEMENTED = "Function not implemented by adapter!"
241
242     def __init__(self,
243                  installer,
244                  installer_ip,
245                  installer_user,
246                  installer_pwd=None,
247                  pkey_file=None):
248
249         self.installer = installer.lower()
250         self.installer_ip = installer_ip
251         self.installer_user = installer_user
252         self.installer_pwd = installer_pwd
253         self.pkey_file = pkey_file
254
255         if pkey_file is not None and not os.path.isfile(pkey_file):
256             raise Exception(
257                 'The private key file %s does not exist!' % pkey_file)
258
259         self.installer_connection = ssh_utils.get_ssh_client(
260             hostname=self.installer_ip,
261             username=self.installer_user,
262             password=self.installer_pwd,
263             pkey_file=self.pkey_file)
264
265         if self.installer_connection:
266             self.installer_node = Node(id='',
267                                        ip=installer_ip,
268                                        name=installer,
269                                        status=NodeStatus.STATUS_OK,
270                                        ssh_client=self.installer_connection,
271                                        roles='installer node')
272         else:
273             raise Exception(
274                 'Cannot establish connection to the installer node!')
275
276         self.nodes = self.get_nodes()
277
278     @abstractmethod
279     def get_openstack_version(self):
280         '''
281         Returns a string of the openstack version (nova-compute)
282         '''
283         raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
284
285     @abstractmethod
286     def get_sdn_version(self):
287         '''
288         Returns a string of the sdn controller and its version, if exists
289         '''
290         raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
291
292     @abstractmethod
293     def get_deployment_status(self):
294         '''
295         Returns a string of the status of the deployment
296         '''
297         raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
298
299     @abstractmethod
300     def get_nodes(self, options=None):
301         '''
302             Generates a list of all the nodes in the deployment
303         '''
304         raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
305
306     def get_installer_node(self):
307         '''
308             Returns the installer node object
309         '''
310         return self.installer_node
311
312     def get_deployment_info(self):
313         '''
314             Returns an object of type Deployment
315         '''
316         return Deployment(installer=self.installer,
317                           installer_ip=self.installer_ip,
318                           scenario=os.getenv('DEPLOY_SCENARIO', 'Unknown'),
319                           status=self.get_deployment_status(),
320                           pod=os.getenv('NODE_NAME', 'Unknown'),
321                           openstack_version=self.get_openstack_version(),
322                           sdn_controller=self.get_sdn_version(),
323                           nodes=self.get_nodes())