def test_run_without_inspection_and_apiurl(self, mock_list_hardware,
                                               mock_wsgi, mock_dispatch,
                                               mock_inspector, mock_wait,
                                               mock_mdns):
        mock_mdns.side_effect = lib_exc.ServiceLookupFailure()
        # If both api_url and inspection_callback_url are not configured when
        # the agent starts, ensure that the inspection will be skipped and wsgi
        # server will work as usual. Also, make sure api_client and heartbeater
        # will not be initialized in this case.
        CONF.set_override('inspection_callback_url', None)

        self.agent = agent.IronicPythonAgent(None,
                                             agent.Host('203.0.113.1', 9990),
                                             agent.Host('192.0.2.1', 9999), 3,
                                             10, 'eth0', 300, 1, False)
        self.assertFalse(hasattr(self.agent, 'api_client'))
        self.assertFalse(hasattr(self.agent, 'heartbeater'))

        def set_serve_api():
            self.agent.serve_api = False

        wsgi_server = mock_wsgi.return_value
        wsgi_server.handle_request.side_effect = set_serve_api

        self.agent.run()

        listen_addr = agent.Host('192.0.2.1', 9999)
        mock_wsgi.assert_called_once_with(
            (listen_addr.hostname, listen_addr.port),
            simple_server.WSGIRequestHandler)
        self.assertTrue(wsgi_server.handle_request.called)

        self.assertFalse(mock_inspector.called)
        self.assertFalse(mock_wait.called)
        self.assertFalse(mock_dispatch.called)
Ejemplo n.º 2
0
    def test_run_with_inspection_without_apiurl(self,
                                                mock_list_hardware,
                                                mock_wsgi,
                                                mock_dispatch,
                                                mock_inspector,
                                                mock_wait,
                                                mock_mdns):
        mock_mdns.side_effect = lib_exc.ServiceLookupFailure()
        # If inspection_callback_url is configured and api_url is not when the
        # agent starts, ensure that the inspection will be called and wsgi
        # server will work as usual. Also, make sure api_client and heartbeater
        # will not be initialized in this case.
        CONF.set_override('inspection_callback_url', 'http://foo/bar')

        self.agent = agent.IronicPythonAgent(None,
                                             agent.Host('203.0.113.1', 9990),
                                             agent.Host('192.0.2.1', 9999),
                                             3,
                                             10,
                                             'eth0',
                                             300,
                                             1,
                                             False,
                                             None)
        self.assertFalse(hasattr(self.agent, 'api_client'))
        self.assertFalse(hasattr(self.agent, 'heartbeater'))

        def set_serve_api():
            self.agent.serve_api = False

        wsgi_server = mock_wsgi.return_value
        wsgi_server.start.side_effect = set_serve_api

        self.agent.run()

        mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
                                          app=self.agent.api,
                                          host=mock.ANY, port=9999,
                                          use_ssl=False)
        wsgi_server.start.assert_called_once_with()

        mock_inspector.assert_called_once_with()

        self.assertTrue(mock_wait.called)
        self.assertFalse(mock_dispatch.called)
Ejemplo n.º 3
0
    def get_endpoint(self, service_type):
        """Get an endpoint and its properties from mDNS.

        If the requested endpoint is already in the built-in server cache, and
        its TTL is not exceeded, the cached value is returned.

        :param service_type: OpenStack service type.
        :returns: tuple (endpoint URL, properties as a dict).
        :raises: :exc:`.ServiceLookupFailure` if the service cannot be found.
        """
        delay = 0.1
        for attempt in range(CONF.mdns.lookup_attempts):
            name = '%s.%s' % (service_type, _MDNS_DOMAIN)
            info = self._zc.get_service_info(name, name)
            if info is not None:
                break
            elif attempt == CONF.mdns.lookup_attempts - 1:
                raise exception.ServiceLookupFailure(service=service_type)
            else:
                time.sleep(delay)
                delay *= 2

        # TODO(dtantsur): IPv6 support
        address = socket.inet_ntoa(info.address)
        properties = {}
        for key, value in info.properties.items():
            try:
                if isinstance(key, bytes):
                    key = key.decode('utf-8')
            except UnicodeError as exc:
                raise exception.ServiceLookupFailure(
                    _('Invalid properties for service %(svc)s. Cannot decode '
                      'key %(key)r: %(exc)r') % {
                          'svc': service_type,
                          'key': key,
                          'exc': exc
                      })

            try:
                if isinstance(value, bytes):
                    value = value.decode('utf-8')
            except UnicodeError as exc:
                LOG.debug(
                    'Cannot convert value %(value)r for key %(key)s '
                    'to string, assuming binary: %(exc)s', {
                        'key': key,
                        'value': value,
                        'exc': exc
                    })

            properties[key] = value

        path = properties.pop('path', '')
        protocol = properties.pop('protocol', None)
        if not protocol:
            if info.port == 80:
                protocol = 'http'
            else:
                protocol = 'https'

        if info.server.endswith('.local.'):
            # Local hostname means that the catalog lists an IP address,
            # so use it
            host = address
        else:
            # Otherwise use the provided hostname.
            host = info.server.rstrip('.')

        return ('{proto}://{host}:{port}{path}'.format(proto=protocol,
                                                       host=host,
                                                       port=info.port,
                                                       path=path), properties)