62cda16cf2021b8d409a559f6cd86135774190e7
[csit.git] / resources / libraries / python / honeycomb / Notifications.py
1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Implementation of keywords for managing Honeycomb notifications."""
15
16 from time import time
17
18 import paramiko
19 from robot.api import logger
20 from interruptingcow import timeout
21
22 from resources.libraries.python.honeycomb.HoneycombUtil import HoneycombError
23
24
25 class Notifications(object):
26     """Implements keywords for managing Honeycomb notifications.
27
28     The keywords implemented in this class make it possible to:
29     - establish SSH session to Honeycomb host
30     - receive notifications from Honeycomb
31     - read received notifications
32     """
33
34     def __init__(self, hello, subscription):
35         """Initializer.
36         :param hello: Hello message to be sent to Honeycomb.
37         :param subscription: rpc command to subscribe to Honeycomb notifications
38         over Netconf.
39         :type hello: str
40         :type subscription: str
41
42         Note: Passing the channel object as a robotframework argument closes
43         the channel. Class variables are used instead,
44         to persist the connection channel throughout the test case.
45         """
46
47         self.client = None
48         self.channel = None
49         self.hello = hello
50         self.subscription = subscription
51
52     def create_session(self, node, time_out=10):
53         """Create an SSH session and connect to Honeycomb on the specified node.
54
55         :param node: Honeycomb node.
56         :param time_out: Timeout value for the connection in seconds.
57         :type node: dict
58         :type time_out: int
59         """
60
61         start = time()
62         client = paramiko.SSHClient()
63         client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
64
65         client.connect(node['host'],
66                        username=node['honeycomb']['user'],
67                        password=node['honeycomb']['passwd'],
68                        pkey=None,
69                        port=node['honeycomb']['netconf_port'],
70                        timeout=time_out,
71                        )
72
73         logger.trace('Connect took {0} seconds'.format(time() - start))
74         logger.debug('New ssh: {0}'.format(client))
75         logger.debug('Connect peer: {0}'.
76                      format(client.get_transport().getpeername()))
77         logger.debug(client)
78
79         channel = client.get_transport().open_session()
80         channel.settimeout(time_out)
81         channel.get_pty()
82         channel.invoke_subsystem("netconf")
83         logger.debug(channel)
84
85         self.client = client
86         self.channel = channel
87
88         # read OpenDaylight's hello message and capability list
89         self._get_response(
90             size=131072,
91             time_out=time_out,
92             err="Timeout on getting hello message."
93         )
94
95         self.channel.send(self.hello)
96         if not self.channel.active:
97             raise HoneycombError("Channel closed on capabilities exchange.")
98
99     def _get_response(self, size=4096, time_out=10, err="Unspecified Error."):
100         """Iteratively read data from the receive buffer and catenate together
101         until message ends with the message delimiter, or
102         until timeout is reached.
103
104         :param size: Maximum number of bytes to read in one iteration.
105         :param time_out: Timeout value for getting the complete response.
106         :param err: Error message to provide when timeout is reached.
107         :type size:int
108         :type time_out:int
109         :type err:str
110         :return: Content of response.
111         :rtype: str
112         :raises HoneycombError: If the read process times out.
113         """
114
115         reply = ''
116
117         try:
118             with timeout(time_out, exception=RuntimeError):
119                 while not reply.endswith(']]>]]>'):
120                     if self.channel.recv_ready():
121                         reply += self.channel.recv(size)
122
123         except RuntimeError:
124             raise HoneycombError(err+" Content of buffer: {0}".format(reply))
125
126         logger.trace(reply)
127         return reply
128
129     def add_notification_listener(self, time_out=10):
130         """Open a new channel on the SSH session, connect to Netconf subsystem
131         and subscribe to receive Honeycomb notifications.
132
133         :param time_out: Timeout value for each read operation in seconds.
134         :type time_out: int
135         :raises HoneycombError: If subscription to notifications fails.
136         """
137
138         self.channel.send(self.subscription)
139
140         reply = self._get_response(
141             time_out=time_out,
142             err="Timeout on notifications subscription."
143         )
144
145         if "<ok/>" not in reply:
146             raise HoneycombError("Notifications subscription failed with"
147                                  " message: {0}".format(reply))
148
149         logger.debug("Notifications subscription successful.")
150
151     def get_notification(self, time_out=10):
152         """Read and return the next notification message.
153
154         :param time_out: Timeout value for the read operation in seconds.
155         :type time_out: int
156         :return: Data received from buffer.
157         :rtype: str
158         """
159
160         logger.debug("Getting notification.")
161
162         reply = self._get_response(
163             time_out=time_out,
164             err="Timeout on getting notification."
165         )
166
167         return reply