Browse Source

Add get method support to the driver-agent

This patch adds support for the octavia-lib to get objects by ID.

Change-Id: I98b399891488e5972ea4d332c06b55b34f20fb11
Story: 2005870
Task: 33680
Co-Authored-By: Adam Harwell <flux.adam@gmail.com>
tags/5.0.0.0rc1
Michael Johnson 4 months ago
parent
commit
09efc2a423
39 changed files with 849 additions and 66 deletions
  1. 23
    3
      doc/source/contributor/guides/providers.rst
  2. 1
    0
      etc/octavia.conf
  3. 82
    0
      octavia/api/drivers/driver_agent/driver_get.py
  4. 41
    0
      octavia/api/drivers/driver_agent/driver_listener.py
  5. 3
    3
      octavia/api/drivers/utils.py
  6. 14
    1
      octavia/certificates/manager/local.py
  7. 10
    1
      octavia/cmd/driver_agent.py
  8. 10
    0
      octavia/common/config.py
  9. 3
    0
      octavia/common/constants.py
  10. 10
    2
      octavia/common/data_models.py
  11. 31
    8
      octavia/common/tls_utils/cert_parser.py
  12. 0
    0
      octavia/tests/common/sample_certs.py
  13. 0
    0
      octavia/tests/common/sample_data_models.py
  14. 2
    1
      octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py
  15. 11
    0
      octavia/tests/functional/api/drivers/__init__.py
  16. 11
    0
      octavia/tests/functional/api/drivers/driver_agent/__init__.py
  17. 334
    0
      octavia/tests/functional/api/drivers/driver_agent/test_driver_agent.py
  18. 1
    1
      octavia/tests/functional/api/v2/test_listener.py
  19. 1
    1
      octavia/tests/functional/api/v2/test_pool.py
  20. 23
    8
      octavia/tests/functional/db/base.py
  21. 1
    1
      octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_0_5.py
  22. 1
    1
      octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_1_0.py
  23. 1
    1
      octavia/tests/unit/api/common/test_types.py
  24. 2
    3
      octavia/tests/unit/api/drivers/amphora_driver/v1/test_amphora_driver.py
  25. 2
    3
      octavia/tests/unit/api/drivers/amphora_driver/v2/test_amphora_driver.py
  26. 121
    0
      octavia/tests/unit/api/drivers/driver_agent/test_driver_get.py
  27. 46
    0
      octavia/tests/unit/api/drivers/driver_agent/test_driver_listener.py
  28. 19
    4
      octavia/tests/unit/api/drivers/test_utils.py
  29. 1
    1
      octavia/tests/unit/certificates/common/test_barbican.py
  30. 1
    1
      octavia/tests/unit/certificates/manager/test_barbican.py
  31. 1
    1
      octavia/tests/unit/certificates/manager/test_barbican_legacy.py
  32. 12
    8
      octavia/tests/unit/certificates/manager/test_local.py
  33. 10
    2
      octavia/tests/unit/cmd/test_driver_agent.py
  34. 1
    1
      octavia/tests/unit/common/sample_configs/sample_configs_combined.py
  35. 1
    1
      octavia/tests/unit/common/sample_configs/sample_configs_split.py
  36. 5
    8
      octavia/tests/unit/common/tls_utils/test_cert_parser.py
  37. 3
    1
      octavia/tests/unit/network/drivers/neutron/test_utils.py
  38. 4
    0
      releasenotes/notes/Add-driver-agent-get-methods-b624a1342c3e6d0f.yaml
  39. 6
    0
      tox.ini

+ 23
- 3
doc/source/contributor/guides/providers.rst View File

@@ -1931,11 +1931,13 @@ resources. See the `Octavia API Reference <https://docs.openstack.org/api-ref/lo
1931 1931
 API Exception Model
1932 1932
 -------------------
1933 1933
 
1934
-The driver support API will include two Exceptions, one for each of the
1934
+The driver support API will include exceptions:
1935 1935
 two API groups:
1936 1936
 
1937 1937
 * UpdateStatusError
1938 1938
 * UpdateStatisticsError
1939
+* DriverAgentNotFound
1940
+* DriverAgentTimeout
1939 1941
 
1940 1942
 Each exception class will include a message field that describes the error and
1941 1943
 references to the failed record if available.
@@ -1955,7 +1957,8 @@ references to the failed record if available.
1955 1957
           self.status_object_id = kwargs.pop('status_object_id', None)
1956 1958
           self.status_record = kwargs.pop('status_record', None)
1957 1959
 
1958
-          super(UpdateStatusError, self).__init__(*args, **kwargs)
1960
+          super(UpdateStatusError, self).__init__(self.fault_string,
1961
+                                                  *args, **kwargs)
1959 1962
 
1960 1963
   class UpdateStatisticsError(Exception):
1961 1964
       fault_string = _("The statistics update had an unknown error.")
@@ -1970,7 +1973,24 @@ references to the failed record if available.
1970 1973
           self.stats_object_id = kwargs.pop('stats_object_id', None)
1971 1974
           self.stats_record = kwargs.pop('stats_record', None)
1972 1975
 
1973
-          super(UpdateStatisticsError, self).__init__(*args, **kwargs)
1976
+          super(UpdateStatisticsError, self).__init__(self.fault_string,
1977
+                                                      *args, **kwargs)
1978
+
1979
+  class DriverAgentNotFound(Exception):
1980
+    fault_string = _("The driver-agent process was not found or not ready.")
1981
+
1982
+    def __init__(self, *args, **kwargs):
1983
+        self.fault_string = kwargs.pop('fault_string', self.fault_string)
1984
+        super(DriverAgentNotFound, self).__init__(self.fault_string,
1985
+                                                  *args, **kwargs)
1986
+
1987
+  class DriverAgentTimeout(Exception):
1988
+    fault_string = _("The driver-agent timeout.")
1989
+
1990
+    def __init__(self, *args, **kwargs):
1991
+        self.fault_string = kwargs.pop('fault_string', self.fault_string)
1992
+        super(DriverAgentTimeout, self).__init__(self.fault_string,
1993
+                                                 *args, **kwargs)
1974 1994
 
1975 1995
 Documenting the Driver
1976 1996
 ======================

+ 1
- 0
etc/octavia.conf View File

@@ -504,6 +504,7 @@
504 504
 [driver_agent]
505 505
 # status_socket_path = /var/run/octavia/status.sock
506 506
 # stats_socket_path = /var/run/octavia/stats.sock
507
+# get_socket_path = /var/run/octavia/get.sock
507 508
 
508 509
 # Maximum time to wait for a status message before checking for shutdown
509 510
 # status_request_timeout = 5

+ 82
- 0
octavia/api/drivers/driver_agent/driver_get.py View File

@@ -0,0 +1,82 @@
1
+# Copyright 2019 Red Hat, Inc. All rights reserved.
2
+#
3
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+# not use this file except in compliance with the License. You may obtain
5
+# 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, WITHOUT
11
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+# License for the specific language governing permissions and limitations
13
+# under the License.
14
+
15
+from octavia_lib.common import constants as lib_consts
16
+
17
+from octavia.api.drivers import utils as driver_utils
18
+from octavia.common import constants
19
+from octavia.db import api as db_api
20
+from octavia.db import repositories
21
+
22
+
23
+def process_get(get_data):
24
+    session = db_api.get_session()
25
+
26
+    if get_data[constants.OBJECT] == lib_consts.LOADBALANCERS:
27
+        lb_repo = repositories.LoadBalancerRepository()
28
+        db_lb = lb_repo.get(session, id=get_data[lib_consts.ID],
29
+                            show_deleted=False)
30
+        if db_lb:
31
+            provider_lb = (
32
+                driver_utils.db_loadbalancer_to_provider_loadbalancer(db_lb))
33
+            return provider_lb.to_dict(recurse=True, render_unsets=True)
34
+    elif get_data[constants.OBJECT] == lib_consts.LISTENERS:
35
+        listener_repo = repositories.ListenerRepository()
36
+        db_listener = listener_repo.get(
37
+            session, id=get_data[lib_consts.ID], show_deleted=False)
38
+        if db_listener:
39
+            provider_listener = (
40
+                driver_utils.db_listener_to_provider_listener(db_listener))
41
+            return provider_listener.to_dict(recurse=True, render_unsets=True)
42
+    elif get_data[constants.OBJECT] == lib_consts.POOLS:
43
+        pool_repo = repositories.PoolRepository()
44
+        db_pool = pool_repo.get(session, id=get_data[lib_consts.ID],
45
+                                show_deleted=False)
46
+        if db_pool:
47
+            provider_pool = (
48
+                driver_utils.db_pool_to_provider_pool(db_pool))
49
+            return provider_pool.to_dict(recurse=True, render_unsets=True)
50
+    elif get_data[constants.OBJECT] == lib_consts.MEMBERS:
51
+        member_repo = repositories.MemberRepository()
52
+        db_member = member_repo.get(session, id=get_data[lib_consts.ID],
53
+                                    show_deleted=False)
54
+        if db_member:
55
+            provider_member = (
56
+                driver_utils.db_member_to_provider_member(db_member))
57
+            return provider_member.to_dict(recurse=True, render_unsets=True)
58
+    elif get_data[constants.OBJECT] == lib_consts.HEALTHMONITORS:
59
+        hm_repo = repositories.HealthMonitorRepository()
60
+        db_hm = hm_repo.get(session, id=get_data[lib_consts.ID],
61
+                            show_deleted=False)
62
+        if db_hm:
63
+            provider_hm = (
64
+                driver_utils.db_HM_to_provider_HM(db_hm))
65
+            return provider_hm.to_dict(recurse=True, render_unsets=True)
66
+    elif get_data[constants.OBJECT] == lib_consts.L7POLICIES:
67
+        l7policy_repo = repositories.L7PolicyRepository()
68
+        db_l7policy = l7policy_repo.get(session, id=get_data[lib_consts.ID],
69
+                                        show_deleted=False)
70
+        if db_l7policy:
71
+            provider_l7policy = (
72
+                driver_utils.db_l7policy_to_provider_l7policy(db_l7policy))
73
+            return provider_l7policy.to_dict(recurse=True, render_unsets=True)
74
+    elif get_data[constants.OBJECT] == lib_consts.L7RULES:
75
+        l7rule_repo = repositories.L7RuleRepository()
76
+        db_l7rule = l7rule_repo.get(session, id=get_data[lib_consts.ID],
77
+                                    show_deleted=False)
78
+        if db_l7rule:
79
+            provider_l7rule = (
80
+                driver_utils.db_l7rule_to_provider_l7rule(db_l7rule))
81
+            return provider_l7rule.to_dict(recurse=True, render_unsets=True)
82
+    return {}

+ 41
- 0
octavia/api/drivers/driver_agent/driver_listener.py View File

@@ -1,4 +1,5 @@
1 1
 # Copyright 2018 Rackspace, US Inc.
2
+# Copyright 2019 Red Hat, Inc. All rights reserved.
2 3
 #
3 4
 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 5
 # not use this file except in compliance with the License. You may obtain
@@ -23,6 +24,7 @@ from oslo_config import cfg
23 24
 from oslo_log import log as logging
24 25
 from oslo_serialization import jsonutils
25 26
 
27
+from octavia.api.drivers.driver_agent import driver_get
26 28
 from octavia.api.drivers.driver_agent import driver_updater
27 29
 
28 30
 
@@ -80,6 +82,22 @@ class StatsRequestHandler(socketserver.BaseRequestHandler):
80 82
         self.request.sendall(json_data)
81 83
 
82 84
 
85
+class GetRequestHandler(socketserver.BaseRequestHandler):
86
+
87
+    def handle(self):
88
+        # Get the data request
89
+        get_data = _recv(self.request)
90
+
91
+        # Process the get
92
+        response = driver_get.process_get(get_data)
93
+
94
+        # Send the response
95
+        json_data = jsonutils.dump_as_bytes(response)
96
+        len_str = '{}\n'.format(len(json_data)).encode('utf-8')
97
+        self.request.send(len_str)
98
+        self.request.sendall(json_data)
99
+
100
+
83 101
 class ForkingUDSServer(socketserver.ForkingMixIn,
84 102
                        socketserver.UnixStreamServer):
85 103
     pass
@@ -142,3 +160,26 @@ def stats_listener(exit_event):
142 160
     LOG.info('Driver statistics listener shutdown finished.')
143 161
     server.server_close()
144 162
     _cleanup_socket_file(CONF.driver_agent.stats_socket_path)
163
+
164
+
165
+def get_listener(exit_event):
166
+    signal.signal(signal.SIGINT, signal.SIG_IGN)
167
+    signal.signal(signal.SIGHUP, _mutate_config)
168
+
169
+    _cleanup_socket_file(CONF.driver_agent.get_socket_path)
170
+
171
+    server = ForkingUDSServer(CONF.driver_agent.get_socket_path,
172
+                              GetRequestHandler)
173
+
174
+    server.timeout = CONF.driver_agent.get_request_timeout
175
+    server.max_children = CONF.driver_agent.get_max_processes
176
+
177
+    while not exit_event.is_set():
178
+        server.handle_request()
179
+
180
+    LOG.info('Waiting for driver get listener to shutdown...')
181
+    # Can't shut ourselves down as we would deadlock, spawn a thread
182
+    threading.Thread(target=server.shutdown).start()
183
+    LOG.info('Driver get listener shutdown finished.')
184
+    server.server_close()
185
+    _cleanup_socket_file(CONF.driver_agent.get_socket_path)

+ 3
- 3
octavia/api/drivers/utils.py View File

@@ -250,11 +250,11 @@ def listener_dict_to_provider_dict(listener_dict):
250 250
                                                        listener_obj)
251 251
         if 'tls_cert' in cert_dict and cert_dict['tls_cert']:
252 252
             new_listener_dict['default_tls_container_data'] = (
253
-                cert_dict['tls_cert'].to_dict())
253
+                cert_dict['tls_cert'].to_dict(recurse=True))
254 254
         if 'sni_certs' in cert_dict and cert_dict['sni_certs']:
255 255
             sni_data_list = []
256 256
             for sni in cert_dict['sni_certs']:
257
-                sni_data_list.append(sni.to_dict())
257
+                sni_data_list.append(sni.to_dict(recurse=True))
258 258
             new_listener_dict['sni_container_data'] = sni_data_list
259 259
 
260 260
         if listener_obj.client_ca_tls_certificate_id:
@@ -344,7 +344,7 @@ def pool_dict_to_provider_dict(pool_dict):
344 344
                                                        pool_obj)
345 345
         if 'tls_cert' in cert_dict and cert_dict['tls_cert']:
346 346
             new_pool_dict['tls_container_data'] = (
347
-                cert_dict['tls_cert'].to_dict())
347
+                cert_dict['tls_cert'].to_dict(recurse=True))
348 348
 
349 349
         if pool_obj.ca_tls_certificate_id:
350 350
             cert = _get_secret_data(cert_manager, pool_obj.project_id,

+ 14
- 1
octavia/certificates/manager/local.py View File

@@ -18,10 +18,12 @@ import uuid
18 18
 
19 19
 from oslo_config import cfg
20 20
 from oslo_log import log as logging
21
+import six
21 22
 
22 23
 from octavia.certificates.common import local as local_common
23 24
 from octavia.certificates.manager import cert_mgr
24 25
 from octavia.common import exceptions
26
+from octavia.common.tls_utils import cert_parser
25 27
 
26 28
 CONF = cfg.CONF
27 29
 LOG = logging.getLogger(__name__)
@@ -49,6 +51,10 @@ class LocalCertManager(cert_mgr.CertManager):
49 51
         """
50 52
         cert_ref = str(uuid.uuid4())
51 53
         filename_base = os.path.join(CONF.certificates.storage_path, cert_ref)
54
+        if type(certificate) == six.binary_type:
55
+            certificate = certificate.decode('utf-8')
56
+        if type(private_key) == six.binary_type:
57
+            private_key = private_key.decode('utf-8')
52 58
 
53 59
         LOG.info("Storing certificate data on the local filesystem.")
54 60
         try:
@@ -66,12 +72,17 @@ class LocalCertManager(cert_mgr.CertManager):
66 72
 
67 73
             if intermediates:
68 74
                 filename_intermediates = "{0}.int".format(filename_base)
75
+                if type(intermediates) == six.binary_type:
76
+                    intermediates = intermediates.decode('utf-8')
69 77
                 with os.fdopen(os.open(
70 78
                         filename_intermediates, flags, mode), 'w') as int_file:
71 79
                     int_file.write(intermediates)
72 80
 
73 81
             if private_key_passphrase:
74 82
                 filename_pkp = "{0}.pass".format(filename_base)
83
+                if type(private_key_passphrase) == six.binary_type:
84
+                    private_key_passphrase = private_key_passphrase.decode(
85
+                        'utf-8')
75 86
                 with os.fdopen(os.open(
76 87
                         filename_pkp, flags, mode), 'w') as pass_file:
77 88
                     pass_file.write(private_key_passphrase)
@@ -122,6 +133,8 @@ class LocalCertManager(cert_mgr.CertManager):
122 133
         try:
123 134
             with os.fdopen(os.open(filename_intermediates, flags)) as int_file:
124 135
                 cert_data['intermediates'] = int_file.read()
136
+            cert_data['intermediates'] = list(
137
+                cert_parser.get_intermediates_pems(cert_data['intermediates']))
125 138
         except IOError:
126 139
             pass
127 140
 
@@ -184,7 +197,7 @@ class LocalCertManager(cert_mgr.CertManager):
184 197
         filename_base = os.path.join(CONF.certificates.storage_path,
185 198
                                      secret_ref)
186 199
 
187
-        filename_secret = "{0}.pem".format(filename_base)
200
+        filename_secret = "{0}.crt".format(filename_base)
188 201
 
189 202
         secret_data = None
190 203
 

+ 10
- 1
octavia/cmd/driver_agent.py View File

@@ -66,16 +66,25 @@ def main():
66 66
     LOG.info("Driver agent statistics listener process starts:")
67 67
     stats_listener_proc.start()
68 68
 
69
+    get_listener_proc = multiprocessing.Process(
70
+        name='get_listener', target=driver_listener.get_listener,
71
+        args=(exit_event,))
72
+    processes.append(get_listener_proc)
73
+
74
+    LOG.info("Driver agent get listener process starts:")
75
+    get_listener_proc.start()
76
+
69 77
     def process_cleanup(*args, **kwargs):
70 78
         LOG.info("Driver agent exiting due to signal")
71 79
         exit_event.set()
72 80
         status_listener_proc.join()
73 81
         stats_listener_proc.join()
82
+        get_listener_proc.join()
74 83
 
75 84
     signal.signal(signal.SIGTERM, process_cleanup)
76 85
     signal.signal(signal.SIGHUP, partial(
77 86
         _handle_mutate_config, status_listener_proc.pid,
78
-        stats_listener_proc.pid))
87
+        stats_listener_proc.pid, get_listener_proc.pid))
79 88
 
80 89
     try:
81 90
         for process in processes:

+ 10
- 0
octavia/common/config.py View File

@@ -637,6 +637,9 @@ driver_agent_opts = [
637 637
                default='/var/run/octavia/stats.sock',
638 638
                help=_('Path to the driver statistics unix domain socket '
639 639
                       'file.')),
640
+    cfg.StrOpt('get_socket_path',
641
+               default='/var/run/octavia/get.sock',
642
+               help=_('Path to the driver get unix domain socket file.')),
640 643
     cfg.IntOpt('status_request_timeout',
641 644
                default=5,
642 645
                help=_('Time, in seconds, to wait for a status update '
@@ -653,6 +656,13 @@ driver_agent_opts = [
653 656
                default=50,
654 657
                help=_('Maximum number of concurrent processes to use '
655 658
                       'servicing statistics updates.')),
659
+    cfg.IntOpt('get_request_timeout',
660
+               default=5,
661
+               help=_('Time, in seconds, to wait for a get request.')),
662
+    cfg.IntOpt('get_max_processes',
663
+               default=50,
664
+               help=_('Maximum number of concurrent processes to use '
665
+                      'servicing get requests.')),
656 666
     cfg.FloatOpt('max_process_warning_percent',
657 667
                  default=0.75, min=0.01, max=0.99,
658 668
                  help=_('Percentage of max_processes (both status and stats) '

+ 3
- 0
octavia/common/constants.py View File

@@ -351,6 +351,9 @@ TOTAL_CONNECTIONS = 'total_connections'
351 351
 NAME = 'name'
352 352
 PROVIDER_NAME = 'provider_name'
353 353
 
354
+# Database fields
355
+SNI_CONTAINERS = 'sni_containers'
356
+
354 357
 CERT_ROTATE_AMPHORA_FLOW = 'octavia-cert-rotate-amphora-flow'
355 358
 CREATE_AMPHORA_FLOW = 'octavia-create-amphora-flow'
356 359
 CREATE_AMPHORA_FOR_LB_FLOW = 'octavia-create-amp-for-lb-flow'

+ 10
- 2
octavia/common/data_models.py View File

@@ -47,9 +47,15 @@ class BaseDataModel(object):
47 47
                                         calling_classes + [type(self)]),
48 48
                                         recurse=recurse))
49 49
                             else:
50
+                                # TODO(rm_work): Is the idea that if this list
51
+                                #  contains ANY BaseDataModel, that all of them
52
+                                #  are data models, and we may as well quit?
53
+                                #  Or, were we supposed to append a `None` for
54
+                                #  each one? I assume the former?
50 55
                                 ret[attr] = None
56
+                                break
51 57
                         else:
52
-                            ret[attr] = item
58
+                            ret[attr].append(item)
53 59
                 elif isinstance(getattr(self, attr), BaseDataModel):
54 60
                     if type(self) not in calling_classes:
55 61
                         ret[attr] = value.to_dict(
@@ -62,8 +68,10 @@ class BaseDataModel(object):
62 68
                 else:
63 69
                     ret[attr] = value
64 70
             else:
65
-                if isinstance(getattr(self, attr), (BaseDataModel, list)):
71
+                if isinstance(getattr(self, attr), BaseDataModel):
66 72
                     ret[attr] = None
73
+                elif isinstance(getattr(self, attr), list):
74
+                    ret[attr] = []
67 75
                 else:
68 76
                     ret[attr] = value
69 77
 

+ 31
- 8
octavia/common/tls_utils/cert_parser.py View File

@@ -106,6 +106,12 @@ def get_intermediates_pems(intermediates=None):
106 106
               X509 pem block surrounded by BEGIN CERTIFICATE,
107 107
               END CERTIFICATE block tags
108 108
     """
109
+    if isinstance(intermediates, six.string_types):
110
+        try:
111
+            intermediates = intermediates.encode("utf-8")
112
+        except UnicodeDecodeError:
113
+            LOG.debug("Couldn't encode intermediates string, it was probably "
114
+                      "in binary DER format.")
109 115
     if X509_BEG in intermediates:
110 116
         for x509Pem in _split_x509s(intermediates):
111 117
             yield _prepare_x509_cert(_get_x509_from_pem_bytes(x509Pem))
@@ -249,6 +255,8 @@ def get_host_names(certificate):
249 255
               certificate, and 'dns_names' is a list of dNSNames
250 256
               (possibly empty) from the SubjectAltNames of the certificate.
251 257
     """
258
+    if isinstance(certificate, six.string_types):
259
+        certificate = certificate.encode('utf-8')
252 260
     try:
253 261
         cert = x509.load_pem_x509_certificate(certificate,
254 262
                                               backends.default_backend())
@@ -362,19 +370,34 @@ def load_certificates_data(cert_mngr, obj, context=None):
362 370
 
363 371
 
364 372
 def _map_cert_tls_container(cert):
373
+    certificate = cert.get_certificate()
374
+    private_key = cert.get_private_key()
375
+    private_key_passphrase = cert.get_private_key_passphrase()
376
+    intermediates = cert.get_intermediates()
377
+    if isinstance(certificate, six.string_types):
378
+        certificate = certificate.encode('utf-8')
379
+    if isinstance(private_key, six.string_types):
380
+        private_key = private_key.encode('utf-8')
381
+    if isinstance(private_key_passphrase, six.string_types):
382
+        private_key_passphrase = private_key_passphrase.encode('utf-8')
383
+    if intermediates:
384
+        intermediates = [
385
+            (imd.encode('utf-8') if isinstance(imd, six.string_types) else imd)
386
+            for imd in intermediates
387
+        ]
388
+    else:
389
+        intermediates = []
365 390
     return data_models.TLSContainer(
366 391
         # TODO(rm_work): applying nosec here because this is not intended to be
367 392
         # secure, it's just a way to get a consistent ID. Changing this would
368 393
         # break backwards compatibility with existing loadbalancers.
369
-        id=hashlib.sha1(cert.get_certificate()).hexdigest(),  # nosec
370
-        primary_cn=get_primary_cn(cert),
371
-        private_key=prepare_private_key(
372
-            cert.get_private_key(),
373
-            cert.get_private_key_passphrase()),
374
-        certificate=cert.get_certificate(),
375
-        intermediates=cert.get_intermediates())
394
+        id=hashlib.sha1(certificate).hexdigest(),  # nosec
395
+        primary_cn=get_primary_cn(certificate),
396
+        private_key=prepare_private_key(private_key, private_key_passphrase),
397
+        certificate=certificate,
398
+        intermediates=intermediates)
376 399
 
377 400
 
378 401
 def get_primary_cn(tls_cert):
379 402
     """Returns primary CN for Certificate."""
380
-    return get_host_names(tls_cert.get_certificate())['cn']
403
+    return get_host_names(tls_cert)['cn']

octavia/tests/unit/common/sample_configs/sample_certs.py → octavia/tests/common/sample_certs.py View File


octavia/tests/unit/api/drivers/sample_data_models.py → octavia/tests/common/sample_data_models.py View File


+ 2
- 1
octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py View File

@@ -2607,7 +2607,8 @@ class TestServerTestCase(base.TestCase):
2607 2607
             self.assertEqual(500, rv.status_code)
2608 2608
 
2609 2609
     def test_version_discovery(self):
2610
-        self.test_client = server.Server().app.test_client()
2610
+        with mock.patch('distro.id', return_value='ubuntu'):
2611
+            self.test_client = server.Server().app.test_client()
2611 2612
         expected_dict = {'api_version': api_server.VERSION}
2612 2613
         rv = self.test_client.get('/')
2613 2614
         self.assertEqual(200, rv.status_code)

+ 11
- 0
octavia/tests/functional/api/drivers/__init__.py View File

@@ -0,0 +1,11 @@
1
+#    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.

+ 11
- 0
octavia/tests/functional/api/drivers/driver_agent/__init__.py View File

@@ -0,0 +1,11 @@
1
+#    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.

+ 334
- 0
octavia/tests/functional/api/drivers/driver_agent/test_driver_agent.py View File

@@ -0,0 +1,334 @@
1
+# Copyright 2019 Red Hat, Inc. All rights reserved.
2
+#
3
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+# not use this file except in compliance with the License. You may obtain
5
+# 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, WITHOUT
11
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+# License for the specific language governing permissions and limitations
13
+# under the License.
14
+
15
+import copy
16
+import multiprocessing
17
+
18
+from octavia_lib.api.drivers import driver_lib as octavia_driver_lib
19
+from octavia_lib.common import constants as lib_consts
20
+from oslo_config import cfg
21
+from oslo_config import fixture as oslo_fixture
22
+from oslo_utils import uuidutils
23
+from stevedore import driver as stevedore_driver
24
+
25
+from octavia.api.drivers.driver_agent import driver_listener
26
+from octavia.common import config
27
+from octavia.common import constants
28
+from octavia.db import repositories
29
+from octavia.tests.common import sample_certs
30
+from octavia.tests.common import sample_data_models
31
+from octavia.tests.functional.db import base
32
+
33
+CONF = cfg.CONF
34
+
35
+
36
+class DriverAgentTest(base.OctaviaDBTestBase):
37
+
38
+    def _process_cleanup(self):
39
+        self.exit_event.set()
40
+        self.status_listener_proc.join(5)
41
+        self.stats_listener_proc.join(5)
42
+        self.get_listener_proc.join(5)
43
+
44
+    def setUp(self):
45
+        status_socket_file = '/tmp/octavia-{}.status.sock'.format(
46
+            uuidutils.generate_uuid())
47
+        stats_socket_file = '/tmp/octavia-{}.stats.sock'.format(
48
+            uuidutils.generate_uuid())
49
+        get_socket_file = '/tmp/octavia-{}.get.sock'.format(
50
+            uuidutils.generate_uuid())
51
+        sqlite_db_file = '/tmp/octavia-{}.sqlite.db'.format(
52
+            uuidutils.generate_uuid())
53
+        sqlite_db_connection = 'sqlite:///{}'.format(sqlite_db_file)
54
+
55
+        # Note that because the driver agent is a multi-process
56
+        # agent we must use a sqlite file rather than an
57
+        # in-memory instance.
58
+        super(DriverAgentTest, self).setUp(
59
+            connection_string=sqlite_db_connection)
60
+
61
+        conf = self.useFixture(oslo_fixture.Config(config.cfg.CONF))
62
+        conf.config(group="driver_agent",
63
+                    status_socket_path=status_socket_file)
64
+        conf.config(group="driver_agent",
65
+                    stats_socket_path=stats_socket_file)
66
+        conf.config(group="driver_agent", status_request_timeout=1)
67
+        conf.config(group="driver_agent", get_socket_path=get_socket_file)
68
+        conf.config(group="certificates", cert_manager='local_cert_manager')
69
+        conf.config(group="certificates", storage_path='/tmp')
70
+
71
+        # Set up the certificate
72
+        cert_manager = stevedore_driver.DriverManager(
73
+            namespace='octavia.cert_manager',
74
+            name=CONF.certificates.cert_manager,
75
+            invoke_on_load=True,
76
+        ).driver
77
+        self.cert_ref = cert_manager.store_cert(
78
+            None,
79
+            sample_certs.X509_CERT,
80
+            sample_certs.X509_CERT_KEY_ENCRYPTED,
81
+            sample_certs.X509_IMDS,
82
+            private_key_passphrase=sample_certs.X509_CERT_KEY_PASSPHRASE)
83
+        self.addCleanup(cert_manager.delete_cert, None, self.cert_ref)
84
+
85
+        self.exit_event = multiprocessing.Event()
86
+
87
+        self.status_listener_proc = multiprocessing.Process(
88
+            name='status_listener', target=driver_listener.status_listener,
89
+            args=(self.exit_event,))
90
+        # TODO(johnsom) Remove once https://bugs.python.org/issue6721
91
+        #               is resolved.
92
+        self.status_listener_proc.daemon = True
93
+
94
+        self.status_listener_proc.start()
95
+
96
+        self.stats_listener_proc = multiprocessing.Process(
97
+            name='stats_listener', target=driver_listener.stats_listener,
98
+            args=(self.exit_event,))
99
+        # TODO(johnsom) Remove once https://bugs.python.org/issue6721
100
+        #               is resolved.
101
+        self.stats_listener_proc.daemon = True
102
+
103
+        self.stats_listener_proc.start()
104
+
105
+        self.get_listener_proc = multiprocessing.Process(
106
+            name='get_listener', target=driver_listener.get_listener,
107
+            args=(self.exit_event,))
108
+        # TODO(johnsom) Remove once https://bugs.python.org/issue6721
109
+        #               is resolved.
110
+        self.get_listener_proc.daemon = True
111
+
112
+        self.get_listener_proc.start()
113
+
114
+        self.addCleanup(self._process_cleanup)
115
+
116
+        self.driver_lib = octavia_driver_lib.DriverLibrary(
117
+            status_socket=status_socket_file,
118
+            stats_socket=stats_socket_file,
119
+            get_socket=get_socket_file)
120
+
121
+        self.sample_data = sample_data_models.SampleDriverDataModels()
122
+        self.repos = repositories.Repositories()
123
+
124
+        # Create the full load balancer in the database
125
+        self.tls_container_dict = {
126
+            'certificate': sample_certs.X509_CERT.decode('utf-8'),
127
+            'id': sample_certs.X509_CERT_SHA1,
128
+            'intermediates': [
129
+                i.decode('utf-8') for i in sample_certs.X509_IMDS_LIST],
130
+            'passphrase': None,
131
+            'primary_cn': sample_certs.X509_CERT_CN,
132
+            'private_key': sample_certs.X509_CERT_KEY.decode('utf-8')}
133
+
134
+        # ### Create load balancer
135
+        self.repos.flavor_profile.create(
136
+            self.session, id=self.sample_data.flavor_profile_id,
137
+            provider_name='amphora', flavor_data='{"something": "else"}')
138
+        self.repos.flavor.create(
139
+            self.session, id=self.sample_data.flavor_id,
140
+            enabled=True, flavor_profile_id=self.sample_data.flavor_profile_id)
141
+        self.repos.create_load_balancer_and_vip(
142
+            self.session, self.sample_data.test_loadbalancer1_dict,
143
+            self.sample_data.test_vip_dict)
144
+
145
+        # ### Create Pool
146
+        pool_dict = copy.deepcopy(self.sample_data.test_pool1_dict)
147
+
148
+        pool_dict['load_balancer_id'] = self.sample_data.lb_id
149
+
150
+        # Use a live certificate
151
+        pool_dict['tls_certificate_id'] = self.cert_ref
152
+        pool_dict['ca_tls_certificate_id'] = self.cert_ref
153
+        pool_dict['crl_container_id'] = self.cert_ref
154
+
155
+        # Remove items that are linked in the DB
156
+        del pool_dict[lib_consts.MEMBERS]
157
+        del pool_dict[constants.HEALTH_MONITOR]
158
+        del pool_dict['session_persistence']
159
+        del pool_dict[lib_consts.LISTENERS]
160
+        del pool_dict[lib_consts.L7POLICIES]
161
+
162
+        self.repos.pool.create(self.session, **pool_dict)
163
+
164
+        self.repos.session_persistence.create(
165
+            self.session, pool_id=self.sample_data.pool1_id,
166
+            type=lib_consts.SESSION_PERSISTENCE_SOURCE_IP)
167
+
168
+        self.provider_pool_dict = copy.deepcopy(
169
+            self.sample_data.provider_pool1_dict)
170
+        self.provider_pool_dict[
171
+            constants.LISTENER_ID] = self.sample_data.listener1_id
172
+
173
+        # Fix for render_unsets = True
174
+        self.provider_pool_dict['session_persistence']['cookie_name'] = None
175
+        self.provider_pool_dict['session_persistence'][
176
+            'persistence_granularity'] = None
177
+        self.provider_pool_dict['session_persistence'][
178
+            'persistence_timeout'] = None
179
+
180
+        # Use a live certificate
181
+        self.provider_pool_dict['tls_container_data'] = self.tls_container_dict
182
+        self.provider_pool_dict['tls_container_ref'] = self.cert_ref
183
+        self.provider_pool_dict[
184
+            'ca_tls_container_data'] = sample_certs.X509_CERT.decode('utf-8')
185
+        self.provider_pool_dict['ca_tls_container_ref'] = self.cert_ref
186
+        self.provider_pool_dict[
187
+            'crl_container_data'] = sample_certs.X509_CERT.decode('utf-8')
188
+        self.provider_pool_dict['crl_container_ref'] = self.cert_ref
189
+
190
+        # ### Create Member
191
+        member_dict = copy.deepcopy(self.sample_data.test_member1_dict)
192
+        self.repos.member.create(self.session, **member_dict)
193
+        self.provider_pool_dict[lib_consts.MEMBERS] = [
194
+            self.sample_data.provider_member1_dict]
195
+
196
+        # ### Create Health Monitor
197
+        hm_dict = copy.deepcopy(self.sample_data.test_hm1_dict)
198
+        self.repos.health_monitor.create(self.session, **hm_dict)
199
+        self.provider_pool_dict[
200
+            'healthmonitor'] = self.sample_data.provider_hm1_dict
201
+
202
+        # ### Create Listener
203
+        listener_dict = copy.deepcopy(self.sample_data.test_listener1_dict)
204
+        listener_dict['default_pool_id'] = self.sample_data.pool1_id
205
+
206
+        # Remove items that are linked in the DB
207
+        del listener_dict[lib_consts.L7POLICIES]
208
+        del listener_dict['default_pool']
209
+        del listener_dict[constants.SNI_CONTAINERS]
210
+
211
+        # Use a live certificate
212
+        listener_dict['tls_certificate_id'] = self.cert_ref
213
+        listener_dict['client_ca_tls_certificate_id'] = self.cert_ref
214
+        listener_dict['client_crl_container_id'] = self.cert_ref
215
+
216
+        self.repos.listener.create(self.session,
217
+                                   **listener_dict)
218
+        self.repos.sni.create(self.session,
219
+                              listener_id=self.sample_data.listener1_id,
220
+                              tls_container_id=self.cert_ref, position=1)
221
+
222
+        # Add our live certs in that differ from the fake certs in sample_data
223
+        self.provider_listener_dict = copy.deepcopy(
224
+            self.sample_data.provider_listener1_dict)
225
+        self.provider_listener_dict['allowed_cidrs'] = None
226
+        self.provider_listener_dict[
227
+            'default_tls_container_ref'] = self.cert_ref
228
+        self.provider_listener_dict[
229
+            'default_tls_container_data'] = self.tls_container_dict
230
+        self.provider_listener_dict[
231
+            'client_ca_tls_container_ref'] = self.cert_ref
232
+        self.provider_listener_dict['client_ca_tls_container_data'] = (
233
+            sample_certs.X509_CERT.decode('utf-8'))
234
+        self.provider_listener_dict['client_crl_container_ref'] = self.cert_ref
235
+        self.provider_listener_dict['client_crl_container_data'] = (
236
+            sample_certs.X509_CERT.decode('utf-8'))
237
+        self.provider_listener_dict[
238
+            'sni_container_data'] = [self.tls_container_dict]
239
+        self.provider_listener_dict['sni_container_refs'] = [self.cert_ref]
240
+
241
+        self.provider_listener_dict['default_pool'] = self.provider_pool_dict
242
+        self.provider_listener_dict[
243
+            'default_pool_id'] = self.sample_data.pool1_id
244
+
245
+        self.provider_listener_dict[lib_consts.L7POLICIES] = [
246
+            self.sample_data.provider_l7policy1_dict]
247
+
248
+        # ### Create L7 Policy
249
+        l7policy_dict = copy.deepcopy(self.sample_data.test_l7policy1_dict)
250
+        del l7policy_dict['l7rules']
251
+        self.repos.l7policy.create(self.session, **l7policy_dict)
252
+
253
+        # ### Create L7 Rules
254
+        l7rule_dict = copy.deepcopy(self.sample_data.test_l7rule1_dict)
255
+        self.repos.l7rule.create(self.session, **l7rule_dict)
256
+        l7rule2_dict = copy.deepcopy(self.sample_data.test_l7rule2_dict)
257
+        self.repos.l7rule.create(self.session, **l7rule2_dict)
258
+
259
+        self.provider_lb_dict = copy.deepcopy(
260
+            self.sample_data.provider_loadbalancer_tree_dict)
261
+        self.provider_lb_dict[lib_consts.POOLS] = [self.provider_pool_dict]
262
+        self.provider_lb_dict[
263
+            lib_consts.LISTENERS] = [self.provider_listener_dict]
264
+
265
+    def test_get_loadbalancer(self):
266
+        result = self.driver_lib.get_loadbalancer(self.sample_data.lb_id)
267
+
268
+        self.assertEqual(self.provider_lb_dict,
269
+                         result.to_dict(render_unsets=True, recurse=True))
270
+
271
+        # Test non-existent load balancer
272
+        result = self.driver_lib.get_loadbalancer('bogus')
273
+        self.assertIsNone(result)
274
+
275
+    def test_get_listener(self):
276
+        result = self.driver_lib.get_listener(self.sample_data.listener1_id)
277
+
278
+        # We need to recurse here to pick up the SNI data
279
+        self.assertEqual(self.provider_listener_dict,
280
+                         result.to_dict(render_unsets=True, recurse=True))
281
+
282
+        # Test non-existent listener
283
+        result = self.driver_lib.get_listener('bogus')
284
+        self.assertIsNone(result)
285
+
286
+    def test_get_pool(self):
287
+        result = self.driver_lib.get_pool(self.sample_data.pool1_id)
288
+
289
+        self.assertEqual(self.provider_pool_dict,
290
+                         result.to_dict(render_unsets=True, recurse=True))
291
+
292
+        # Test non-existent pool
293
+        result = self.driver_lib.get_pool('bogus')
294
+        self.assertIsNone(result)
295
+
296
+    def test_get_member(self):
297
+        result = self.driver_lib.get_member(self.sample_data.member1_id)
298
+
299
+        self.assertEqual(self.sample_data.provider_member1_dict,
300
+                         result.to_dict(render_unsets=True))
301
+
302
+        # Test non-existent member
303
+        result = self.driver_lib.get_member('bogus')
304
+        self.assertIsNone(result)
305
+
306
+    def test_get_healthmonitor(self):
307
+        result = self.driver_lib.get_healthmonitor(self.sample_data.hm1_id)
308
+
309
+        self.assertEqual(self.sample_data.provider_hm1_dict,
310
+                         result.to_dict(render_unsets=True))
311
+
312
+        # Test non-existent health monitor
313
+        result = self.driver_lib.get_healthmonitor('bogus')
314
+        self.assertIsNone(result)
315
+
316
+    def test_get_l7policy(self):
317
+        result = self.driver_lib.get_l7policy(self.sample_data.l7policy1_id)
318
+
319
+        self.assertEqual(self.sample_data.provider_l7policy1_dict,
320
+                         result.to_dict(render_unsets=True, recurse=True))
321
+
322
+        # Test non-existent L7 policy
323
+        result = self.driver_lib.get_l7policy('bogus')
324
+        self.assertIsNone(result)
325
+
326
+    def test_get_l7rule(self):
327
+        result = self.driver_lib.get_l7rule(self.sample_data.l7rule1_id)
328
+
329
+        self.assertEqual(self.sample_data.provider_l7rule1_dict,
330
+                         result.to_dict(render_unsets=True))
331
+
332
+        # Test non-existent L7 rule
333
+        result = self.driver_lib.get_l7rule('bogus')
334
+        self.assertIsNone(result)

+ 1
- 1
octavia/tests/functional/api/v2/test_listener.py View File

@@ -25,8 +25,8 @@ from octavia.common import constants
25 25
 import octavia.common.context
26 26
 from octavia.common import data_models
27 27
 from octavia.common import exceptions
28
+from octavia.tests.common import sample_certs
28 29
 from octavia.tests.functional.api.v2 import base
29
-from octavia.tests.unit.common.sample_configs import sample_certs
30 30
 
31 31
 
32 32
 class TestListener(base.BaseAPITest):

+ 1
- 1
octavia/tests/functional/api/v2/test_pool.py View File

@@ -22,8 +22,8 @@ from octavia.common import constants
22 22
 import octavia.common.context
23 23
 from octavia.common import data_models
24 24
 from octavia.common import exceptions
25
+from octavia.tests.common import sample_certs
25 26
 from octavia.tests.functional.api.v2 import base
26
-from octavia.tests.unit.common.sample_configs import sample_certs
27 27
 
28 28
 
29 29
 class TestPool(base.BaseAPITest):

+ 23
- 8
octavia/tests/functional/db/base.py View File

@@ -12,7 +12,11 @@
12 12
 #    License for the specific language governing permissions and limitations
13 13
 #    under the License.
14 14
 
15
+import os
16
+
17
+from oslo_config import cfg
15 18
 from oslo_config import fixture as oslo_fixture
19
+from oslo_db.sqlalchemy import session as db_session
16 20
 from oslo_db.sqlalchemy import test_base
17 21
 
18 22
 from octavia.common import config
@@ -24,29 +28,40 @@ from octavia.db import models
24 28
 
25 29
 class OctaviaDBTestBase(test_base.DbTestCase):
26 30
 
27
-    def setUp(self):
31
+    def setUp(self, connection_string='sqlite://'):
28 32
         super(OctaviaDBTestBase, self).setUp()
29 33
         # NOTE(blogan): doing this for now because using the engine and
30 34
         # session set up in the fixture for test_base.DbTestCase does not work
31 35
         # with the API functional tests.  Need to investigate more if this
32 36
         # becomes a problem
33 37
         conf = self.useFixture(oslo_fixture.Config(config.cfg.CONF))
34
-        conf.config(group="database", connection='sqlite://')
38
+        conf.config(group="database", connection=connection_string)
39
+
40
+        # We need to get our own Facade so that the file backed sqlite tests
41
+        # don't use the _FACADE singleton. Some tests will use in-memory
42
+        # sqlite, some will use a file backed sqlite.
43
+        if 'sqlite:///' in connection_string:
44
+            facade = db_session.EngineFacade.from_config(cfg.CONF,
45
+                                                         sqlite_fk=True)
46
+            engine = facade.get_engine()
47
+            self.session = facade.get_session(expire_on_commit=True,
48
+                                              autocommit=True)
49
+        else:
50
+            engine = db_api.get_engine()
51
+            self.session = db_api.get_session()
35 52
 
36
-        # needed for closure
37
-        engine = db_api.get_engine()
38
-        session = db_api.get_session()
39 53
         base_models.BASE.metadata.create_all(engine)
40
-        self._seed_lookup_tables(session)
54
+        self._seed_lookup_tables(self.session)
41 55
 
42 56
         def clear_tables():
43 57
             """Unregister all data models."""
44 58
             base_models.BASE.metadata.drop_all(engine)
59
+            # If we created a file, clean it up too
60
+            if 'sqlite:///' in connection_string:
61
+                os.remove(connection_string.replace('sqlite:///', ''))
45 62
 
46 63
         self.addCleanup(clear_tables)
47 64
 
48
-        self.session = session
49
-
50 65
     def _seed_lookup_tables(self, session):
51 66
         self._seed_lookup_table(
52 67
             session, constants.SUPPORTED_PROVISIONING_STATUSES,

+ 1
- 1
octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_0_5.py View File

@@ -29,8 +29,8 @@ from octavia.amphorae.drivers.haproxy import rest_api_driver as driver
29 29
 from octavia.common import constants
30 30
 from octavia.db import models
31 31
 from octavia.network import data_models as network_models
32
+from octavia.tests.common import sample_certs
32 33
 from octavia.tests.unit import base
33
-from octavia.tests.unit.common.sample_configs import sample_certs
34 34
 from octavia.tests.unit.common.sample_configs import sample_configs_split
35 35
 
36 36
 API_VERSION = '0.5'

+ 1
- 1
octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_1_0.py View File

@@ -29,8 +29,8 @@ from octavia.amphorae.drivers.haproxy import rest_api_driver as driver
29 29
 from octavia.common import constants
30 30
 from octavia.db import models
31 31
 from octavia.network import data_models as network_models
32
+from octavia.tests.common import sample_certs
32 33
 from octavia.tests.unit import base
33
-from octavia.tests.unit.common.sample_configs import sample_certs
34 34
 from octavia.tests.unit.common.sample_configs import sample_configs_combined
35 35
 
36 36
 API_VERSION = '1.0'

+ 1
- 1
octavia/tests/unit/api/common/test_types.py View File

@@ -160,7 +160,7 @@ class TestDataModelToDict(base.TestCase):
160 160
     NO_RECURSE_RESULT = {'parent': None,
161 161
                          'text': 'parent_text',
162 162
                          'child': None,
163
-                         'children': None}
163
+                         'children': []}
164 164
 
165 165
     def setUp(self):
166 166
         super(TestDataModelToDict, self).setUp()

+ 2
- 3
octavia/tests/unit/api/drivers/amphora_driver/v1/test_amphora_driver.py View File

@@ -21,7 +21,7 @@ from octavia_lib.api.drivers import exceptions
21 21
 from octavia.api.drivers.amphora_driver.v1 import driver
22 22
 from octavia.common import constants as consts
23 23
 from octavia.network import base as network_base
24
-from octavia.tests.unit.api.drivers import sample_data_models
24
+from octavia.tests.common import sample_data_models
25 25
 from octavia.tests.unit import base
26 26
 
27 27
 
@@ -585,8 +585,7 @@ class TestAmphoraDriver(base.TestRpc):
585 585
             self.assertRaises(exceptions.DriverError,
586 586
                               self.amp_driver.get_supported_flavor_metadata)
587 587
 
588
-    @mock.patch('jsonschema.validators.requests')
589
-    def test_validate_flavor(self, mock_validate):
588
+    def test_validate_flavor(self):
590 589
         ref_dict = {consts.LOADBALANCER_TOPOLOGY: consts.TOPOLOGY_SINGLE}
591 590
         self.amp_driver.validate_flavor(ref_dict)
592 591
 

+ 2
- 3
octavia/tests/unit/api/drivers/amphora_driver/v2/test_amphora_driver.py View File

@@ -21,7 +21,7 @@ from octavia_lib.api.drivers import exceptions
21 21
 from octavia.api.drivers.amphora_driver.v2 import driver
22 22
 from octavia.common import constants as consts
23 23
 from octavia.network import base as network_base
24
-from octavia.tests.unit.api.drivers import sample_data_models
24
+from octavia.tests.common import sample_data_models
25 25
 from octavia.tests.unit import base
26 26
 
27 27
 
@@ -585,8 +585,7 @@ class TestAmphoraDriver(base.TestRpc):
585 585
             self.assertRaises(exceptions.DriverError,
586 586
                               self.amp_driver.get_supported_flavor_metadata)
587 587
 
588
-    @mock.patch('jsonschema.validators.requests')
589
-    def test_validate_flavor(self, mock_validate):
588
+    def test_validate_flavor(self):
590 589
         ref_dict = {consts.LOADBALANCER_TOPOLOGY: consts.TOPOLOGY_SINGLE}
591 590
         self.amp_driver.validate_flavor(ref_dict)
592 591
 

+ 121
- 0
octavia/tests/unit/api/drivers/driver_agent/test_driver_get.py View File

@@ -0,0 +1,121 @@
1
+# Copyright 2019 Red Hat, Inc. All rights reserved.
2
+#
3
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+# not use this file except in compliance with the License. You may obtain
5
+# 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, WITHOUT
11
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+# License for the specific language governing permissions and limitations
13
+# under the License.
14
+
15
+import mock
16
+
17
+from octavia_lib.common import constants as lib_consts
18
+from oslo_utils import uuidutils
19
+
20
+from octavia.api.drivers.driver_agent import driver_get
21
+from octavia.common import constants
22
+import octavia.tests.unit.base as base
23
+
24
+
25
+class TestDriverGet(base.TestCase):
26
+
27
+    @mock.patch('octavia.db.api.get_session')
28
+    def _test_process_get_object(self, object_name, mock_object_repo,
29
+                                 mock_object_to_provider, mock_get_session):
30
+        mock_get_session.return_value = 'bogus_session'
31
+        object_repo_mock = mock.MagicMock()
32
+        mock_object_repo.return_value = object_repo_mock
33
+        db_object_mock = mock.MagicMock()
34
+        object_repo_mock.get.return_value = db_object_mock
35
+
36
+        mock_prov_object = mock.MagicMock()
37
+        mock_object_to_provider.return_value = mock_prov_object
38
+        ref_prov_dict = mock_prov_object.to_dict(recurse=True,
39
+                                                 render_unsets=True)
40
+
41
+        object_id = uuidutils.generate_uuid()
42
+
43
+        data = {constants.OBJECT: object_name, lib_consts.ID: object_id}
44
+
45
+        # Happy path
46
+        result = driver_get.process_get(data)
47
+
48
+        mock_object_repo.assert_called_once_with()
49
+        object_repo_mock.get.assert_called_once_with(
50
+            'bogus_session', id=object_id, show_deleted=False)
51
+        mock_object_to_provider.assert_called_once_with(db_object_mock)
52
+        self.assertEqual(ref_prov_dict, result)
53
+
54
+        # No matching listener
55
+        mock_object_repo.reset_mock()
56
+        mock_object_to_provider.reset_mock()
57
+
58
+        object_repo_mock.get.return_value = None
59
+
60
+        result = driver_get.process_get(data)
61
+
62
+        mock_object_repo.assert_called_once_with()
63
+        object_repo_mock.get.assert_called_once_with(
64
+            'bogus_session', id=object_id, show_deleted=False)
65
+        mock_object_to_provider.assert_not_called()
66
+        self.assertEqual({}, result)
67
+
68
+    @mock.patch('octavia.api.drivers.utils.'
69
+                'db_loadbalancer_to_provider_loadbalancer')
70
+    @mock.patch('octavia.db.repositories.LoadBalancerRepository')
71
+    def test_process_get_loadbalancer(self, mock_lb_repo, mock_lb_to_provider):
72
+        self._test_process_get_object(
73
+            lib_consts.LOADBALANCERS, mock_lb_repo, mock_lb_to_provider)
74
+
75
+    @mock.patch('octavia.api.drivers.utils.db_listener_to_provider_listener')
76
+    @mock.patch('octavia.db.repositories.ListenerRepository')
77
+    def test_process_get_listener(self, mock_listener_repo,
78
+                                  mock_listener_to_provider):
79
+        self._test_process_get_object(lib_consts.LISTENERS, mock_listener_repo,
80
+                                      mock_listener_to_provider)
81
+
82
+    @mock.patch('octavia.api.drivers.utils.db_pool_to_provider_pool')
83
+    @mock.patch('octavia.db.repositories.PoolRepository')
84
+    def test_process_get_pool(self, mock_pool_repo, mock_pool_to_provider):
85
+        self._test_process_get_object(lib_consts.POOLS, mock_pool_repo,
86
+                                      mock_pool_to_provider)
87
+
88
+    @mock.patch('octavia.api.drivers.utils.db_member_to_provider_member')
89
+    @mock.patch('octavia.db.repositories.MemberRepository')
90
+    def test_process_get_member(self, mock_member_repo,
91
+                                mock_member_to_provider):
92
+        self._test_process_get_object(lib_consts.MEMBERS, mock_member_repo,
93
+                                      mock_member_to_provider)
94
+
95
+    @mock.patch('octavia.api.drivers.utils.db_HM_to_provider_HM')
96
+    @mock.patch('octavia.db.repositories.HealthMonitorRepository')
97
+    def test_process_get_healthmonitor(self, mock_hm_repo,
98
+                                       mock_hm_to_provider):
99
+        self._test_process_get_object(lib_consts.HEALTHMONITORS, mock_hm_repo,
100
+                                      mock_hm_to_provider)
101
+
102
+    @mock.patch('octavia.api.drivers.utils.db_l7policy_to_provider_l7policy')
103
+    @mock.patch('octavia.db.repositories.L7PolicyRepository')
104
+    def test_process_get_l7policy(self, mock_l7policy_repo,
105
+                                  mock_l7policy_to_provider):
106
+        self._test_process_get_object(lib_consts.L7POLICIES,
107
+                                      mock_l7policy_repo,
108
+                                      mock_l7policy_to_provider)
109
+
110
+    @mock.patch('octavia.api.drivers.utils.db_l7rule_to_provider_l7rule')
111
+    @mock.patch('octavia.db.repositories.L7RuleRepository')
112
+    def test_process_get_l7rule(self, mock_l7rule_repo,
113
+                                mock_l7rule_to_provider):
114
+        self._test_process_get_object(lib_consts.L7RULES, mock_l7rule_repo,
115
+                                      mock_l7rule_to_provider)
116
+
117
+    @mock.patch('octavia.db.api.get_session')
118
+    def test_process_get_bogus_object(self, mock_get_session):
119
+        data = {constants.OBJECT: 'bogus', lib_consts.ID: 'bad ID'}
120
+        result = driver_get.process_get(data)
121
+        self.assertEqual({}, result)

+ 46
- 0
octavia/tests/unit/api/drivers/driver_agent/test_driver_listener.py View File

@@ -110,6 +110,31 @@ class TestDriverListener(base.TestCase):
110 110
         mock_send.assert_called_with(b'15\n')
111 111
         mock_sendall.assert_called_with(jsonutils.dump_as_bytes(TEST_OBJECT))
112 112
 
113
+    @mock.patch('octavia.api.drivers.driver_agent.driver_get.'
114
+                'process_get')
115
+    @mock.patch('octavia.api.drivers.driver_agent.driver_listener._recv')
116
+    def test_GetRequestHandler_handle(self, mock_recv, mock_process_get):
117
+        TEST_OBJECT = {"test": "msg"}
118
+
119
+        mock_recv.return_value = 'bogus'
120
+
121
+        mock_process_get.return_value = TEST_OBJECT
122
+        mock_request = mock.MagicMock()
123
+        mock_send = mock.MagicMock()
124
+        mock_sendall = mock.MagicMock()
125
+        mock_request.send = mock_send
126
+        mock_request.sendall = mock_sendall
127
+
128
+        GetRequestHandler = driver_listener.GetRequestHandler(
129
+            mock_request, 'bogus', 'bogus')
130
+        GetRequestHandler.handle()
131
+
132
+        mock_recv.assert_called_with(mock_request)
133
+        mock_process_get.assert_called_with('bogus')
134
+
135
+        mock_send.assert_called_with(b'15\n')
136
+        mock_sendall.assert_called_with(jsonutils.dump_as_bytes(TEST_OBJECT))
137
+
113 138
     @mock.patch('octavia.api.drivers.driver_agent.driver_listener.CONF')
114 139
     def test_mutate_config(self, mock_conf):
115 140
         driver_listener._mutate_config()
@@ -169,3 +194,24 @@ class TestDriverListener(base.TestCase):
169 194
         driver_listener.stats_listener(mock_exit_event)
170 195
         mock_server.handle_request.assert_called()
171 196
         mock_server.server_close.assert_called_once()
197
+
198
+    @mock.patch('octavia.api.drivers.driver_agent.driver_listener.'
199
+                '_cleanup_socket_file')
200
+    @mock.patch('octavia.api.drivers.driver_agent.driver_listener.signal')
201
+    @mock.patch('octavia.api.drivers.driver_agent.driver_listener.'
202
+                'ForkingUDSServer')
203
+    def test_get_listener(self, mock_forking_server,
204
+                          mock_signal, mock_cleanup):
205
+        mock_server = mock.MagicMock()
206
+        mock_active_children = mock.PropertyMock(
207
+            side_effect=['a', 'a', 'a',
208
+                         'a' * CONF.driver_agent.status_max_processes, 'a',
209
+                         'a' * 1000, ''])
210
+        type(mock_server).active_children = mock_active_children
211
+        mock_forking_server.return_value = mock_server
212
+        mock_exit_event = mock.MagicMock()
213
+        mock_exit_event.is_set.side_effect = [False, False, False, False, True]
214
+
215
+        driver_listener.get_listener(mock_exit_event)
216
+        mock_server.handle_request.assert_called()
217
+        mock_server.server_close.assert_called_once()

+ 19
- 4
octavia/tests/unit/api/drivers/test_utils.py View File

@@ -24,7 +24,7 @@ from octavia.api.drivers import utils
24 24
 from octavia.common import constants
25 25
 from octavia.common import data_models
26 26
 from octavia.common import exceptions
27
-from octavia.tests.unit.api.drivers import sample_data_models
27
+from octavia.tests.common import sample_data_models
28 28
 from octavia.tests.unit import base
29 29
 
30 30
 
@@ -140,12 +140,20 @@ class TestUtils(base.TestCase):
140 140
                         'operating_status': constants.OFFLINE,
141 141
                         'flavor_id': 'flavor_id',
142 142
                         'provider': 'noop_driver'}
143
+        ref_listeners = copy.deepcopy(self.sample_data.provider_listeners)
144
+        # TODO(johnsom) Remove this once the listener ACLs patch merges
145
+        # https://review.opendev.org/#/c/659626/
146
+        for listener in ref_listeners:
147
+            try:
148
+                del listener.allowed_cidrs
149
+            except AttributeError:
150
+                pass
143 151
         ref_prov_lb_dict = {
144 152
             'vip_address': self.sample_data.ip_address,
145 153
             'admin_state_up': True,
146 154
             'loadbalancer_id': self.sample_data.lb_id,
147 155
             'vip_subnet_id': self.sample_data.subnet_id,
148
-            'listeners': self.sample_data.provider_listeners,
156
+            'listeners': ref_listeners,
149 157
             'description': '',
150 158
             'project_id': self.sample_data.project_id,
151 159
             'vip_port_id': self.sample_data.port_id,
@@ -211,8 +219,15 @@ class TestUtils(base.TestCase):
211 219
                                        'sni_certs': [cert2, cert3]}
212 220
         provider_listeners = utils.db_listeners_to_provider_listeners(
213 221
             self.sample_data.test_db_listeners)
214
-        self.assertEqual(self.sample_data.provider_listeners,
215
-                         provider_listeners)
222
+        ref_listeners = copy.deepcopy(self.sample_data.provider_listeners)
223
+        # TODO(johnsom) Remove this once the listener ACLs patch merges
224
+        # https://review.opendev.org/#/c/659626/
225
+        for listener in ref_listeners:
226
+            try:
227
+                del listener.allowed_cidrs
228
+            except AttributeError:
229
+                pass
230
+        self.assertEqual(ref_listeners, provider_listeners)
216 231
 
217 232
     @mock.patch('octavia.api.drivers.utils._get_secret_data')
218 233
     @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')

+ 1
- 1
octavia/tests/unit/certificates/common/test_barbican.py View File

@@ -18,8 +18,8 @@ import mock
18 18
 import six
19 19
 
20 20
 import octavia.certificates.common.barbican as barbican_common
21
+import octavia.tests.common.sample_certs as sample
21 22
 import octavia.tests.unit.base as base
22
-import octavia.tests.unit.common.sample_configs.sample_certs as sample
23 23
 
24 24
 
25 25
 class TestBarbicanCert(base.TestCase):

+ 1
- 1
octavia/tests/unit/certificates/manager/test_barbican.py View File

@@ -21,8 +21,8 @@ import octavia.certificates.common.barbican as barbican_common
21 21
 import octavia.certificates.common.cert as cert
22 22
 import octavia.certificates.manager.barbican as barbican_cert_mgr
23 23
 from octavia.common import exceptions
24
+import octavia.tests.common.sample_certs as sample
24 25
 import octavia.tests.unit.base as base
25
-import octavia.tests.unit.common.sample_configs.sample_certs as sample
26 26
 
27 27
 
28 28
 PROJECT_ID = "12345"

+ 1
- 1
octavia/tests/unit/certificates/manager/test_barbican_legacy.py View File

@@ -21,8 +21,8 @@ import six
21 21
 import octavia.certificates.common.barbican as barbican_common
22 22
 import octavia.certificates.common.cert as cert
23 23
 import octavia.certificates.manager.barbican_legacy as barbican_cert_mgr
24
+import octavia.tests.common.sample_certs as sample
24 25
 import octavia.tests.unit.base as base
25
-import octavia.tests.unit.common.sample_configs.sample_certs as sample
26 26
 
27 27
 
28 28
 PROJECT_ID = "12345"

+ 12
- 8
octavia/tests/unit/certificates/manager/test_local.py View File

@@ -23,15 +23,16 @@ from oslo_utils import uuidutils
23 23
 import octavia.certificates.common.cert as cert
24 24
 import octavia.certificates.manager.local as local_cert_mgr
25 25
 from octavia.common import exceptions
26
+from octavia.tests.common import sample_certs
26 27
 import octavia.tests.unit.base as base
27 28
 
28 29
 
29 30
 class TestLocalManager(base.TestCase):
30 31
 
31 32
     def setUp(self):
32
-        self.certificate = "My Certificate"
33
-        self.intermediates = "My Intermediates"
34
-        self.private_key = "My Private Key"
33
+        self.certificate = sample_certs.X509_CERT.decode('utf-8')
34
+        self.intermediates = sample_certs.X509_IMDS.decode('utf-8')
35
+        self.private_key = sample_certs.X509_CERT_KEY.decode('utf-8')
35 36
         self.private_key_passphrase = "My Private Key Passphrase"
36 37
 
37 38
         conf = oslo_fixture.Config(cfg.CONF)
@@ -82,6 +83,12 @@ class TestLocalManager(base.TestCase):
82 83
 
83 84
     def _get_cert(self, cert_id):
84 85
         fd_mock = mock.mock_open()
86
+        fd_mock.side_effect = [
87
+            mock.mock_open(read_data=self.certificate).return_value,
88
+            mock.mock_open(read_data=self.private_key).return_value,
89
+            mock.mock_open(read_data=self.intermediates).return_value,
90
+            mock.mock_open(read_data=self.private_key_passphrase).return_value
91
+        ]
85 92
         open_mock = mock.Mock()
86 93
         # Attempt to retrieve the cert
87 94
         with mock.patch('os.open', open_mock), mock.patch.object(
@@ -120,11 +127,8 @@ class TestLocalManager(base.TestCase):
120 127
         self._store_cert()
121 128
 
122 129
     def test_get_cert(self):
123
-        # Store a cert
124
-        cert_id = self._store_cert()
125
-
126 130
         # Get the cert
127
-        self._get_cert(cert_id)
131
+        self._get_cert("cert1")
128 132
 
129 133
     def test_delete_cert(self):
130 134
         # Store a cert
@@ -147,7 +151,7 @@ class TestLocalManager(base.TestCase):
147 151
 
148 152
         # Verify the correct files were opened
149 153
         flags = os.O_RDONLY
150
-        open_mock.assert_called_once_with('/tmp/{0}.pem'.format(secret_id),
154
+        open_mock.assert_called_once_with('/tmp/{0}.crt'.format(secret_id),
151 155
                                           flags)
152 156
 
153 157
         # Test failure path

+ 10
- 2
octavia/tests/unit/cmd/test_driver_agent.py View File

@@ -45,15 +45,19 @@ class TestDriverAgentCMD(base.TestCase):
45 45
         mock_multiprocessing.Event.return_value = mock_exit_event
46 46
         mock_status_listener_proc = mock.MagicMock()
47 47
         mock_stats_listener_proc = mock.MagicMock()
48
+        mock_get_listener_proc = mock.MagicMock()
48 49
         mock_multiprocessing.Process.side_effect = [mock_status_listener_proc,
49 50
                                                     mock_stats_listener_proc,
51
+                                                    mock_get_listener_proc,
50 52
                                                     mock_status_listener_proc,
51
-                                                    mock_stats_listener_proc]
53
+                                                    mock_stats_listener_proc,
54
+                                                    mock_get_listener_proc]
52 55
         driver_agent.main()
53 56
         mock_prep_srvc.assert_called_once()
54 57
         mock_gmr.assert_called_once()
55 58
         mock_status_listener_proc.start.assert_called_once()
56 59
         mock_stats_listener_proc.start.assert_called_once()
60
+        mock_get_listener_proc.start.assert_called_once()
57 61
         process_calls = [mock.call(
58 62
             args=mock_exit_event, name='status_listener',
59 63
             target=(octavia.api.drivers.driver_agent.driver_listener.
@@ -61,7 +65,11 @@ class TestDriverAgentCMD(base.TestCase):
61 65
             mock.call(
62 66
                 args=mock_exit_event, name='stats_listener',
63 67
                 target=(octavia.api.drivers.driver_agent.driver_listener.
64
-                        stats_listener))]
68
+                        stats_listener)),
69
+            mock.call(
70
+                args=mock_exit_event, name='get_listener',
71
+                target=(octavia.api.drivers.driver_agent.driver_listener.
72
+                        get_listener))]
65 73
         mock_multiprocessing.Process.has_calls(process_calls, any_order=True)
66 74
 
67 75
         # Test keyboard interrupt path

+ 1
- 1
octavia/tests/unit/common/sample_configs/sample_configs_combined.py View File

@@ -18,7 +18,7 @@ import collections
18 18
 from oslo_config import cfg
19 19
 
20 20
 from octavia.common import constants
21
-from octavia.tests.unit.common.sample_configs import sample_certs
21
+from octavia.tests.common import sample_certs
22 22
 
23 23
 CONF = cfg.CONF
24 24
 

+ 1
- 1
octavia/tests/unit/common/sample_configs/sample_configs_split.py View File

@@ -18,7 +18,7 @@ import collections
18 18
 from oslo_config import cfg
19 19
 
20 20
 from octavia.common import constants
21
-from octavia.tests.unit.common.sample_configs import sample_certs
21
+from octavia.tests.common import sample_certs
22 22
 
23 23
 CONF = cfg.CONF
24 24
 

+ 5
- 8
octavia/tests/unit/common/tls_utils/test_cert_parser.py View File

@@ -20,8 +20,8 @@ import mock
20 20
 from octavia.common import data_models
21 21
 import octavia.common.exceptions as exceptions
22 22
 import octavia.common.tls_utils.cert_parser as cert_parser
23
+from octavia.tests.common import sample_certs
23 24
 from octavia.tests.unit import base
24
-from octavia.tests.unit.common.sample_configs import sample_certs
25 25
 from octavia.tests.unit.common.sample_configs import sample_configs_combined
26 26
 
27 27
 
@@ -110,14 +110,12 @@ class TestTLSParseUtils(base.TestCase):
110 110
     def test_get_intermediates_pem_chain(self):
111 111
         self.assertEqual(
112 112
             sample_certs.X509_IMDS_LIST,
113
-            [c for c in
114
-                cert_parser.get_intermediates_pems(sample_certs.X509_IMDS)])
113
+            list(cert_parser.get_intermediates_pems(sample_certs.X509_IMDS)))
115 114
 
116 115
     def test_get_intermediates_pkcs7_pem(self):
117 116
         self.assertEqual(
118 117
             sample_certs.X509_IMDS_LIST,
119
-            [c for c in
120
-                cert_parser.get_intermediates_pems(sample_certs.PKCS7_PEM)])
118
+            list(cert_parser.get_intermediates_pems(sample_certs.PKCS7_PEM)))
121 119
 
122 120
     def test_get_intermediates_pkcs7_pem_bad(self):
123 121
         self.assertRaises(
@@ -128,8 +126,7 @@ class TestTLSParseUtils(base.TestCase):
128 126
     def test_get_intermediates_pkcs7_der(self):
129 127
         self.assertEqual(
130 128
             sample_certs.X509_IMDS_LIST,
131
-            [c for c in
132
-                cert_parser.get_intermediates_pems(sample_certs.PKCS7_DER)])
129
+            list(cert_parser.get_intermediates_pems(sample_certs.PKCS7_DER)))
133 130
 
134 131
     def test_get_intermediates_pkcs7_der_bad(self):
135 132
         self.assertRaises(
@@ -217,7 +214,7 @@ class TestTLSParseUtils(base.TestCase):
217 214
         self.assertEqual(expected, cert_parser.build_pem(tls_tuple))
218 215
 
219 216
     def test_get_primary_cn(self):
220
-        cert = mock.MagicMock()
217
+        cert = sample_certs.X509_CERT
221 218
 
222 219
         with mock.patch.object(cert_parser, 'get_host_names') as cp:
223 220
             cp.return_value = {'cn': 'fakeCN'}

+ 3
- 1
octavia/tests/unit/network/drivers/neutron/test_utils.py View File

@@ -47,7 +47,8 @@ class TestNeutronUtils(base.TestCase):
47 47
             project_id=t_constants.MOCK_PROJECT_ID,
48 48
             gateway_ip=t_constants.MOCK_GATEWAY_IP,
49 49
             cidr=t_constants.MOCK_CIDR,
50
-            ip_version=t_constants.MOCK_IP_VERSION
50
+            ip_version=t_constants.MOCK_IP_VERSION,
51
+            host_routes=[],
51 52
         )
52 53
         self._compare_ignore_value_none(model_obj.to_dict(), assert_dict)
53 54
 
@@ -64,6 +65,7 @@ class TestNeutronUtils(base.TestCase):
64 65
             status=t_constants.MOCK_STATUS,
65 66
             project_id=t_constants.MOCK_PROJECT_ID,
66 67
             admin_state_up=t_constants.MOCK_ADMIN_STATE_UP,
68
+            fixed_ips=[],
67 69
         )
68 70
         self._compare_ignore_value_none(model_obj.to_dict(), assert_dict)
69 71
         fixed_ips = t_constants.MOCK_NEUTRON_PORT['port']['fixed_ips']

+ 4
- 0
releasenotes/notes/Add-driver-agent-get-methods-b624a1342c3e6d0f.yaml View File

@@ -0,0 +1,4 @@
1
+---
2
+features:
3
+  - |
4
+    Adds support for the driver agent to query for load balancer objects.

+ 6
- 0
tox.ini View File

@@ -48,6 +48,12 @@ commands =
48 48
     coverage report --fail-under=90 --skip-covered
49 49
 
50 50
 [testenv:functional]
51
+# This is set as py27 right now, though the name is ambiguous.
52
+basepython = python2.7
53
+setenv = OS_TEST_PATH={toxinidir}/octavia/tests/functional
54
+
55
+[testenv:functional-py27]
56
+basepython = python2.7
51 57
 setenv = OS_TEST_PATH={toxinidir}/octavia/tests/functional
52 58
 
53 59
 [testenv:functional-py36]

Loading…
Cancel
Save