"Fossies" - the Fresh Open Source Software Archive

Member "masakari-9.0.0/masakari/tests/unit/api/openstack/ha/test_hosts.py" (13 May 2020, 22396 Bytes) of package /linux/misc/openstack/masakari-9.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_hosts.py": 8.0.0_vs_9.0.0.

    1 # Copyright (c) 2016 NTT DATA
    2 # All Rights Reserved.
    3 #
    4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    5 #    not use this file except in compliance with the License. You may obtain
    6 #    a copy of the License at
    7 #
    8 #         http://www.apache.org/licenses/LICENSE-2.0
    9 #
   10 #    Unless required by applicable law or agreed to in writing, software
   11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   13 #    License for the specific language governing permissions and limitations
   14 #    under the License.
   15 
   16 """Tests for the hosts api."""
   17 
   18 from unittest import mock
   19 
   20 import ddt
   21 from oslo_serialization import jsonutils
   22 from six.moves import http_client as http
   23 from webob import exc
   24 
   25 from masakari.api.openstack.ha import hosts
   26 from masakari import exception
   27 from masakari.ha import api as ha_api
   28 from masakari.objects import base as obj_base
   29 from masakari.objects import host as host_obj
   30 from masakari.objects import segment as segment_obj
   31 from masakari import test
   32 from masakari.tests.unit.api.openstack import fakes
   33 from masakari.tests import uuidsentinel
   34 
   35 
   36 def _make_host_obj(host_dict):
   37     return host_obj.Host(**host_dict)
   38 
   39 
   40 def _make_hosts_list(hosts_list):
   41     return host_obj.Host(objects=[
   42         _make_host_obj(a) for a in hosts_list])
   43 
   44 HOST_LIST = [
   45     {"name": "host_1", "id": "1", "reserved": False,
   46      "on_maintenance": False, "type": "fake",
   47      "control_attributes": "fake-control_attributes",
   48      "uuid": uuidsentinel.fake_host_1,
   49      "failover_segment_id": uuidsentinel.fake_segment1},
   50 
   51     {"name": "host_2", "id": "2", "reserved": False,
   52      "on_maintenance": False, "type": "fake",
   53      "control_attributes": "fake-control_attributes",
   54      "uuid": uuidsentinel.fake_host_2,
   55      "failover_segment_id": uuidsentinel.fake_segment1}
   56 ]
   57 
   58 HOST_LIST = _make_hosts_list(HOST_LIST)
   59 
   60 HOST = {
   61     "name": "host_1", "id": "1", "reserved": False,
   62     "on_maintenance": False, "type": "fake",
   63     "control_attributes": "fake-control_attributes",
   64     "uuid": uuidsentinel.fake_host_1,
   65     "failover_segment_id": uuidsentinel.fake_segment1
   66 }
   67 
   68 HOST = _make_host_obj(HOST)
   69 
   70 
   71 @ddt.ddt
   72 class HostTestCase(test.TestCase):
   73     """Test Case for host api."""
   74 
   75     bad_request = exception.ValidationError
   76 
   77     def _set_up(self):
   78         self.controller = hosts.HostsController()
   79         self.req = fakes.HTTPRequest.blank(
   80             '/v1/segments/%s/hosts' % uuidsentinel.fake_segment1,
   81             use_admin_context=True)
   82         self.context = self.req.environ['masakari.context']
   83 
   84     def setUp(self):
   85         super(HostTestCase, self).setUp()
   86         self._set_up()
   87 
   88     @property
   89     def app(self):
   90         return fakes.wsgi_app_v1(init_only='os-hosts')
   91 
   92     def _assert_host_data(self, expected, actual):
   93         self.assertTrue(obj_base.obj_equal_prims(expected, actual),
   94                         "The host objects were not equal")
   95 
   96     @mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
   97     @mock.patch.object(ha_api.HostAPI, 'get_all')
   98     def test_index(self, mock_get_all, mock_segment):
   99         mock_segment.return_value = mock.Mock()
  100         mock_get_all.return_value = HOST_LIST
  101 
  102         result = self.controller.index(self.req, uuidsentinel.fake_segment1)
  103         result = result['hosts']
  104         self._assert_host_data(HOST_LIST, _make_hosts_list(result))
  105 
  106     @mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
  107     @mock.patch.object(ha_api.HostAPI, 'get_all')
  108     def test_index_valid_on_maintenance(self, mock_get_all, mock_segment):
  109         host_list = [{"name": "host_1", "id": "1", "on_maintenance": True},
  110                      {"name": "host_2", "id": "2", "on_maintenance": True}]
  111         mock_get_all.return_value = host_list
  112         for parameter in ['1', 't', 'true', 'on', 'y', 'yes']:
  113             req = fakes.HTTPRequest.blank(
  114                 '/v1/segments/%s/hosts?on_maintenance=''%s' % (
  115                     uuidsentinel.fake_segment1, parameter),
  116                 use_admin_context=True)
  117             result = self.controller.index(req, uuidsentinel.fake_segment1)
  118             self.assertIn('hosts', result)
  119             self.assertEqual(len(host_list), len(result['hosts']))
  120             for host in result['hosts']:
  121                 self.assertTrue(host['on_maintenance'])
  122 
  123         host_list = [{"name": "host_1", "id": "1", "on_maintenance": False},
  124                      {"name": "host_2", "id": "2", "on_maintenance": False}]
  125         mock_get_all.return_value = host_list
  126         for parameter in ['0', 'f', 'false', 'off', 'n', 'no']:
  127             req = fakes.HTTPRequest.blank(
  128                 '/v1/segments/%s/hosts?on_maintenance=''%s' % (
  129                     uuidsentinel.fake_segment1, parameter),
  130                 use_admin_context=True)
  131             result = self.controller.index(req, uuidsentinel.fake_segment1)
  132             self.assertIn('hosts', result)
  133             self.assertEqual(len(host_list), len(result['hosts']))
  134             for host in result['hosts']:
  135                 self.assertFalse(host['on_maintenance'])
  136 
  137     @mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid',
  138                        return_value=mock.Mock())
  139     def test_index_invalid_on_maintenance(self, mock_segment):
  140 
  141         req = fakes.HTTPRequest.blank('/v1/segments/%s/hosts?on_maintenance='
  142                                       'abcd' % uuidsentinel.fake_segment1,
  143                                       use_admin_context=True)
  144         self.assertRaises(exc.HTTPBadRequest, self.controller.index, req,
  145                           uuidsentinel.fake_segment1)
  146 
  147     @mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
  148     @mock.patch.object(ha_api.HostAPI, 'get_all')
  149     def test_index_valid_reserved(self, mock_get_all, mock_segment):
  150         host_list = [{"name": "host_1", "id": "1", "reserved": True},
  151                      {"name": "host_2", "id": "2", "reserved": True}]
  152         mock_get_all.return_value = host_list
  153         for parameter in ['1', 't', 'true', 'on', 'y', 'yes']:
  154             req = fakes.HTTPRequest.blank(
  155                 '/v1/segments/%s/hosts?reserved=''%s' % (
  156                     uuidsentinel.fake_segment1, parameter
  157                 ), use_admin_context=True)
  158             result = self.controller.index(req, uuidsentinel.fake_segment1)
  159             self.assertIn('hosts', result)
  160             self.assertEqual(len(host_list), len(result['hosts']))
  161             for host in result['hosts']:
  162                 self.assertTrue(host['reserved'])
  163 
  164         host_list = [{"name": "host_1", "id": "1", "reserved": False},
  165                      {"name": "host_2", "id": "2", "reserved": False}]
  166         mock_get_all.return_value = host_list
  167         for parameter in ['0', 'f', 'false', 'off', 'n', 'no']:
  168             req = fakes.HTTPRequest.blank(
  169                 '/v1/segments/%s/hosts?reserved=''%s' % (
  170                     uuidsentinel.fake_segment1, parameter),
  171                 use_admin_context=True)
  172             result = self.controller.index(req, uuidsentinel.fake_segment1)
  173             self.assertIn('hosts', result)
  174             self.assertEqual(len(host_list), len(result['hosts']))
  175             for host in result['hosts']:
  176                 self.assertFalse(host['reserved'])
  177 
  178     @mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid',
  179                        return_value=mock.Mock())
  180     def test_index_invalid_reserved(self, mock_segment):
  181 
  182         req = fakes.HTTPRequest.blank('/v1/segments/%s/hosts?reserved='
  183                                       'abcd' % uuidsentinel.fake_segment1,
  184                                       use_admin_context=True)
  185         self.assertRaises(exc.HTTPBadRequest, self.controller.index, req,
  186                           uuidsentinel.fake_segment1)
  187 
  188     @mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
  189     @mock.patch.object(ha_api.HostAPI, 'get_all')
  190     def test_index_marker_not_found(self, mock_get_all, mock_segment):
  191         req = fakes.HTTPRequest.blank('/v1/segments/%s/hosts?marker=123456' % (
  192             uuidsentinel.fake_segment1), use_admin_context=True)
  193         mock_segment.return_value = mock.Mock()
  194         mock_get_all.side_effect = exception.MarkerNotFound(marker="123456")
  195         self.assertRaises(exc.HTTPBadRequest, self.controller.index,
  196                           req, uuidsentinel.fake_segment1)
  197 
  198     def test_get_all_marker_negative(self):
  199 
  200         req = fakes.HTTPRequest.blank('/v1/segments/%s/hosts?limit=-1' % (
  201             uuidsentinel.fake_segment1), use_admin_context=True)
  202         self.assertRaises(exc.HTTPBadRequest, self.controller.index,
  203                           req, uuidsentinel.fake_segment1)
  204 
  205     @ddt.data('sort_key', 'sort_dir')
  206     @mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid',
  207                        return_value=mock.Mock())
  208     def test_index_invalid(self, sort_by, mock_segment):
  209         req = fakes.HTTPRequest.blank('/v1/segments/%s/hosts?%s=abcd' % (
  210             uuidsentinel.fake_segment1, sort_by), use_admin_context=True)
  211         self.assertRaises(exc.HTTPBadRequest, self.controller.index, req,
  212                           uuidsentinel.fake_segment1)
  213 
  214     @ddt.data([exception.MarkerNotFound(marker="123456"),
  215                "/v1/segments/%s/hosts?marker=123456", exc.HTTPBadRequest],
  216               [exception.FailoverSegmentNotFound(
  217                   id=uuidsentinel.fake_segment1), "/v1/segments/%s/hosts",
  218                   exc.HTTPNotFound])
  219     @ddt.unpack
  220     @mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
  221     @mock.patch.object(ha_api.HostAPI, 'get_all')
  222     def test_index_not_found(self, masakari_exc, url, exc, mock_get_all,
  223                              mock_segment):
  224         mock_segment.return_value = mock.Mock()
  225         mock_get_all.side_effect = masakari_exc
  226 
  227         req = fakes.HTTPRequest.blank(url % uuidsentinel.fake_segment1,
  228                                       use_admin_context=True)
  229         self.assertRaises(exc, self.controller.index, req,
  230                           uuidsentinel.fake_segment1)
  231 
  232     @mock.patch.object(ha_api.HostAPI, 'create_host')
  233     def test_create(self, mock_create):
  234         mock_create.return_value = HOST
  235         body = {
  236             "host": {
  237                 "name": "host-1", "type": "fake",
  238                 "reserved": False,
  239                 "on_maintenance": False,
  240                 "control_attributes": "fake-control_attributes"
  241             }
  242         }
  243         result = self.controller.create(self.req,
  244                                         uuidsentinel.fake_segment1, body=body)
  245         result = result['host']
  246         self._assert_host_data(HOST, _make_host_obj(result))
  247 
  248     @mock.patch('masakari.rpc.get_client')
  249     @mock.patch.object(ha_api.HostAPI, 'create_host')
  250     def test_create_success_with_201_response_code(
  251         self, mock_client, mock_create):
  252         body = {
  253             "host": {
  254                 "name": "host-1", "type": "fake",
  255                 "reserved": False,
  256                 "on_maintenance": False,
  257                 "control_attributes": "fake-control_attributes"
  258             }
  259         }
  260         fake_req = self.req
  261         fake_req.headers['Content-Type'] = 'application/json'
  262         fake_req.method = 'POST'
  263         fake_req.body = jsonutils.dump_as_bytes(body)
  264         resp = fake_req.get_response(self.app)
  265         self.assertEqual(http.CREATED, resp.status_code)
  266 
  267     @mock.patch.object(ha_api.HostAPI, 'create_host')
  268     def test_create_with_duplicate_host_name(self, mock_create):
  269 
  270         mock_create.side_effect = (exception.
  271                                    HostExists(name='host-1'))
  272         body = {
  273             "host": {
  274                 "name": "host-1", "type": "fake",
  275                 "reserved": False,
  276                 "on_maintenance": False,
  277                 "control_attributes": "fake-control_attributes"
  278             }
  279         }
  280         self.assertRaises(exc.HTTPConflict, self.controller.create,
  281                           self.req, uuidsentinel.fake_segment1, body=body)
  282 
  283     @ddt.data(
  284         # no_host
  285         {"body": {
  286             "name": "host-1", "type": "fake",
  287             "reserved": False,
  288             "on_maintenance": False,
  289             "control_attributes": "fake-control_attributes"}},
  290 
  291         # no_name
  292         {"body": {
  293             "host": {
  294                 "type": "fake",
  295                 "reserved": False,
  296                 "on_maintenance": False,
  297                 "control_attributes": "fake-control_attributes"}}},
  298 
  299         # name_with_leading_trailing_spaces
  300         {"body": {
  301             "host": {
  302                 "name": " host-1 ", "type": "fake",
  303                 "reserved": False,
  304                 "on_maintenance": False,
  305                 "control_attributes": "fake-control_attributes"}}},
  306 
  307         # null_name
  308         {"body": {
  309             "host": {
  310                 "name": "", "type": "fake",
  311                 "reserved": False,
  312                 "on_maintenance": False,
  313                 "control_attributes": "fake-control_attributes"}}},
  314 
  315         # name_too_long
  316         {"body": {
  317             "host": {
  318                 "name": "host-1" * 255, "type": "fake",
  319                 "reserved": False,
  320                 "on_maintenance": False,
  321                 "control_attributes": "fake-control_attributes"}}},
  322 
  323         # extra_invalid_arg
  324         {"body": {
  325             "host": {
  326                 "name": "host-1", "type": "fake",
  327                 "reserved": False,
  328                 "on_maintenance": False,
  329                 "control_attributes": "fake-control_attributes",
  330                 "foo": "bar"}}},
  331 
  332         # type too long
  333         {"body": {
  334             "host": {
  335                 "name": "host-1", "type": "x" * 256,
  336                 "reserved": False,
  337                 "on_maintenance": False,
  338                 "control_attributes": "fake-control_attributes"}}},
  339 
  340         # type special characters
  341         {"body": {
  342             "host": {
  343                 "name": "host-1", "type": "x_y",
  344                 "reserved": False,
  345                 "on_maintenance": False,
  346                 "control_attributes": "fake-control_attributes"}}}
  347     )
  348     @ddt.unpack
  349     def test_create_failure(self, body):
  350         self.assertRaises(self.bad_request, self.controller.create,
  351                           self.req, uuidsentinel.fake_segment1, body=body)
  352 
  353     @mock.patch.object(ha_api.HostAPI, 'get_host')
  354     def test_show(self, mock_get_host):
  355 
  356         mock_get_host.return_value = HOST
  357 
  358         result = self.controller.show(self.req, uuidsentinel.fake_segment1,
  359                                       uuidsentinel.fake_host_1)
  360         result = result['host']
  361         self._assert_host_data(HOST, _make_host_obj(result))
  362 
  363     @mock.patch.object(ha_api.HostAPI, 'get_host')
  364     def test_show_with_non_existing_id(self, mock_get_host):
  365 
  366         mock_get_host.side_effect = exception.HostNotFound(id="2")
  367         self.assertRaises(exc.HTTPNotFound,
  368                           self.controller.show, self.req,
  369                           uuidsentinel.fake_segment1, "2")
  370 
  371     @ddt.data(
  372         {"body": {
  373             "host": {
  374                 "name": "host-1", "type": "fake", "reserved": False,
  375                 "on_maintenance": False,
  376                 "control_attributes": "fake-control_attributes"}}},
  377 
  378         # only name
  379         {"body": {"host": {"name": "host-1"}}}
  380     )
  381     @ddt.unpack
  382     @mock.patch.object(ha_api.HostAPI, 'update_host')
  383     def test_update(self, mock_update_host, body):
  384         mock_update_host.return_value = HOST
  385 
  386         result = self.controller.update(self.req, uuidsentinel.fake_segment1,
  387                                         uuidsentinel.fake_host_1,
  388                                         body=body)
  389 
  390         result = result['host']
  391         self._assert_host_data(HOST, _make_host_obj(result))
  392 
  393     @ddt.data(
  394         # no updates
  395         {"test_data": {"host": {}}},
  396 
  397         # no update key
  398         {"test_data": {"asdf": {}}},
  399 
  400         # wrong updates
  401         {"test_data": {"host": {"name": "disable", "foo": "bar"}}},
  402 
  403         # null name
  404         {"test_data": {"host": {"name": ""}}},
  405 
  406         # name too long
  407         {"test_data": {"host": {"name": "x" * 256}}},
  408 
  409         # type too long
  410         {"test_data": {"host": {"type": "x" * 256}}},
  411 
  412         # type with special characters
  413         {"test_data": {"host": {"type": "x_y"}}}
  414     )
  415     @ddt.unpack
  416     def test_update_failure(self, test_data):
  417         self.assertRaises(self.bad_request, self.controller.update,
  418                           self.req, uuidsentinel.fake_segment1,
  419                           uuidsentinel.fake_host_1, body=test_data)
  420 
  421     @mock.patch.object(ha_api.HostAPI, 'update_host')
  422     def test_update_with_non_exising_host(self, mock_update_host):
  423 
  424         test_data = {"host": {"name": "host11"}}
  425         mock_update_host.side_effect = exception.HostNotFound(id="2")
  426         self.assertRaises(exc.HTTPNotFound, self.controller.update,
  427                 self.req, uuidsentinel.fake_segment1, "2", body=test_data)
  428 
  429     @mock.patch.object(ha_api.HostAPI, 'update_host')
  430     def test_update_with_duplicated_name(self, mock_update_host):
  431         test_data = {"host": {"name": "host-1"}}
  432         mock_update_host.side_effect = exception.HostExists(name="host-1")
  433         self.assertRaises(exc.HTTPConflict, self.controller.update,
  434                 self.req, uuidsentinel.fake_segment1,
  435                           uuidsentinel.fake_host_1, body=test_data)
  436 
  437     @mock.patch.object(ha_api.HostAPI, 'delete_host')
  438     def test_delete_host(self, mock_delete):
  439 
  440         self.controller.delete(self.req, uuidsentinel.fake_segment1,
  441                                uuidsentinel.fake_host_1)
  442         self.assertTrue(mock_delete.called)
  443 
  444     @mock.patch('masakari.rpc.get_client')
  445     @mock.patch.object(ha_api.HostAPI, 'delete_host')
  446     def test_delete_host_with_204_status(self, mock_client, mock_delete):
  447         url = '/v1/segments/%(segment)s/hosts/%(host)s' % {
  448             'segment': uuidsentinel.fake_segment1,
  449             'host': uuidsentinel.fake_host_1
  450         }
  451         fake_req = fakes.HTTPRequest.blank(url, use_admin_context=True)
  452         fake_req.headers['Content-Type'] = 'application/json'
  453         fake_req.method = 'DELETE'
  454         resp = fake_req.get_response(self.app)
  455         self.assertEqual(http.NO_CONTENT, resp.status_code)
  456 
  457     @mock.patch.object(ha_api.HostAPI, 'delete_host')
  458     def test_delete_host_not_found(self, mock_delete):
  459 
  460         mock_delete.side_effect = exception.HostNotFound(id="2")
  461         self.assertRaises(exc.HTTPNotFound, self.controller.delete,
  462                 self.req, uuidsentinel.fake_segment1,
  463                           uuidsentinel.fake_host_3)
  464 
  465     @mock.patch.object(ha_api.HostAPI, 'delete_host')
  466     def test_delete_host_not_found_for_failover_segment(self, mock_delete):
  467 
  468         mock_delete.side_effect = exception.HostNotFoundUnderFailoverSegment(
  469             host_uuid=uuidsentinel.fake_host_3,
  470             segment_uuid=uuidsentinel.fake_segment1)
  471         self.assertRaises(exc.HTTPNotFound, self.controller.delete,
  472                 self.req, uuidsentinel.fake_segment1,
  473                           uuidsentinel.fake_host_3)
  474 
  475 
  476 class HostTestCasePolicyNotAuthorized(test.NoDBTestCase):
  477     """Test Case for host non admin."""
  478 
  479     def _set_up(self):
  480         self.controller = hosts.HostsController()
  481         self.req = fakes.HTTPRequest.blank(
  482             '/v1/segments/%s/hosts' % uuidsentinel.fake_segment1)
  483         self.context = self.req.environ['masakari.context']
  484 
  485     def setUp(self):
  486         super(HostTestCasePolicyNotAuthorized, self).setUp()
  487         self._set_up()
  488 
  489     def _check_rule(self, exc, rule_name):
  490         self.assertEqual(
  491             "Policy doesn't allow %s to be performed." % rule_name,
  492             exc.format_message())
  493 
  494     def test_index_no_admin(self):
  495         rule_name = "os_masakari_api:os-hosts:index"
  496         self.policy.set_rules({rule_name: "project:non_fake"})
  497         exc = self.assertRaises(exception.PolicyNotAuthorized,
  498                                 self.controller.index,
  499                                 self.req, uuidsentinel.fake_segment1)
  500         self._check_rule(exc, rule_name)
  501 
  502     def test_create_no_admin(self):
  503         rule_name = "os_masakari_api:os-hosts:create"
  504         self.policy.set_rules({rule_name: "project:non_fake"})
  505         body = {
  506             "host": {
  507                 "name": "host-1", "type": "fake", "reserved": False,
  508                 "on_maintenance": False,
  509                 "control_attributes": "fake-control_attributes"
  510             }
  511         }
  512         exc = self.assertRaises(exception.PolicyNotAuthorized,
  513                                 self.controller.create,
  514                                 self.req, uuidsentinel.fake_segment1,
  515                                 body=body)
  516         self._check_rule(exc, rule_name)
  517 
  518     def test_show_no_admin(self):
  519         rule_name = "os_masakari_api:os-hosts:detail"
  520         self.policy.set_rules({rule_name: "project:non_fake"})
  521         exc = self.assertRaises(exception.PolicyNotAuthorized,
  522                                 self.controller.show,
  523                                 self.req, uuidsentinel.fake_segment1,
  524                                 uuidsentinel.fake_host_1)
  525         self._check_rule(exc, rule_name)
  526 
  527     def test_update_no_admin(self):
  528         rule_name = "os_masakari_api:os-hosts:update"
  529         self.policy.set_rules({rule_name: "project:non_fake"})
  530         body = {
  531             "host": {
  532                 "name": "host-1", "type": "fake", "reserved": False,
  533                 "on_maintenance": False,
  534                 "control_attributes": "fake-control_attributes",
  535             }
  536         }
  537         exc = self.assertRaises(exception.PolicyNotAuthorized,
  538                                 self.controller.update,
  539                                 self.req, uuidsentinel.fake_segment1,
  540                                 uuidsentinel.fake_host_1, body=body)
  541         self._check_rule(exc, rule_name)
  542 
  543     def test_delete_no_admin(self):
  544         rule_name = "os_masakari_api:os-hosts:delete"
  545         self.policy.set_rules({rule_name: "project:non_fake"})
  546         exc = self.assertRaises(exception.PolicyNotAuthorized,
  547                                 self.controller.delete,
  548                                 self.req, uuidsentinel.fake_segment1,
  549                                 uuidsentinel.fake_host_1)
  550         self._check_rule(exc, rule_name)