Merge "Remove deprecated TrustedFilter"
This commit is contained in:
commit
690f8e4e24
@ -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:
|
||||||
|
|
||||||
TypeAffinityFilter
|
TypeAffinityFilter
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 123 KiB |
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
@ -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
|
increase security. For more information, see the `OpenStack Security Guide
|
||||||
<https://docs.openstack.org/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
|
Encrypt Compute metadata traffic
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ System administration
|
|||||||
live-migration-usage.rst
|
live-migration-usage.rst
|
||||||
remote-console-access.rst
|
remote-console-access.rst
|
||||||
service-groups.rst
|
service-groups.rst
|
||||||
security.rst
|
|
||||||
node-down.rst
|
node-down.rst
|
||||||
adv-config.rst
|
adv-config.rst
|
||||||
|
|
||||||
|
@ -154,14 +154,6 @@ There are many standard filter classes which may be used
|
|||||||
a set of instances.
|
a set of instances.
|
||||||
* |RetryFilter| - filters hosts that have been attempted for scheduling.
|
* |RetryFilter| - filters hosts that have been attempted for scheduling.
|
||||||
Only passes hosts that have not been previously attempted.
|
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
|
* |TypeAffinityFilter| - Only passes hosts that are not already running an
|
||||||
instance of the requested type.
|
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
|
case in the scheduler logs, then your problem is most likely related to a
|
||||||
compute problem and you should check the compute logs.
|
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
|
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
|
through the use of flavor extra_specs in combination with the image properties, as
|
||||||
described in detail in the related nova-spec document:
|
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>`
|
.. |DifferentHostFilter| replace:: :class:`DifferentHostFilter <nova.scheduler.filters.affinity_filter.DifferentHostFilter>`
|
||||||
.. |SameHostFilter| replace:: :class:`SameHostFilter <nova.scheduler.filters.affinity_filter.SameHostFilter>`
|
.. |SameHostFilter| replace:: :class:`SameHostFilter <nova.scheduler.filters.affinity_filter.SameHostFilter>`
|
||||||
.. |RetryFilter| replace:: :class:`RetryFilter <nova.scheduler.filters.retry_filter.RetryFilter>`
|
.. |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>`
|
.. |TypeAffinityFilter| replace:: :class:`TypeAffinityFilter <nova.scheduler.filters.type_filter.TypeAffinityFilter>`
|
||||||
.. |AggregateTypeAffinityFilter| replace:: :class:`AggregateTypeAffinityFilter <nova.scheduler.filters.type_filter.AggregateTypeAffinityFilter>`
|
.. |AggregateTypeAffinityFilter| replace:: :class:`AggregateTypeAffinityFilter <nova.scheduler.filters.type_filter.AggregateTypeAffinityFilter>`
|
||||||
.. |ServerGroupAntiAffinityFilter| replace:: :class:`ServerGroupAntiAffinityFilter <nova.scheduler.filters.affinity_filter.ServerGroupAntiAffinityFilter>`
|
.. |ServerGroupAntiAffinityFilter| replace:: :class:`ServerGroupAntiAffinityFilter <nova.scheduler.filters.affinity_filter.ServerGroupAntiAffinityFilter>`
|
||||||
|
@ -610,207 +610,6 @@ Related options:
|
|||||||
* aggregate_image_properties_isolation_namespace
|
* 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",
|
metrics_group = cfg.OptGroup(name="metrics",
|
||||||
title="Metrics parameters",
|
title="Metrics parameters",
|
||||||
help="""
|
help="""
|
||||||
@ -938,9 +737,6 @@ def register_opts(conf):
|
|||||||
conf.register_group(filter_scheduler_group)
|
conf.register_group(filter_scheduler_group)
|
||||||
conf.register_opts(filter_scheduler_opts, 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_group(metrics_group)
|
||||||
conf.register_opts(metrics_weight_opts, group=metrics_group)
|
conf.register_opts(metrics_weight_opts, group=metrics_group)
|
||||||
|
|
||||||
@ -948,5 +744,4 @@ def register_opts(conf):
|
|||||||
def list_opts():
|
def list_opts():
|
||||||
return {scheduler_group: scheduler_opts,
|
return {scheduler_group: scheduler_opts,
|
||||||
filter_scheduler_group: filter_scheduler_opts,
|
filter_scheduler_group: filter_scheduler_opts,
|
||||||
trust_group: trusted_opts,
|
|
||||||
metrics_group: metrics_weight_opts}
|
metrics_group: metrics_weight_opts}
|
||||||
|
@ -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
|
|
@ -61,8 +61,6 @@ class TestAggregateInstanceExtraSpecsFilter(test.NoDBTestCase):
|
|||||||
'opt1': '1',
|
'opt1': '1',
|
||||||
# Scoped extra spec that applies to this filter
|
# Scoped extra spec that applies to this filter
|
||||||
'aggregate_instance_extra_specs:opt2': '2',
|
'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)
|
self._do_test_aggregate_filter_extra_specs(especs, passes=True)
|
||||||
|
|
||||||
@ -73,8 +71,6 @@ class TestAggregateInstanceExtraSpecsFilter(test.NoDBTestCase):
|
|||||||
'opt1': '1',
|
'opt1': '1',
|
||||||
# Scoped extra spec that applies to this filter
|
# Scoped extra spec that applies to this filter
|
||||||
'aggregate_instance_extra_specs:opt1': '3',
|
'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)
|
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'])}
|
agg_mock.return_value = {'opt1': set(['1']), 'opt2': set(['2'])}
|
||||||
especs = {
|
especs = {
|
||||||
'opt1': '1',
|
'opt1': '1',
|
||||||
'opt2': '222',
|
'opt2': '222'
|
||||||
'trust:trusted_host': 'true'
|
|
||||||
}
|
}
|
||||||
self._do_test_aggregate_filter_extra_specs(especs, passes=False)
|
self._do_test_aggregate_filter_extra_specs(especs, passes=False)
|
||||||
|
@ -107,20 +107,19 @@ class TestComputeCapabilitiesFilter(test.NoDBTestCase):
|
|||||||
def test_compute_filter_passes_extra_specs_simple(self):
|
def test_compute_filter_passes_extra_specs_simple(self):
|
||||||
self._do_test_compute_filter_extra_specs(
|
self._do_test_compute_filter_extra_specs(
|
||||||
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
||||||
especs={'opt1': '1', 'opt2': '2', 'trust:trusted_host': 'true'},
|
especs={'opt1': '1', 'opt2': '2'},
|
||||||
passes=True)
|
passes=True)
|
||||||
|
|
||||||
def test_compute_filter_fails_extra_specs_simple(self):
|
def test_compute_filter_fails_extra_specs_simple(self):
|
||||||
self._do_test_compute_filter_extra_specs(
|
self._do_test_compute_filter_extra_specs(
|
||||||
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
||||||
especs={'opt1': '1', 'opt2': '222', 'trust:trusted_host': 'true'},
|
especs={'opt1': '1', 'opt2': '222'},
|
||||||
passes=False)
|
passes=False)
|
||||||
|
|
||||||
def test_compute_filter_pass_extra_specs_simple_with_scope(self):
|
def test_compute_filter_pass_extra_specs_simple_with_scope(self):
|
||||||
self._do_test_compute_filter_extra_specs(
|
self._do_test_compute_filter_extra_specs(
|
||||||
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
||||||
especs={'capabilities:opt1': '1',
|
especs={'capabilities:opt1': '1'},
|
||||||
'trust:trusted_host': 'true'},
|
|
||||||
passes=True)
|
passes=True)
|
||||||
|
|
||||||
def test_compute_filter_pass_extra_specs_same_as_scope(self):
|
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):
|
def test_compute_filter_extra_specs_simple_with_wrong_scope(self):
|
||||||
self._do_test_compute_filter_extra_specs(
|
self._do_test_compute_filter_extra_specs(
|
||||||
ecaps={'opt1': 1, 'opt2': 2},
|
ecaps={'opt1': 1, 'opt2': 2},
|
||||||
especs={'wrong_scope:opt1': '1',
|
especs={'wrong_scope:opt1': '1'},
|
||||||
'trust:trusted_host': 'true'},
|
|
||||||
passes=True)
|
passes=True)
|
||||||
|
|
||||||
def test_compute_filter_extra_specs_pass_multi_level_with_scope(self):
|
def test_compute_filter_extra_specs_pass_multi_level_with_scope(self):
|
||||||
self._do_test_compute_filter_extra_specs(
|
self._do_test_compute_filter_extra_specs(
|
||||||
ecaps={'stats': {'opt1': {'a': 1, 'b': {'aa': 2}}, 'opt2': 2}},
|
ecaps={'stats': {'opt1': {'a': 1, 'b': {'aa': 2}}, 'opt2': 2}},
|
||||||
especs={'opt1:a': '1', 'capabilities:opt1:b:aa': '2',
|
especs={'opt1:a': '1', 'capabilities:opt1:b:aa': '2'},
|
||||||
'trust:trusted_host': 'true'},
|
|
||||||
passes=True)
|
passes=True)
|
||||||
|
|
||||||
def test_compute_filter_pass_ram_with_backward_compatibility(self):
|
def test_compute_filter_pass_ram_with_backward_compatibility(self):
|
||||||
|
@ -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))
|
|
@ -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.
|
Loading…
Reference in New Issue
Block a user