Merge "Remove deprecated TrustedFilter"

This commit is contained in:
Zuul 2017-11-29 20:05:25 +00:00 committed by Gerrit Code Review
commit 690f8e4e24
12 changed files with 14 additions and 904 deletions

View File

@ -753,12 +753,6 @@ With the API, use the ``os:scheduler_hints`` key:
}
}
TrustedFilter
-------------
Filters hosts based on their trust. Only passes hosts that meet the trust
requirements specified in the instance properties.
.. _TypeAffinityFilter:
TypeAffinityFilter

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@ -6,137 +6,6 @@ OpenStack Compute can be integrated with various third-party technologies to
increase security. For more information, see the `OpenStack Security Guide
<https://docs.openstack.org/security-guide/>`_.
Trusted compute pools
~~~~~~~~~~~~~~~~~~~~~
.. note:: The ``TrustedFilter`` was deprecated in the 16.0.0 Pike release and
will be removed in the 17.0.0 Queens release.
Administrators can designate a group of compute hosts as trusted using trusted
compute pools. The trusted hosts use hardware-based security features, such as
the Intel Trusted Execution Technology (TXT), to provide an additional level of
security. Combined with an external stand-alone, web-based remote attestation
server, cloud providers can ensure that the compute node runs only software
with verified measurements and can ensure a secure cloud stack.
Trusted compute pools provide the ability for cloud subscribers to request
services run only on verified compute nodes.
The remote attestation server performs node verification like this:
1. Compute nodes boot with Intel TXT technology enabled.
2. The compute node BIOS, hypervisor, and operating system are measured.
3. When the attestation server challenges the compute node, the measured data
is sent to the attestation server.
4. The attestation server verifies the measurements against a known good
database to determine node trustworthiness.
A description of how to set up an attestation service is beyond the scope of
this document. For an open source project that you can use to implement an
attestation service, see the `Open Attestation
<https://github.com/OpenAttestation/OpenAttestation>`__ project.
.. figure:: figures/OpenStackTrustedComputePool1.png
**Configuring Compute to use trusted compute pools**
#. Enable scheduling support for trusted compute pools by adding these lines to
the ``DEFAULT`` section of the ``/etc/nova/nova.conf`` file:
.. code-block:: ini
[DEFAULT]
compute_scheduler_driver=nova.scheduler.filter_scheduler.FilterScheduler
scheduler_available_filters=nova.scheduler.filters.all_filters
scheduler_default_filters=AvailabilityZoneFilter,RamFilter,ComputeFilter,TrustedFilter
#. Specify the connection information for your attestation service by adding
these lines to the ``trusted_computing`` section of the
``/etc/nova/nova.conf`` file:
.. code-block:: ini
[trusted_computing]
attestation_server = 10.1.71.206
attestation_port = 8443
# If using OAT v2.0 after, use this port:
# attestation_port = 8181
attestation_server_ca_file = /etc/nova/ssl.10.1.71.206.crt
# If using OAT v1.5, use this api_url:
attestation_api_url = /AttestationService/resources
# If using OAT pre-v1.5, use this api_url:
# attestation_api_url = /OpenAttestationWebServices/V1.0
attestation_auth_blob = i-am-openstack
In this example:
``server``
Host name or IP address of the host that runs the attestation service
``port``
HTTPS port for the attestation service
``server_ca_file``
Certificate file used to verify the attestation server's identity
``api_url``
The attestation service's URL path
``auth_blob``
An authentication blob, required by the attestation service.
#. Save the file, and restart the ``nova-compute`` and ``nova-scheduler``
service to pick up the changes.
To customize the trusted compute pools, use these configuration option
settings:
.. list-table:: **Description of trusted computing configuration options**
:header-rows: 2
* - Configuration option = Default value
- Description
* - [trusted_computing]
-
* - attestation_api_url = /OpenAttestationWebServices/V1.0
- (StrOpt) Attestation web API URL
* - attestation_auth_blob = None
- (StrOpt) Attestation authorization blob - must change
* - attestation_auth_timeout = 60
- (IntOpt) Attestation status cache valid period length
* - attestation_insecure_ssl = False
- (BoolOpt) Disable SSL cert verification for Attestation service
* - attestation_port = 8443
- (StrOpt) Attestation server port
* - attestation_server = None
- (StrOpt) Attestation server HTTP
* - attestation_server_ca_file = None
- (StrOpt) Attestation server Cert file for Identity verification
**Specifying trusted flavors**
#. Flavors can be designated as trusted using the :command:`openstack flavor
set` command. In this example, the ``m1.tiny`` flavor is being set as
trusted:
.. code-block:: console
$ openstack flavor set --property trusted_host=trusted m1.tiny
#. You can request that your instance is run on a trusted host by specifying a
trusted flavor when booting the instance:
.. code-block:: console
$ openstack server create --flavor m1.tiny \
--key-name myKeypairName --image myImageID newInstanceName
.. figure:: figures/OpenStackTrustedComputePool2.png
Encrypt Compute metadata traffic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -19,7 +19,6 @@ System administration
live-migration-usage.rst
remote-console-access.rst
service-groups.rst
security.rst
node-down.rst
adv-config.rst

View File

@ -154,14 +154,6 @@ There are many standard filter classes which may be used
a set of instances.
* |RetryFilter| - filters hosts that have been attempted for scheduling.
Only passes hosts that have not been previously attempted.
* |TrustedFilter| (EXPERIMENTAL) - filters hosts based on their trust. Only
passes hosts that meet the trust requirements specified in the instance
properties.
.. warning:: TrustedFilter is deprecated for removal in the 17.0.0 Queens
release. There is no replacement planned for this filter. It has been
marked experimental since its inception. It is incomplete and not tested.
* |TypeAffinityFilter| - Only passes hosts that are not already running an
instance of the requested type.
@ -304,14 +296,6 @@ exception even if the problem is related to 1:N compute nodes. If you see that
case in the scheduler logs, then your problem is most likely related to a
compute problem and you should check the compute logs.
The |TrustedFilter| filters hosts based on their trust. Only passes hosts
that match the trust requested in the ``extra_specs`` for the flavor. The key
for this filter must be scope format as ``trust:trusted_host``, where ``trust``
is the scope of the key and ``trusted_host`` is the actual key value.
The value of this pair (``trusted``/``untrusted``) must match the
integrity of a host (obtained from the Attestation service) before it is
passed by the |TrustedFilter|.
The |NUMATopologyFilter| considers the NUMA topology that was specified for the instance
through the use of flavor extra_specs in combination with the image properties, as
described in detail in the related nova-spec document:
@ -501,7 +485,6 @@ in :mod:`nova.tests.scheduler`.
.. |DifferentHostFilter| replace:: :class:`DifferentHostFilter <nova.scheduler.filters.affinity_filter.DifferentHostFilter>`
.. |SameHostFilter| replace:: :class:`SameHostFilter <nova.scheduler.filters.affinity_filter.SameHostFilter>`
.. |RetryFilter| replace:: :class:`RetryFilter <nova.scheduler.filters.retry_filter.RetryFilter>`
.. |TrustedFilter| replace:: :class:`TrustedFilter <nova.scheduler.filters.trusted_filter.TrustedFilter>`
.. |TypeAffinityFilter| replace:: :class:`TypeAffinityFilter <nova.scheduler.filters.type_filter.TypeAffinityFilter>`
.. |AggregateTypeAffinityFilter| replace:: :class:`AggregateTypeAffinityFilter <nova.scheduler.filters.type_filter.AggregateTypeAffinityFilter>`
.. |ServerGroupAntiAffinityFilter| replace:: :class:`ServerGroupAntiAffinityFilter <nova.scheduler.filters.affinity_filter.ServerGroupAntiAffinityFilter>`

View File

@ -610,207 +610,6 @@ Related options:
* aggregate_image_properties_isolation_namespace
""")]
trust_group = cfg.OptGroup(name="trusted_computing",
title="Trust parameters",
help="""
Configuration options for enabling Trusted Platform Module.
""")
trusted_opts = [
cfg.HostAddressOpt("attestation_server",
deprecated_for_removal=True,
deprecated_reason="Incomplete filter",
deprecated_since="Pike",
help="""
The host to use as the attestation server.
Cloud computing pools can involve thousands of compute nodes located at
different geographical locations, making it difficult for cloud providers to
identify a node's trustworthiness. When using the Trusted filter, users can
request that their VMs only be placed on nodes that have been verified by the
attestation server specified in this option.
This option is only used by the FilterScheduler and its subclasses; if you use
a different scheduler, this option has no effect. Also note that this setting
only affects scheduling if the 'TrustedFilter' filter is enabled.
Possible values:
* A string representing the host name or IP address of the attestation server,
or an empty string.
Related options:
* attestation_server_ca_file
* attestation_port
* attestation_api_url
* attestation_auth_blob
* attestation_auth_timeout
* attestation_insecure_ssl
"""),
cfg.StrOpt("attestation_server_ca_file",
deprecated_for_removal=True,
deprecated_reason="Incomplete filter",
deprecated_since="Pike",
help="""
The absolute path to the certificate to use for authentication when connecting
to the attestation server. See the `attestation_server` help text for more
information about host verification.
This option is only used by the FilterScheduler and its subclasses; if you use
a different scheduler, this option has no effect. Also note that this setting
only affects scheduling if the 'TrustedFilter' filter is enabled.
Possible values:
* A string representing the path to the authentication certificate for the
attestation server, or an empty string.
Related options:
* attestation_server
* attestation_port
* attestation_api_url
* attestation_auth_blob
* attestation_auth_timeout
* attestation_insecure_ssl
"""),
cfg.PortOpt("attestation_port",
default=8443,
deprecated_for_removal=True,
deprecated_reason="Incomplete filter",
deprecated_since="Pike",
help="""
The port to use when connecting to the attestation server. See the
`attestation_server` help text for more information about host verification.
This option is only used by the FilterScheduler and its subclasses; if you use
a different scheduler, this option has no effect. Also note that this setting
only affects scheduling if the 'TrustedFilter' filter is enabled.
Related options:
* attestation_server
* attestation_server_ca_file
* attestation_api_url
* attestation_auth_blob
* attestation_auth_timeout
* attestation_insecure_ssl
"""),
cfg.StrOpt("attestation_api_url",
default="/OpenAttestationWebServices/V1.0",
deprecated_for_removal=True,
deprecated_reason="Incomplete filter",
deprecated_since="Pike",
help="""
The URL on the attestation server to use. See the `attestation_server` help
text for more information about host verification.
This value must be just that path portion of the full URL, as it will be joined
to the host specified in the attestation_server option.
This option is only used by the FilterScheduler and its subclasses; if you use
a different scheduler, this option has no effect. Also note that this setting
only affects scheduling if the 'TrustedFilter' filter is enabled.
Possible values:
* A valid URL string of the attestation server, or an empty string.
Related options:
* attestation_server
* attestation_server_ca_file
* attestation_port
* attestation_auth_blob
* attestation_auth_timeout
* attestation_insecure_ssl
"""),
cfg.StrOpt("attestation_auth_blob",
secret=True,
deprecated_for_removal=True,
deprecated_reason="Incomplete filter",
deprecated_since="Pike",
help="""
Attestation servers require a specific blob that is used to authenticate. The
content and format of the blob are determined by the particular attestation
server being used. There is no default value; you must supply the value as
specified by your attestation service. See the `attestation_server` help text
for more information about host verification.
This option is only used by the FilterScheduler and its subclasses; if you use
a different scheduler, this option has no effect. Also note that this setting
only affects scheduling if the 'TrustedFilter' filter is enabled.
Possible values:
* A string containing the specific blob required by the attestation server, or
an empty string.
Related options:
* attestation_server
* attestation_server_ca_file
* attestation_port
* attestation_api_url
* attestation_auth_timeout
* attestation_insecure_ssl
"""),
cfg.IntOpt("attestation_auth_timeout",
default=60,
deprecated_for_removal=True,
deprecated_reason="Incomplete filter",
deprecated_since="Pike",
min=0,
help="""
This value controls how long a successful attestation is cached. Once this
period has elapsed, a new attestation request will be made. See the
`attestation_server` help text for more information about host verification.
This option is only used by the FilterScheduler and its subclasses; if you use
a different scheduler, this option has no effect. Also note that this setting
only affects scheduling if the 'TrustedFilter' filter is enabled.
Possible values:
* A integer value, corresponding to the timeout interval for attestations in
seconds. Any integer is valid, although setting this to zero or negative
values can greatly impact performance when using an attestation service.
Related options:
* attestation_server
* attestation_server_ca_file
* attestation_port
* attestation_api_url
* attestation_auth_blob
* attestation_insecure_ssl
"""),
cfg.BoolOpt("attestation_insecure_ssl",
default=False,
deprecated_for_removal=True,
deprecated_reason="Incomplete filter",
deprecated_since="Pike",
help="""
When set to True, the SSL certificate verification is skipped for the
attestation service. See the `attestation_server` help text for more
information about host verification.
This option is only used by the FilterScheduler and its subclasses; if you use
a different scheduler, this option has no effect. Also note that this setting
only affects scheduling if the 'TrustedFilter' filter is enabled.
Related options:
* attestation_server
* attestation_server_ca_file
* attestation_port
* attestation_api_url
* attestation_auth_blob
* attestation_auth_timeout
"""),
]
metrics_group = cfg.OptGroup(name="metrics",
title="Metrics parameters",
help="""
@ -938,9 +737,6 @@ def register_opts(conf):
conf.register_group(filter_scheduler_group)
conf.register_opts(filter_scheduler_opts, group=filter_scheduler_group)
conf.register_group(trust_group)
conf.register_opts(trusted_opts, group=trust_group)
conf.register_group(metrics_group)
conf.register_opts(metrics_weight_opts, group=metrics_group)
@ -948,5 +744,4 @@ def register_opts(conf):
def list_opts():
return {scheduler_group: scheduler_opts,
filter_scheduler_group: filter_scheduler_opts,
trust_group: trusted_opts,
metrics_group: metrics_weight_opts}

View File

@ -1,253 +0,0 @@
# Copyright (c) 2012 Intel, Inc.
# Copyright (c) 2011-2012 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Filter to add support for Trusted Computing Pools (EXPERIMENTAL).
Filter that only schedules tasks on a host if the integrity (trust)
of that host matches the trust requested in the ``extra_specs`` for the
flavor. The ``extra_specs`` will contain a key/value pair where the
key is ``trust``. The value of this pair (``trusted``/``untrusted``) must
match the integrity of that host (obtained from the Attestation
service) before the task can be scheduled on that host.
Note that the parameters to control access to the Attestation Service
are in the ``nova.conf`` file in a separate ``trust`` section. For example,
the config file will look something like:
[DEFAULT]
debug=True
...
[trust]
server=attester.mynetwork.com
Details on the specific parameters can be found in the file
``trust_attest.py``.
Details on setting up and using an Attestation Service can be found at
the Open Attestation project at:
https://github.com/OpenAttestation/OpenAttestation
"""
from oslo_log import log as logging
from oslo_log import versionutils
from oslo_serialization import jsonutils
from oslo_utils import timeutils
import requests
import nova.conf
from nova import context
from nova.i18n import _LW
from nova import objects
from nova.scheduler import filters
LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF
class AttestationService(object):
# Provide access wrapper to attestation server to get integrity report.
def __init__(self):
self.api_url = CONF.trusted_computing.attestation_api_url
self.host = CONF.trusted_computing.attestation_server
self.port = CONF.trusted_computing.attestation_port
self.auth_blob = CONF.trusted_computing.attestation_auth_blob
self.key_file = None
self.cert_file = None
self.ca_file = CONF.trusted_computing.attestation_server_ca_file
self.request_count = 100
# If the CA file is not provided, let's check the cert if verification
# asked
self.verify = (not CONF.trusted_computing.attestation_insecure_ssl
and self.ca_file or True)
self.cert = (self.cert_file, self.key_file)
def _do_request(self, method, action_url, body, headers):
# Connects to the server and issues a request.
# :returns: result data
# :raises: IOError if the request fails
action_url = "https://%s:%d%s/%s" % (self.host, self.port,
self.api_url, action_url)
try:
res = requests.request(method, action_url, data=body,
headers=headers, cert=self.cert,
verify=self.verify)
status_code = res.status_code
if status_code in (requests.codes.OK,
requests.codes.CREATED,
requests.codes.ACCEPTED,
requests.codes.NO_CONTENT):
try:
return requests.codes.OK, jsonutils.loads(res.text)
except (TypeError, ValueError):
return requests.codes.OK, res.text
return status_code, None
except requests.exceptions.RequestException:
return IOError, None
def _request(self, cmd, subcmd, hosts):
body = {}
body['count'] = len(hosts)
body['hosts'] = hosts
cooked = jsonutils.dumps(body)
headers = {}
headers['content-type'] = 'application/json'
headers['Accept'] = 'application/json'
if self.auth_blob:
headers['x-auth-blob'] = self.auth_blob
status, res = self._do_request(cmd, subcmd, cooked, headers)
return status, res
def do_attestation(self, hosts):
"""Attests compute nodes through OAT service.
:param hosts: hosts list to be attested
:returns: dictionary for trust level and validate time
"""
result = None
status, data = self._request("POST", "PollHosts", hosts)
if data is not None:
result = data.get('hosts')
return result
class ComputeAttestationCache(object):
"""Cache for compute node attestation
Cache compute node's trust level for sometime,
if the cache is out of date, poll OAT service to flush the
cache.
OAT service may have cache also. OAT service's cache valid time
should be set shorter than trusted filter's cache valid time.
"""
def __init__(self):
self.attestservice = AttestationService()
self.compute_nodes = {}
admin = context.get_admin_context()
# Fetch compute node list to initialize the compute_nodes,
# so that we don't need poll OAT service one by one for each
# host in the first round that scheduler invokes us.
computes = objects.ComputeNodeList.get_all(admin)
for compute in computes:
host = compute.hypervisor_hostname
self._init_cache_entry(host)
def _cache_valid(self, host):
cachevalid = False
if host in self.compute_nodes:
node_stats = self.compute_nodes.get(host)
if not timeutils.is_older_than(
node_stats['vtime'],
CONF.trusted_computing.attestation_auth_timeout):
cachevalid = True
return cachevalid
def _init_cache_entry(self, host):
self.compute_nodes[host] = {
'trust_lvl': 'unknown',
'vtime': timeutils.normalize_time(
timeutils.parse_isotime("1970-01-01T00:00:00Z"))}
def _invalidate_caches(self):
for host in self.compute_nodes:
self._init_cache_entry(host)
def _update_cache_entry(self, state):
entry = {}
host = state['host_name']
entry['trust_lvl'] = state['trust_lvl']
try:
# Normalize as naive object to interoperate with utcnow().
entry['vtime'] = timeutils.normalize_time(
timeutils.parse_isotime(state['vtime']))
except ValueError:
try:
# Mt. Wilson does not necessarily return an ISO8601 formatted
# `vtime`, so we should try to parse it as a string formatted
# datetime.
vtime = timeutils.parse_strtime(state['vtime'], fmt="%c")
entry['vtime'] = timeutils.normalize_time(vtime)
except ValueError:
# Mark the system as un-trusted if get invalid vtime.
entry['trust_lvl'] = 'unknown'
entry['vtime'] = timeutils.utcnow()
self.compute_nodes[host] = entry
def _update_cache(self):
self._invalidate_caches()
states = self.attestservice.do_attestation(
list(self.compute_nodes.keys()))
if states is None:
return
for state in states:
self._update_cache_entry(state)
def get_host_attestation(self, host):
"""Check host's trust level."""
if host not in self.compute_nodes:
self._init_cache_entry(host)
if not self._cache_valid(host):
self._update_cache()
level = self.compute_nodes.get(host).get('trust_lvl')
return level
class ComputeAttestation(object):
def __init__(self):
self.caches = ComputeAttestationCache()
def is_trusted(self, host, trust):
level = self.caches.get_host_attestation(host)
return trust == level
class TrustedFilter(filters.BaseHostFilter):
"""Trusted filter to support Trusted Compute Pools."""
RUN_ON_REBUILD = False
def __init__(self):
self.compute_attestation = ComputeAttestation()
msg = _LW('The TrustedFilter is deprecated as it has been marked '
'experimental for some time with no tests. It will be '
'removed in the 17.0.0 Queens release.')
versionutils.report_deprecated_feature(LOG, msg)
# The hosts the instances are running on doesn't change within a request
run_filter_once_per_request = True
def host_passes(self, host_state, spec_obj):
instance_type = spec_obj.flavor
extra = (instance_type.extra_specs
if 'extra_specs' in instance_type else {})
trust = extra.get('trust:trusted_host')
host = host_state.nodename
if trust:
return self.compute_attestation.is_trusted(host, trust)
return True

View File

@ -61,8 +61,6 @@ class TestAggregateInstanceExtraSpecsFilter(test.NoDBTestCase):
'opt1': '1',
# Scoped extra spec that applies to this filter
'aggregate_instance_extra_specs:opt2': '2',
# Scoped extra spec that does not apply to this filter
'trust:trusted_host': 'true',
}
self._do_test_aggregate_filter_extra_specs(especs, passes=True)
@ -73,8 +71,6 @@ class TestAggregateInstanceExtraSpecsFilter(test.NoDBTestCase):
'opt1': '1',
# Scoped extra spec that applies to this filter
'aggregate_instance_extra_specs:opt1': '3',
# Scoped extra spec that does not apply to this filter
'trust:trusted_host': 'true',
}
self._do_test_aggregate_filter_extra_specs(especs, passes=True)
@ -91,7 +87,6 @@ class TestAggregateInstanceExtraSpecsFilter(test.NoDBTestCase):
agg_mock.return_value = {'opt1': set(['1']), 'opt2': set(['2'])}
especs = {
'opt1': '1',
'opt2': '222',
'trust:trusted_host': 'true'
'opt2': '222'
}
self._do_test_aggregate_filter_extra_specs(especs, passes=False)

View File

@ -107,20 +107,19 @@ class TestComputeCapabilitiesFilter(test.NoDBTestCase):
def test_compute_filter_passes_extra_specs_simple(self):
self._do_test_compute_filter_extra_specs(
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
especs={'opt1': '1', 'opt2': '2', 'trust:trusted_host': 'true'},
especs={'opt1': '1', 'opt2': '2'},
passes=True)
def test_compute_filter_fails_extra_specs_simple(self):
self._do_test_compute_filter_extra_specs(
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
especs={'opt1': '1', 'opt2': '222', 'trust:trusted_host': 'true'},
especs={'opt1': '1', 'opt2': '222'},
passes=False)
def test_compute_filter_pass_extra_specs_simple_with_scope(self):
self._do_test_compute_filter_extra_specs(
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
especs={'capabilities:opt1': '1',
'trust:trusted_host': 'true'},
especs={'capabilities:opt1': '1'},
passes=True)
def test_compute_filter_pass_extra_specs_same_as_scope(self):
@ -140,15 +139,13 @@ class TestComputeCapabilitiesFilter(test.NoDBTestCase):
def test_compute_filter_extra_specs_simple_with_wrong_scope(self):
self._do_test_compute_filter_extra_specs(
ecaps={'opt1': 1, 'opt2': 2},
especs={'wrong_scope:opt1': '1',
'trust:trusted_host': 'true'},
especs={'wrong_scope:opt1': '1'},
passes=True)
def test_compute_filter_extra_specs_pass_multi_level_with_scope(self):
self._do_test_compute_filter_extra_specs(
ecaps={'stats': {'opt1': {'a': 1, 'b': {'aa': 2}}, 'opt2': 2}},
especs={'opt1:a': '1', 'capabilities:opt1:b:aa': '2',
'trust:trusted_host': 'true'},
especs={'opt1:a': '1', 'capabilities:opt1:b:aa': '2'},
passes=True)
def test_compute_filter_pass_ram_with_backward_compatibility(self):

View File

@ -1,277 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
from oslo_utils import fixture as utils_fixture
from oslo_utils import timeutils
import requests
from nova import objects
from nova.scheduler.filters import trusted_filter
from nova import test
from nova.tests.unit.scheduler import fakes
from nova import utils
CONF = cfg.CONF
class AttestationServiceTestCase(test.NoDBTestCase):
def setUp(self):
super(AttestationServiceTestCase, self).setUp()
self.api_url = '/OpenAttestationWebServices/V1.0'
self.host = 'localhost'
self.port = '8443'
self.statuses = (requests.codes.OK, requests.codes.CREATED,
requests.codes.ACCEPTED, requests.codes.NO_CONTENT)
@mock.patch.object(requests, 'request')
def test_do_request_possible_statuses(self, request_mock):
"""This test case checks if '_do_request()' method returns
appropriate status_code (200) and result (text converted to json),
while status_code returned by request is in one of fourth eligible
statuses
"""
for status_code in self.statuses:
request_mock.return_value.status_code = status_code
request_mock.return_value.text = '{"test": "test"}'
attestation_service = trusted_filter.AttestationService()
status, result = attestation_service._do_request(
'POST', 'PollHosts', {}, {})
self.assertEqual(requests.codes.OK, status)
self.assertEqual(jsonutils.loads(request_mock.return_value.text),
result)
@mock.patch.object(requests, 'request')
def test_do_request_other_status(self, request_mock):
"""This test case checks if '_do_request()' method returns
appropriate status (this returned by request method) and result
(None), while status_code returned by request is not in one of fourth
eligible statuses
"""
request_mock.return_value.status_code = requests.codes.NOT_FOUND
request_mock.return_value.text = '{"test": "test"}'
attestation_service = trusted_filter.AttestationService()
status, result = attestation_service._do_request(
'POST', 'PollHosts', {}, {})
self.assertEqual(requests.codes.NOT_FOUND, status)
self.assertIsNone(result)
@mock.patch.object(requests, 'request')
def test_do_request_unconvertible_text(self, request_mock):
for status_code in self.statuses:
# this unconvertible_texts leads to TypeError and ValueError
# in jsonutils.loads(res.text) in _do_request() method
for unconvertible_text in ({"test": "test"}, '{}{}'):
request_mock.return_value.status_code = status_code
request_mock.return_value.text = unconvertible_text
attestation_service = trusted_filter.AttestationService()
status, result = attestation_service._do_request(
'POST', 'PollHosts', {}, {})
self.assertEqual(requests.codes.OK, status)
self.assertEqual(unconvertible_text, result)
@mock.patch.object(trusted_filter.AttestationService, '_request')
class TestTrustedFilter(test.NoDBTestCase):
def setUp(self):
super(TestTrustedFilter, self).setUp()
# TrustedFilter's constructor creates the attestation cache, which
# calls to get a list of all the compute nodes.
fake_compute_nodes = [
objects.ComputeNode(hypervisor_hostname='node1'),
]
with mock.patch('nova.objects.ComputeNodeList.get_all') as mocked:
mocked.return_value = fake_compute_nodes
self.filt_cls = trusted_filter.TrustedFilter()
def test_trusted_filter_default_passes(self, req_mock):
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024))
host = fakes.FakeHostState('host1', 'node1', {})
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
self.assertFalse(req_mock.called)
def test_trusted_filter_trusted_and_trusted_passes(self, req_mock):
oat_data = {"hosts": [{"host_name": "node1",
"trust_lvl": "trusted",
"vtime": utils.isotime()}]}
req_mock.return_value = requests.codes.OK, oat_data
extra_specs = {'trust:trusted_host': 'trusted'}
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024,
extra_specs=extra_specs))
host = fakes.FakeHostState('host1', 'node1', {})
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
req_mock.assert_called_once_with("POST", "PollHosts", ["node1"])
def test_trusted_filter_trusted_and_untrusted_fails(self, req_mock):
oat_data = {"hosts": [{"host_name": "node1",
"trust_lvl": "untrusted",
"vtime": utils.isotime()}]}
req_mock.return_value = requests.codes.OK, oat_data
extra_specs = {'trust:trusted_host': 'trusted'}
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024,
extra_specs=extra_specs))
host = fakes.FakeHostState('host1', 'node1', {})
self.assertFalse(self.filt_cls.host_passes(host, spec_obj))
def test_trusted_filter_untrusted_and_trusted_fails(self, req_mock):
oat_data = {"hosts": [{"host_name": "node",
"trust_lvl": "trusted",
"vtime": utils.isotime()}]}
req_mock.return_value = requests.codes.OK, oat_data
extra_specs = {'trust:trusted_host': 'untrusted'}
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024,
extra_specs=extra_specs))
host = fakes.FakeHostState('host1', 'node1', {})
self.assertFalse(self.filt_cls.host_passes(host, spec_obj))
def test_trusted_filter_untrusted_and_untrusted_passes(self, req_mock):
oat_data = {"hosts": [{"host_name": "node1",
"trust_lvl": "untrusted",
"vtime": utils.isotime()}]}
req_mock.return_value = requests.codes.OK, oat_data
extra_specs = {'trust:trusted_host': 'untrusted'}
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024,
extra_specs=extra_specs))
host = fakes.FakeHostState('host1', 'node1', {})
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
def test_trusted_filter_update_cache(self, req_mock):
oat_data = {"hosts": [{"host_name": "node1",
"trust_lvl": "untrusted",
"vtime": utils.isotime()}]}
req_mock.return_value = requests.codes.OK, oat_data
extra_specs = {'trust:trusted_host': 'untrusted'}
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024,
extra_specs=extra_specs))
host = fakes.FakeHostState('host1', 'node1', {})
self.filt_cls.host_passes(host, spec_obj) # Fill the caches
req_mock.reset_mock()
self.filt_cls.host_passes(host, spec_obj)
self.assertFalse(req_mock.called)
req_mock.reset_mock()
time_fixture = self.useFixture(utils_fixture.TimeFixture())
time_fixture.advance_time_seconds(
CONF.trusted_computing.attestation_auth_timeout + 80)
self.filt_cls.host_passes(host, spec_obj)
self.assertTrue(req_mock.called)
def test_trusted_filter_update_cache_timezone(self, req_mock):
oat_data = {"hosts": [{"host_name": "node1",
"trust_lvl": "untrusted",
"vtime": "2012-09-09T05:10:40-04:00"}]}
req_mock.return_value = requests.codes.OK, oat_data
extra_specs = {'trust:trusted_host': 'untrusted'}
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024,
extra_specs=extra_specs))
host = fakes.FakeHostState('host1', 'node1', {})
time_fixture = self.useFixture(utils_fixture.TimeFixture(
timeutils.normalize_time(
timeutils.parse_isotime("2012-09-09T09:10:40Z"))))
self.filt_cls.host_passes(host, spec_obj) # Fill the caches
req_mock.reset_mock()
self.filt_cls.host_passes(host, spec_obj)
self.assertFalse(req_mock.called)
req_mock.reset_mock()
time_fixture.advance_time_seconds(
CONF.trusted_computing.attestation_auth_timeout - 10)
self.filt_cls.host_passes(host, spec_obj)
self.assertFalse(req_mock.called)
def test_trusted_filter_combine_hosts(self, req_mock):
fake_compute_nodes = [
objects.ComputeNode(hypervisor_hostname='node1'),
objects.ComputeNode(hypervisor_hostname='node2')
]
with mock.patch('nova.objects.ComputeNodeList.get_all') as mocked:
mocked.return_value = fake_compute_nodes
self.filt_cls = trusted_filter.TrustedFilter()
oat_data = {"hosts": [{"host_name": "node1",
"trust_lvl": "untrusted",
"vtime": "2012-09-09T05:10:40-04:00"}]}
req_mock.return_value = requests.codes.OK, oat_data
extra_specs = {'trust:trusted_host': 'trusted'}
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024,
extra_specs=extra_specs))
host = fakes.FakeHostState('host1', 'node1', {})
self.filt_cls.host_passes(host, spec_obj) # Fill the caches
self.assertTrue(req_mock.called)
self.assertEqual(1, req_mock.call_count)
call_args = list(req_mock.call_args[0])
expected_call_args = ['POST', 'PollHosts', ['node2', 'node1']]
self.assertJsonEqual(call_args, expected_call_args)
def test_trusted_filter_trusted_and_locale_formated_vtime_passes(self,
req_mock):
oat_data = {"hosts": [{"host_name": "host1",
"trust_lvl": "trusted",
"vtime": timeutils.utcnow().strftime(
"%c")},
{"host_name": "host2",
"trust_lvl": "trusted",
"vtime": timeutils.utcnow().strftime(
"%D")},
# This is just a broken date to ensure that
# we're not just arbitrarily accepting any
# date format.
]}
req_mock.return_value = requests.codes.OK, oat_data
extra_specs = {'trust:trusted_host': 'trusted'}
spec_obj = objects.RequestSpec(
context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024,
extra_specs=extra_specs))
host = fakes.FakeHostState('host1', 'host1', {})
bad_host = fakes.FakeHostState('host2', 'host2', {})
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
self.assertFalse(self.filt_cls.host_passes(bad_host,
spec_obj))

View File

@ -0,0 +1,8 @@
---
upgrade:
- |
The *TrustedFilter* along with its related ``[trusted_computing]``
configuration options were deprecated in the 16.0.0 Pike release and have
been removed in the 17.0.0 Queens release. The *TrustedFilter* was always
experimental, had no continuous integration testing to prove it still
worked, and no reported users.