Merge lp:~fred-yang/nova/TrustedComputingPools into lp:~hudson-openstack/nova/trunk

Proposed by fred yang
Status: Needs review
Proposed branch: lp:~fred-yang/nova/TrustedComputingPools
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 1441 lines (+1393/-0)
9 files modified
nova/scheduler/attestation/__init__.py (+24/-0)
nova/scheduler/attestation/attestation.py (+124/-0)
nova/scheduler/attestation/client.py (+223/-0)
nova/scheduler/attestation/service.py (+204/-0)
nova/scheduler/filters/__init__.py (+1/-0)
nova/scheduler/filters/json_filter_integrity.py (+124/-0)
nova/scheduler/manager_integrity.py (+240/-0)
nova/tests/scheduler/test_json_filter_integrity.py (+251/-0)
nova/tests/scheduler/test_manager_integrity.py (+202/-0)
To merge this branch: bzr merge lp:~fred-yang/nova/TrustedComputingPools
Reviewer Review Type Date Requested Status
Paul Voccio (community) Needs Fixing
Sandy Walsh (community) Needs Fixing
Chris Behrens (community) Needs Fixing
Review via email: mp+76134@code.launchpad.net

Description of the change

Enable Trusted Computing Pools into Openstack scheduler by adding new filter driver
Spec/Design - http://wiki.openstack.org/TrustedComputingPools
Blueprint discussed at Diablo Summit - https://blueprints.launchpad.net/nova/+spec/trusted-computing-pools

To post a comment you must log in.
Revision history for this message
Chris Behrens (cbehrens) wrote :

This looks really interesting! I haven't done a thorough review yet, but it looks like around line 276, there's extreme indenting. Maybe there are tabs instead of spaces?

review: Needs Fixing
Revision history for this message
Sandy Walsh (sandy-walsh) wrote :

Thanks Fred ... perhaps we can start with a pep8 cleanup and ensure it complies with the HACKING document. Also if you could remove all the debug/commented out code?

Then we can get into the nitty gritty of it.

review: Needs Fixing
Revision history for this message
Johannes Erdfelt (johannes.erdfelt) wrote :

I don't know enough about the scheduler to comment on the functionality of the patch, but I do have a style comment. It doesn't seem necessary to split tlvl_string and tlvl_value as separate lists. Especially since the code combines them into a dict at runtime. It's probably cleaner to create a dict literal instead.

Revision history for this message
Paul Voccio (pvo) wrote :

 Wanted to echo removing comments and some of the formatting the others
 mentioned here.

 I got some 3 failures with the S3APITestCase. Not sure if this is related or
 not yet.
>
 Please also add yourself to the Authors file. This failed on the unit tests
 for me.

>
> ======================================================================
> FAIL: test_authors_up_to_date (nova.tests.test_misc.ProjectTestCase)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "/root/bzr/TrustedComputingPools/nova/tests/test_misc.py", line 93, in
> test_authors_up_to_date
> '%r not listed in Authors' % missing)
> AssertionError: set([u'<email address hidden>']) not listed in Authors

review: Needs Fixing
Revision history for this message
fred yang (fred-yang) wrote :

Woop! Learning the process. Should I submit the Authors file now, or I can do another clean-up to submit
Thanks,
-Fred

> Wanted to echo removing comments and some of the formatting the others
> mentioned here.
>
> I got some 3 failures with the S3APITestCase. Not sure if this is related or
> not yet.
> >
> Please also add yourself to the Authors file. This failed on the unit tests
> for me.
>
> >
> > ======================================================================
> > FAIL: test_authors_up_to_date (nova.tests.test_misc.ProjectTestCase)
> > ----------------------------------------------------------------------
> > Traceback (most recent call last):
> > File "/root/bzr/TrustedComputingPools/nova/tests/test_misc.py", line 93,
> in
> > test_authors_up_to_date
> > '%r not listed in Authors' % missing)
> > AssertionError: set([u'<email address hidden>']) not listed in Authors

Revision history for this message
fred yang (fred-yang) wrote :

Will start from pep 8 tool to clearn up ...
Thanks,
-Fred

> Thanks Fred ... perhaps we can start with a pep8 cleanup and ensure it
> complies with the HACKING document. Also if you could remove all the
> debug/commented out code?
>
> Then we can get into the nitty gritty of it.

Revision history for this message
fred yang (fred-yang) wrote :

Combining sting & value into a dict is good suggestion
Thanks,
-Fred

> I don't know enough about the scheduler to comment on the functionality of the
> patch, but I do have a style comment. It doesn't seem necessary to split
> tlvl_string and tlvl_value as separate lists. Especially since the code
> combines them into a dict at runtime. It's probably cleaner to create a dict
> literal instead.

Unmerged revisions

1587. By fred yang

First Trusted Computing Pools enabling

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'nova/scheduler/attestation'
=== added file 'nova/scheduler/attestation/__init__.py'
--- nova/scheduler/attestation/__init__.py 1970-01-01 00:00:00 +0000
+++ nova/scheduler/attestation/__init__.py 2011-09-20 01:13:28 +0000
@@ -0,0 +1,24 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright (c) 2010 Openstack, LLC.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17"""
18:mod:`nova.scheduler.attestation` -- API service to attestation server
19=====================================================
20
21.. automodule:: nova.scheduler.attestation
22 :platform: Unix
23 :synopsis: Module that provide https client to attestation server
24"""
025
=== added file 'nova/scheduler/attestation/attestation.py'
--- nova/scheduler/attestation/attestation.py 1970-01-01 00:00:00 +0000
+++ nova/scheduler/attestation/attestation.py 2011-09-20 01:13:28 +0000
@@ -0,0 +1,124 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2010-2011 OpenStack, LLC
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18"""
19Client classes for callers to attestation server
20"""
21
22import json
23from nova import log as logging
24
25from nova.scheduler.attestation import client
26LOG = logging.getLogger('nova.scheduler.attestation')
27
28class v10Client(client.BaseClient):
29
30 """Main client class for accessing Attestation server"""
31
32 DEFAULT_PORT = 4443
33
34 def __init__(self, host, port=None, use_ssl=False, doc_root="/v1",
35 auth_user=None, auth_passwd=None,
36 key_file=None, cert_file=None, ca_file=None):
37 """
38 Creates a new client to a Attestation API service.
39
40 :param host: The host where Attestation resides
41 :param port: The port where Attestation resides (defaults to 9292)
42 :param use_ssl: Should we use HTTPS? (defaults to False)
43 :param doc_root: Prefix for all URLs we request from host
44 :param auth_tok: The auth token to pass to the server
45 """
46 port = port or self.DEFAULT_PORT
47 self.doc_root = doc_root
48 super(v10Client, self).__init__(host, port, use_ssl,
49 auth_user, auth_passwd,
50 key_file, cert_file, ca_file)
51
52 def do_request(self, method, action, body=None, headers=None, params=None):
53 action = "%s/%s" % (self.doc_root, action)
54 return super(v10Client, self).do_request(method, action, body,
55 headers, params)
56
57 def post_hosts(self, hosts, PcrMask=None):
58 """
59 Post a list of hosts to attestation server to get trustworthiness
60 as well as PCR index back if specified -
61 Return RequestId
62
63 :hosts: hosts list to be posted
64 :PcrMask: PCR index to be retrieved
65 :TODO: limit the hosts count to be less than 500
66 """
67 body = {}
68 body['count'] = len(hosts)
69 if PcrMask:
70 body['PCRmask'] = PcrMask
71 #body['hosts'] = '+'.join(hosts)
72 body['hosts'] = hosts
73 cooked = json.dumps(body)
74 headers = {}
75 headers['Context-Type'] = 'application/json'
76 headers['Accept'] = 'application/json'
77 res = self.do_request("POST", "PostHosts", cooked, headers)
78 rheader = res.getheaders()
79 LOG.debug(_("post_hosts got return headers %(rheader)s " % locals()))
80
81 raw = json.loads(res.read())
82 RequestId = raw['RequestId']
83 LOG.debug(_("post_hosts got %(raw)s %(RequestId)s" % locals()))
84 return RequestId
85
86 def get_hosts(self, rid):
87 """
88 Get trustworthiness from previously posted hosts
89 """
90
91 #TODO: perform mark and limit read to pull all data
92 body = {}
93 body['RequestId'] = rid
94 cooked = json.dumps(body)
95 headers = {}
96 headers['Context-Type'] = 'application/json'
97 headers['Accept'] = 'application/json'
98 res = self.do_request("GET", "RequestId", cooked, headers)
99 rheader = res.getheaders()
100 LOG.debug(_("get_hosts got return headers %(rheader)s " % locals()))
101 raw = json.loads(res.read())
102 data = raw['RequestId']
103 return data
104
105 def poll_hosts(self, hosts, PcrMask=None):
106 """
107 same as post_hosts, but sync & wait for operation done to return data
108 """
109 body = {}
110 body['count'] = len(hosts)
111 if PcrMask:
112 body['PCRmask'] = PcrMask
113 #body['hosts'] = '+'.join(hosts)
114 body['hosts'] = hosts
115 headers = {}
116 headers['Context-Type'] = 'application/json'
117 headers['Accept'] = 'application/json'
118 body = json.dumps(body)
119 res = self.do_request("POST", "PollHosts", body, headers)
120 rheader = res.getheaders()
121 LOG.debug(_("Poll_hosts headers %(rheader)s " % locals()))
122 raw = json.loads(res.read())
123 data = raw['PollHosts']
124 return data
0125
=== added file 'nova/scheduler/attestation/client.py'
--- nova/scheduler/attestation/client.py 1970-01-01 00:00:00 +0000
+++ nova/scheduler/attestation/client.py 2011-09-20 01:13:28 +0000
@@ -0,0 +1,223 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright (c) 2010 Openstack, LLC.
4# Copyright 2010 United States Government as represented by the
5# Administrator of the National Aeronautics and Space Administration.
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
19
20"""
21Attestation Client service
22"""
23
24import httplib
25import logging
26import socket
27import urllib
28import ssl
29
30from nova import log as logging
31from nova import exception
32LOG = logging.getLogger('nova.scheduler.attestation.client')
33
34class HTTPSClientAuthConnection(httplib.HTTPSConnection):
35 """ Class to make a HTTPS connection, with support for full client-based SSL Authentication"""
36
37 def __init__(self, host, port, key_file, cert_file, ca_file, timeout=None):
38 httplib.HTTPSConnection.__init__(self, host, port,
39 key_file=key_file, cert_file=cert_file)
40 self.host = host
41 self.post = port
42 self.key_file = key_file
43 self.cert_file = cert_file
44 self.ca_file = ca_file
45 self.timeout = timeout
46
47 def connect(self):
48 """ Connect to a host on a given (SSL) port.
49 Use ca_file pointing somewhere, use it to check Server Certificate.
50 Redefined/copied and extended from httplib.py:1105 (Python 2.6.x).
51 """
52 host = self.host
53 port = self.port
54 sock = socket.create_connection((self.host, self.port), self.timeout)
55 if self._tunnel_host:
56 self.sock = sock
57 self._tunnel()
58 # If there's no CA File, don't force Server Certificate Check
59 if self.ca_file:
60 self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
61 ssl_version=ssl.PROTOCOL_SSLv23,
62 ca_certs=self.ca_file,
63 cert_reqs=ssl.CERT_REQUIRED)
64 LOG.debug(_("Https server certified " % locals()))
65 s = self.sock
66 cert = s.getpeercert()
67 else:
68 self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
69 cert_reqs=ssl.CERT_NONE)
70
71
72class BaseClient(object):
73
74 """A base client class"""
75
76 CHUNKSIZE = 65536
77
78 def __init__(self, host, port, use_ssl, auth_user, auth_passwd,
79 key_file=None, cert_file=None, ca_file=None):
80 """
81 Creates a new client to some service.
82
83 :param host: The host where service resides
84 :param port: The port where service resides
85 :param use_ssl: Should we use HTTPS?
86 :param auth_user: user name to connect with attestation server
87 :param auth_passwd: password to get attestation server authentication
88 """
89 self.host = host
90 self.port = port
91 self.use_ssl = use_ssl
92 self.auth_user = auth_user
93 self.auth_passwd = auth_passwd
94 self.connection = None
95 self.key_file = key_file
96 self.cert_file = cert_file
97 self.ca_file = ca_file
98
99 def set_auth_blob(self, auth_user, auth_passwd):
100 """
101 Updates the authentication token for this client connection.
102 """
103 self.auth_user = auth_user
104 self.auth_passwd = auth_passwd
105
106 def get_connection(self):
107 """
108 Returns the proper connection type
109 """
110 host = self.host
111 port = self.port
112 if self.use_ssl:
113 return HTTPSClientAuthConnection(self.host, self.port,
114 key_file=self.key_file,
115 cert_file=self.cert_file,
116 ca_file=self.ca_file)
117 else:
118 return httplib.HTTPConnection(self.host, self.port)
119
120 def do_request(self, method, action, body=None, headers=None, params=None):
121 """
122 Connects to the server and issues a request. Handles converting
123 any returned HTTP error status codes to OpenStack/Glance exceptions
124 and closing the server connection. Returns the result data, or
125 raises an appropriate exception.
126
127 """
128 if type(params) is dict:
129
130 # remove any params that are None
131 for (key, value) in params.items():
132 if value is None:
133 del params[key]
134
135 action += '?' + urllib.urlencode(params)
136
137 try:
138 c = self.get_connection()
139 headers = headers or {}
140 if 'x-auth-user' not in headers and self.auth_user:
141 headers['x-auth-user'] = self.auth_user
142 if 'x-auth-passwd' not in headers and self.auth_passwd:
143 headers['x-auth-passwd'] = self.auth_passwd
144
145 # Do a simple request or a chunked request, depending
146 # on whether the body param is a file-like object and
147 # the method is PUT or POST
148 #if hasattr(body, 'read') and method.lower() in ('post', 'put'):
149 if body:
150 # query is in body
151 c.putrequest(method, action)
152
153 for header, value in headers.items():
154 c.putheader(header, value)
155 #c.putheader('Content-type', 'text/json')
156 c.putheader('Content-length', len(body))
157 c.putheader('Transfer-Encoding', 'chunked')
158 c.endheaders()
159
160 #chunk = body.read(self.CHUNKSIZE)
161 c.send('%s' % (body))
162 c.send('0\r\n\r\n')
163 else:
164 # Simple request.through uri
165 c.request(method, action, body, headers)
166 res = c.getresponse()
167 #status_code = self.get_status_code(res)
168 status_code = res.status
169 if status_code in (httplib.OK,
170 httplib.CREATED,
171 httplib.ACCEPTED,
172 httplib.NO_CONTENT):
173 return res
174 elif status_code == httplib.UNAUTHORIZED:
175 raise exception.NotAuthorized
176 elif status_code == httplib.FORBIDDEN:
177 raise exception.NotAuthorized
178 elif status_code == httplib.NOT_FOUND:
179 raise exception.NotFound
180 elif status_code == httplib.CONFLICT:
181 raise exception.Duplicate(res.read())
182 elif status_code == httplib.BAD_REQUEST:
183 raise exception.Invalid(res.read())
184 elif status_code == httplib.INTERNAL_SERVER_ERROR:
185 raise Exception("Internal Server error: %s" % res.read())
186 else:
187 raise Exception("Unknown error occurred! %s" % res.read())
188
189 except (socket.error, IOError), e:
190 raise Exception("Unable to connect to server. Got error: %s" % e)
191
192 def get_status_code(self, response):
193 """
194 Returns the integer status code from the response, which
195 can be either a Webob.Response (used in testing) or httplib.Response
196 """
197 if hasattr(response, 'status_int'):
198 return response.status_int
199 else:
200 return response.status
201
202 def _extract_params(self, actual_params, allowed_params):
203 """
204 Extract a subset of keys from a dictionary. The filters key
205 will also be extracted, and each of its values will be returned
206 as an individual param.
207
208 :param actual_params: dict of keys to filter
209 :param allowed_params: list of keys that 'actual_params' will be
210 reduced to
211 :retval subset of 'params' dict
212 """
213 try:
214 # expect 'filters' param to be a dict here
215 result = dict(actual_params.get('filters'))
216 except TypeError:
217 result = {}
218
219 for allowed_param in allowed_params:
220 if allowed_param in actual_params:
221 result[allowed_param] = actual_params[allowed_param]
222
223 return result
0224
=== added file 'nova/scheduler/attestation/service.py'
--- nova/scheduler/attestation/service.py 1970-01-01 00:00:00 +0000
+++ nova/scheduler/attestation/service.py 2011-09-20 01:13:28 +0000
@@ -0,0 +1,204 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright (c) 2010 Openstack, LLC.
4# Copyright 2010 United States Government as represented by the
5# Administrator of the National Aeronautics and Space Administration.
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
19
20"""
21Attestation Service
22"""
23
24import datetime
25
26from nova import flags
27from nova import log as logging
28from nova import utils
29
30from nova.scheduler.attestation import attestation
31
32
33LOG = logging.getLogger('nova.scheduler.attestaion.service')
34FLAGS = flags.FLAGS
35flags.DEFINE_integer('attestation_geo_required', 0,
36 'require to use PCR22 as geo indicator')
37flags.DEFINE_integer('attestation_max_hosts', 300,
38 'max number of hosts per request')
39
40flags.DEFINE_string('attestation_server', 'localhost',
41 'attestation server http')
42flags.DEFINE_string('attestation_server_ca_file', None,
43 'attestation server Cert file for Identity verification')
44flags.DEFINE_string('attestation_port', '4443',
45 'attestation server port')
46flags.DEFINE_string('attestation_auth_user', 'MustSetupThisUser',
47 'attestation access user - must change')
48flags.DEFINE_string('attestation_auth_passwd', 'MustChangeThisPasswd',
49 'attestation access passwd - must change')
50
51class AttestationService(object):
52 def __init__(self):
53 self.host = FLAGS.attestation_server
54 self.port = FLAGS.attestation_port
55 self.auth_user = FLAGS.attestation_auth_user
56 self.auth_passwd = FLAGS.attestation_auth_passwd
57 self.ca_file = FLAGS.attestation_server_ca_file
58 self.use_ssl = True
59 self.doc = "v1.0"
60 self.attestation = attestation.v10Client(self.host, self.port,
61 self.use_ssl, self.doc, self.auth_user, self.auth_passwd,
62 ca_file = self.ca_file)
63 self.req_count = FLAGS.attestation_max_hosts or 300
64 # max number of hosts per request
65 # TODO: retrive from server
66 self.requests = {} # tracks request ids with host list
67 self.req_id = []
68
69 tlvl_string = ['trusted', 'untrusted', 'unknown']
70 tlvl_value = ['lvl10', 'lvl01', 'lvl01']
71
72 def _post_hosts(self, hosts, PcrMask=None):
73 total = len(hosts)
74 max = self.req_count
75 n = (total + max - 1) // max
76 count_per_loop = total // n
77 start = 0
78 if FLAGS.attestation_geo_required:
79 PcrMask = '400000' # also read PCR22 in
80 #TODO: need error handling
81 while total:
82 if total < count_per_loop:
83 count_per_loop = total
84 list = hosts[start:count_per_loop]
85 rid = self.attestation.post_hosts(list, PcrMask)
86 self.req_id.append(rid)
87 total -= count_per_loop
88 start += count_per_loop
89 return {}
90
91 def _get_hosts(self, hosts):
92 """
93 get result from previously posted rid
94 """
95 if not len(self.req_id):
96 return {}
97 data = []
98 done = []
99 index = 0
100 for rid in self.req_id:
101 res = self.attestation.get_hosts(rid)
102 hosts = self._process_packet(res, rid)
103 if isinstance(hosts, dict):
104 # got result
105 data.append(hosts)
106 done.append(rid)
107 else:
108 # TODO: something wrong, need clean-up
109 # return hosts to get caller to issue command again
110 return 'BAD_DATA'
111 index += 1
112
113 report = self._pack_host_data(data)
114 for rid in done:
115 self.req_id.remove(rid)
116 return report
117
118 def _poll_hosts(self, hosts, PcrMask=None):
119 total = len(hosts)
120 max = self.req_count
121 n = (total + max - 1) // max
122 count_per_loop = total // n
123 start = 0
124 if FLAGS.attestation_geo_required:
125 PcrMask = '400000' # also read PCR22 in
126 #TODO: need error handling
127 res = []
128 while total:
129 if total < count_per_loop:
130 count_per_loop = total
131 list = hosts[start:count_per_loop]
132 data = self.attestation.poll_hosts(list, PcrMask)
133 if isinstance(data, dict):
134 res.append(data)
135 total -= count_per_loop
136 start += count_per_loop
137 report = self._pack_host_data(res)
138 return report
139
140 commands = {
141 'post_hosts': _post_hosts,
142 'get_hosts': _get_hosts,
143 'poll_hosts': _poll_hosts,
144 }
145
146 def do_attestation(self, cmd, hosts=None):
147 """
148 cmd: [post_hosts, get_hosts, poll_hosts]
149 """
150 method = self.commands[cmd.lower()] # Let exception fly.
151 report = method(self, hosts)
152 return report
153
154 def _pack_host_data(self, data):
155 report = {}
156 cvt = dict(zip(self.tlvl_string, self.tlvl_value))
157 for item in data:
158 count = item['count']
159 hosts = item['hosts']
160 for host, state in hosts.iteritems():
161 entry = {}
162 entry['trust_lvl'] = cvt.get(state['trust_lvl'], {})
163 entry['vtime'] = state['timestamp']
164 if FLAGS.attestation_geo_required:
165 entry['geo'] = state[PCR22]
166 report[host] = entry
167 count -= 1
168 if count:
169 LOG.warn(_("Get hosts missed count %(count)s" % locals()))
170 return report
171
172
173 def _process_packet(self, packet, rid):
174 """
175 Expected input format
176 { 'jobid' : rid,
177 'jobstatus' : 0/1/2 (processing/success/failed)
178 'jobcode' : 0 or others
179 'jobresult' : "error information"
180 (continue hosts information if success)
181 'count' : 2
182 'hosts' : [
183 {host1 : {
184 'trust_lvl': trusted|untrusted|unknown
185 'timestamp': UTC
186 'PCRxx' : xxx},
187 host2 : {
188 'trust_lvl': trusted|untrusted|unknown
189 'timestamp': UTC
190 'PCRxx' : xxx},
191 }]
192 }
193 """
194 if not packet['jobid'] == rid:
195 LOG.warn(_("Incoming packet has wrong RequestId" % locals()))
196 return 2
197 status = packet['jobstatus']
198 if status is 2:
199 result = packet['jobresult']
200 LOG.warn(_("Incoming packet error %(result)s " % locals()))
201 return 2
202 if status is 1:
203 return packet
204 return {}
0205
=== modified file 'nova/scheduler/filters/__init__.py'
--- nova/scheduler/filters/__init__.py 2011-08-15 22:09:39 +0000
+++ nova/scheduler/filters/__init__.py 2011-09-20 01:13:28 +0000
@@ -34,3 +34,4 @@
34from all_hosts_filter import AllHostsFilter34from all_hosts_filter import AllHostsFilter
35from instance_type_filter import InstanceTypeFilter35from instance_type_filter import InstanceTypeFilter
36from json_filter import JsonFilter36from json_filter import JsonFilter
37from json_filter_integrity import JsonFilterIntegrity
3738
=== added file 'nova/scheduler/filters/json_filter_integrity.py'
--- nova/scheduler/filters/json_filter_integrity.py 1970-01-01 00:00:00 +0000
+++ nova/scheduler/filters/json_filter_integrity.py 2011-09-20 01:13:28 +0000
@@ -0,0 +1,124 @@
1# Copyright (c) 2011 Openstack, LLC.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16"""
17Supporting trusted compute pool host filtering for Json
18"""
19
20import json
21
22from nova import exception
23from nova import flags
24from nova import log as logging
25from nova import utils
26from nova.scheduler.filters import json_filter
27
28LOG = logging.getLogger('nova.scheduler.filter.json_filter_integrity')
29
30FLAGS = flags.FLAGS
31flags.DEFINE_integer('trust_pool_enforced', 1,
32 'enforce compute nodes to be trusted or non-trusted')
33
34#currently supported commands
35trust_value = ['trusted']
36untrust_value = ['Untrusted']
37basic_cmd = ['=']
38extra_cmd = ['>=']
39tlvl_string = ['trusted', 'untrusted']
40tlvl_value = ['lvl10', 'lvl01']
41
42class JsonFilterIntegrity(json_filter.JsonFilter):
43 """Host Filter to allow simple JSON-based grammar for
44 selecting hosts.
45 """
46
47 def _trust_pool_reconfirm(self, zone_manager, hosts):
48 return zone_manager.integrity_service.reconfirm(hosts)
49
50
51 def _convert_trust_query(self, cmd, arg):
52 """pre-scan query to sanitize trust_lvl request"""
53 if len(arg) != 2:
54 raise exception.Error(_("Bad $trust_state combination"))
55
56 # convert to internal value
57 cvt = dict(zip(tlvl_string, tlvl_value))
58 value = cvt.get(arg[1].lower(), {})
59 if value:
60 tmp = [arg[0], value]
61 else:
62 tmp = arg
63 v = arg[1]
64 if cmd in extra_cmd and v in trust_value:
65 return tmp
66 if cmd in basic_cmd and (v in trust_value or v in untrust_value):
67 return tmp
68 raise exception.Error(_("Bad $trust_state combination"))
69
70 def _process_tfilter(self, zone_manager, query, host, services, c):
71 """Recursively parse the query structure."""
72 if len(query) == 0:
73 return c, True
74 cmd = query[0]
75 method = self.commands[cmd] # Let exception fly.
76 cooked_args = []
77 for arg in query[1:]:
78 if isinstance(arg, list):
79 c, arg = self._process_tfilter(
80 zone_manager, arg, host, services, c)
81 elif isinstance(arg, basestring):
82 arg = self._parse_string(arg, host, services)
83 if arg != None:
84 cooked_args.append(arg)
85
86 if cooked_args and isinstance(cooked_args[0], str):
87 if len(cooked_args[0]) > 3 and cooked_args[0][0:3] == 'lvl':
88 cooked_args = self._convert_trust_query(cmd, cooked_args)
89 c += 1
90
91 result = method(self, cooked_args)
92 return c, result
93
94 def filter_hosts(self, zone_manager, query):
95 """Return a list of hosts that can fulfill filter."""
96 expanded = json.loads(query)
97 filtered_hosts = []
98 for host, services in zone_manager.service_states.iteritems():
99 c = 0
100 c, r = self._process_tfilter(
101 zone_manager, expanded, host, services, c)
102 if isinstance(r, list):
103 r = True in r
104 if r:
105 filtered_hosts.append((host, services))
106
107 if not c and FLAGS.trust_pool_enforced:
108 lists = []
109 # There is no trust_lvl from query, so force all request
110 # to only untrusted nodes
111 cvt = dict(zip(tlvl_string, tlvl_value))
112 untrusted = cvt.get('untrusted', {})
113 query = ["=", "$trust_state.trust_lvl", untrusted]
114 for host, services in filtered_hosts:
115 r = self._process_filter(zone_manager, query, host, services)
116 if isinstance(r, list):
117 r = True in r
118 if r:
119 lists.append((host, services))
120 filtered_hosts = lists
121 filtered_hosts = self._trust_pool_reconfirm(
122 zone_manager, filtered_hosts)
123 LOG.debug(_("Found hosts %(filtered_hosts)s" % locals()))
124 return filtered_hosts
0125
=== added file 'nova/scheduler/manager_integrity.py'
--- nova/scheduler/manager_integrity.py 1970-01-01 00:00:00 +0000
+++ nova/scheduler/manager_integrity.py 2011-09-20 01:13:28 +0000
@@ -0,0 +1,240 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright (c) 2010 Openstack, LLC.
4# Copyright 2010 United States Government as represented by the
5# Administrator of the National Aeronautics and Space Administration.
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
19
20"""
21Scheduler Service
22"""
23
24import functools
25import datetime
26
27from nova import context
28from nova import db
29from nova import flags
30from nova import log as logging
31from nova import manager
32from nova import rpc
33from nova import utils
34from nova.scheduler import manager
35from nova.scheduler.attestation import service
36
37LOG = logging.getLogger('nova.scheduler.manager_integrity')
38FLAGS = flags.FLAGS
39flags.DEFINE_bool('integrity_reconfirm', False,
40 'real-time reconfirm integrity')
41flags.DEFINE_bool('trusted_pool_debug', False,
42 'work-around before attestation server is ready')
43
44class IntegrityService(object):
45 """
46 Trusted computing pool service - filters and provide cache for
47 compute nodes' trustworthiness through attestation
48 """
49
50 def __init__(self, zone_manager):
51 self.zone_manager = zone_manager
52 self.zone_manager.inflight_hosts = {} #{host:utc (for testing)}
53 self.zone_manager.tick_snapshots = {} #{host:{count:tick, update:UTC}}
54 self.zone_manager.trust_cache = {} #{host:trust_lvl}
55 self.zone_manager.attestation_service = AttestationService()
56
57 def snapshot_update(self, host, service):
58 """
59 update lastest compute node's report status into last snapshots
60 tick_snapshots = {host:{'count':report_count,'updated_at':updated_at}}
61 tick_snapshot{} is used for checking if a node has been rebooted since
62 """
63 updated_at = service['updated_at'] or service['created_at']
64 self.zone_manager.tick_snapshots[host] = {'updated_at' : updated_at,
65 'report_count':service['report_count']}
66
67 def _do_attestation(self, method, hosts):
68 """expect integrity_report {'host':{trust_lvl:lvl, vtime:UTC}}"""
69 return self.zone_manager.attestation_service.do_attestation(
70 method, hosts)
71
72 def cache_states_update(self, integrity_report):
73 """called right after do_attestation"""
74 for host, state in integrity_report.iteritems():
75 self.zone_manager.trust_cache[host] = state['trust_lvl']
76 self.zone_manager.update_service_capabilities(
77 'trust_state', host, state)
78 del self.zone_manager.inflight_hosts[host]
79
80 def post_hosts(self, hosts):
81 """POST list list of hosts to attestation server"""
82 lists = []
83 now = utils.utcnow()
84 for host in hosts:
85 self.zone_manager.inflight_hosts[host] = now
86 lists.append(host)
87 if lists:
88 self._do_attestation('POST_HOSTS', lists)
89 #LOG.debug(_("POST_HOSTS %(lists)s" % locals()))
90
91 def get_hosts(self, context=None):
92 """GET previously posted attestation results """
93 """TODO: need to break hosts into controable size for GET"""
94 hosts = []
95 for host, time in self.zone_manager.inflight_hosts.iteritems():
96 hosts.append(host)
97 # expect integrity_report {'host':{Trust_lvl:lvl, vtime:UTC}}
98 if hosts:
99 integrity_report = self._do_attestation('GET_HOSTS', hosts)
100 self.cache_states_update(integrity_report)
101
102 def post_get_host(self, host, context=None):
103 """POST and GET for a host's integrity report"""
104 # expect integrity_report {'host':{Trust_lvl:lvl, vtime:UTC}}
105 intergity_report = self._do_attestation('POLL_HOSTS', host)
106 self.cache_states_update(integrity_report)
107
108 def service_is_down(self, service):
109 """check if a compute node has missed a heatbeat since"""
110 heartbeat = service['updated_at'] or service['created_at']
111 x = utils.utcnow()
112 elapsed = utils.utcnow() - heartbeat
113 return elapsed >= datetime.timedelta(seconds=FLAGS.service_down_time)
114
115 def host_is_rebooted(self, service, snapshot):
116 """
117 check if a host was rebooted if report_count was discontinuous
118 this should only be called after service_is_down() verifed
119 """
120 e_tick = service['report_count'] - snapshot['report_count']
121 e_time = service['updated_at'] - snapshot['updated_at']
122 unit = datetime.timedelta(seconds=FLAGS.report_interval)
123 #LOG.debug(_("e_tick %(e_tick)s e_time %(e_time)s" % locals()))
124 return not e_tick * unit <= e_time <= (e_tick+1) * unit
125
126 def cached_data_removal(self, host, service):
127 if host in self.zone_manager.inflight_hosts:
128 del self.zone_manager.inflight_hosts[host]
129 #self.service_state_integrity_update(host, {})
130 self.zone_manager.update_service_capabilities('trust_state', host, {})
131
132 def collect_new_hosts(self, context):
133 """locate hosts who were rebooted in the last FLAG.report_interval"""
134 services = db.service_get_all_by_topic(context, 'compute')
135 hosts = []
136 for service in services:
137 snapshot = self.zone_manager.tick_snapshots.get(service.host, {})
138 if self.service_is_down(service):
139 if snapshot:
140 del self.zone_manager.tick_snapshots[service.host]
141 self.cached_data_removal(service.host, service)
142 else:
143 if not snapshot or self.host_is_rebooted(service, snapshot):
144 self.cached_data_removal(service.host, service)
145 hosts.append(service.host)
146 self.snapshot_update(service.host, service)
147 return hosts
148
149 def cache_setup(self, context=None):
150 """
151 Populate service_states{} with cached integrity data
152 runs as periodic task from scheduler for every FLAG.report_interval
153 """
154 self.get_hosts(context)
155 hosts = self.collect_new_hosts(context)
156 self.post_hosts(hosts)
157
158 def reconfirm(self, lists):
159 """
160 verify if we need to do the very last minute verifcation
161 see if any candidate host got rebooted since last snapshot
162 """
163 if FLAGS.integrity_reconfirm:
164 hosts = []
165 for host, service in lists:
166 hosts.append(host)
167 integrity_report = self._do_attestation('POLL_HOSTS', hosts)
168 self.cache_states_update(integrity_report)
169 hosts = []
170 for host, service in lists:
171 lvl = service['trust_state'].get('trust_lvl', {})
172 if integrity_report[host].get('trust_lvl', {}) == lvl:
173 hosts.append((host, service))
174 else:
175 LOG.warn(_("mis-matched %(host)s trust_lvl %(lvl)s"
176 % locals()))
177 return hosts
178
179 #TODO:: need a better mechanism
180 # collect all good hosts
181 ctx = context.get_admin_context()
182 services = db.service_get_all_by_topic(ctx, 'compute')
183 hosts_good = {}
184 for service in services:
185 if not self.service_is_down(service):
186 snapshot = self.zone_manager.tick_snapshots.get(service.host,{})
187 if snapshot and not self.host_is_rebooted(service, snapshot):
188 hosts_good[service.host] = 'v'
189
190 hosts = []
191 for host, service in lists:
192 if hosts_good.get(host, None):
193 lvl = service['trust_state'].get('trust_lvl', {})
194 if self.zone_manager.trust_cache[host] == lvl:
195 hosts.append((host, service))
196 return hosts
197
198
199class AttestationService(object):
200 def __init__(self):
201 if FLAGS.trusted_pool_debug is True:
202 """
203 only used for unit test
204 """
205 utc = utils.utcnow() - datetime.timedelta(0)
206 self.tlvl_db = {}
207 # tlvl_string = ['trusted', 'untrusted']
208 # tlvl_value = ['lvl10', 'lvl01']
209 # must match with host_filter_integrity.py
210 self.tlvl_db['storky'] = {'trust_lvl':'lvl10', 'vtime':utc}
211 self.tlvl_db['stocky'] = {'trust_lvl':'lvl01', 'vtime':utc}
212 else:
213 self.attestation = service.AttestationService()
214
215 def do_attestation(self, cmd, hosts):
216 #TODO: final code must do tlvl_string to tlvl_value conversion
217 if FLAGS.trusted_pool_debug is True:
218 report = {}
219 utc = utils.utcnow() - datetime.timedelta(0)
220 for host in hosts:
221 lvl = self.tlvl_db[host].get('trust_lvl', {})
222 report[host] = {'trust_lvl':lvl,'vtime':utc}
223 return report
224 else:
225 return self.attestation.do_attestation(cmd, hosts)
226
227class SchedulerManager(manager.SchedulerManager):
228 """inject a periodic_task into schedule for building integrity cache"""
229 def __init__(self, scheduler_driver=None, *args, **kwargs):
230 super(SchedulerManager, self).__init__(*args, **kwargs)
231 self.zone_manager.integrity_service= IntegrityService(self.zone_manager)
232
233 def setup_test_attestation(self, module):
234 """ testing purpost"""
235 self.zone_manager.integrity_service._do_attestation = module
236
237 def periodic_tasks(self, context=None):
238 super(SchedulerManager, self).periodic_tasks(context)
239 """Setup integrity cache for this zone"""
240 self.zone_manager.integrity_service.cache_setup(context)
0241
=== added file 'nova/tests/scheduler/test_json_filter_integrity.py'
--- nova/tests/scheduler/test_json_filter_integrity.py 1970-01-01 00:00:00 +0000
+++ nova/tests/scheduler/test_json_filter_integrity.py 2011-09-20 01:13:28 +0000
@@ -0,0 +1,251 @@
1# Copyright 2011 OpenStack LLC.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15"""
16Tests For Scheduler Host Filters.
17"""
18
19import json
20
21import time
22import datetime
23from nova import utils
24from nova import exception
25from nova import flags
26from nova import test
27from nova.scheduler.filters import json_filter
28from nova.scheduler.filters import json_filter_integrity
29from nova import log as logging
30
31FLAGS = flags.FLAGS
32
33
34class FakeZoneManager:
35 pass
36
37
38class HostFilterTestCase(test.TestCase):
39 """Test case for host filters."""
40
41 def _host_caps(self, multiplier):
42 # Returns host capabilities in the following way:
43 # host1 = memory:free 10 (100max)
44 # disk:available 100 (1000max)
45 # hostN = memory:free 10 + 10N
46 # disk:available 100 + 100N
47 # in other words: hostN has more resources than host0
48 # which means ... don't go above 10 hosts.
49 return {'host_name-description': 'XenServer %s' % multiplier,
50 'host_hostname': 'xs-%s' % multiplier,
51 'host_memory_total': 100,
52 'host_memory_overhead': 10,
53 'host_memory_free': 10 + multiplier * 10,
54 'host_memory_free-computed': 10 + multiplier * 10,
55 'host_other-config': {},
56 'host_ip_address': '192.168.1.%d' % (100 + multiplier),
57 'host_cpu_info': {},
58 'disk_available': 100 + multiplier * 100,
59 'disk_total': 1000,
60 'disk_used': 0,
61 'host_uuid': 'xxx-%d' % multiplier,
62 'host_name-label': 'xs-%s' % multiplier}
63
64 def setUp(self):
65 self.old_flag = FLAGS.default_host_filter
66 FLAGS.default_host_filter = \
67 'nova.scheduler.filters.json_filter_integrity.AllHostsFilter'
68 self.instance_type = dict(name='tiny',
69 memory_mb=50,
70 vcpus=10,
71 local_gb=500,
72 flavorid=1,
73 swap=500,
74 rxtx_quota=30000,
75 rxtx_cap=200,
76 extra_specs={})
77
78 self.zone_manager = FakeZoneManager()
79 states = {}
80 now = utils.utcnow() - datetime.timedelta(0)
81 for x in xrange(10):
82 state={}
83 if x in [0, 2, 4, 6, 9]:
84 lvl = 'lvl10'
85 state= {'compute': self._host_caps(x),
86 'trust_state':{'trust_lvl':lvl, 'vtime':now}}
87 elif x in [1, 3, 5, 8]:
88 lvl = 'lvl01'
89 state= {'compute': self._host_caps(x),
90 'trust_state':{'trust_lvl':lvl, 'vtime':now}}
91 else:
92 lvl = 'inflight'
93 state= {'compute': self._host_caps(x)}
94 states['host%02d' % (x)] = state
95 self.zone_manager.service_states = states
96
97 def tearDown(self):
98 FLAGS.default_host_filter = self.old_flag
99
100 def dummy_reconfirm(self, zone_manager, hosts):
101 return hosts
102
103 def test_json_filter_integrity(self):
104 hf = json_filter_integrity.JsonFilterIntegrity()
105 #self.zone_manager.integrity_service_confirm=self.dummy_reconfirm
106 hf._trust_pool_reconfirm = self.dummy_reconfirm
107 # filter all hosts that can support 50 ram and 500 disk
108
109 name, cooked = hf.instance_type_to_filter(self.instance_type)
110 self.assertEquals('nova.scheduler.filters.json_filter_integrity.JsonFilterIntegrity',
111 name)
112 hosts = hf.filter_hosts(self.zone_manager, cooked)
113 self.assertEquals(2, len(hosts))
114 just_hosts = [host for host, caps in hosts]
115 just_hosts.sort()
116 self.assertEquals('host05', just_hosts[0])
117 self.assertEquals('host08', just_hosts[1])
118
119 raw=[]
120 cooked = json.dumps(raw)
121 hosts = hf.filter_hosts(self.zone_manager, cooked)
122 self.assertEquals(4, len(hosts))
123
124 raw= ['>=', '$compute.host_memory_free', 20]
125 cooked = json.dumps(raw)
126 hosts = hf.filter_hosts(self.zone_manager, cooked)
127 self.assertEquals(4, len(hosts))
128
129 raw= ['>=', '$trust_state.trust_lvl', 'trusted']
130 cooked = json.dumps(raw)
131 hosts = hf.filter_hosts(self.zone_manager, cooked)
132 self.assertEquals(5, len(hosts))
133
134 raw= ['<', '$trust_state.trust_lvl', 'untrusted']
135 cooked = json.dumps(raw)
136 try:
137 hf.filter_hosts(self.zone_manager, cooked)
138 self.fail("UsageError")
139 except exception.Error, e:
140 pass
141 # Try some custom queries
142
143 raw = [ 'or',
144 ['and',
145 ['<', '$compute.host_memory_free', 30],
146 ['<', '$compute.disk_available', 300],
147 ],
148 ['and',
149 ['>', '$compute.host_memory_free', 70],
150 ['>', '$compute.disk_available', 700],
151 ]
152 ]
153 cooked = json.dumps(raw)
154 hosts = hf.filter_hosts(self.zone_manager, cooked)
155 self.assertEquals(2, len(hosts))
156
157 raw = [ 'or',
158 ['and',
159 ['<', '$compute.host_memory_free', 30],
160 ['<', '$compute.disk_available', 300],
161 ],
162 ['and',
163 ['>', '$compute.host_memory_free', 60],
164 ['>', '$compute.disk_available', 600],
165 ['>=', '$trust_state.trust_lvl', 'trusted']
166 ]
167 ]
168 cooked = json.dumps(raw)
169 hosts = hf.filter_hosts(self.zone_manager, cooked)
170 self.assertEquals(4, len(hosts))
171
172 raw = ['=', '$trust_state.trust_lvl', 'trusted']
173
174 cooked = json.dumps(raw)
175 hosts = hf.filter_hosts(self.zone_manager, cooked)
176
177 self.assertEquals(5, len(hosts))
178 just_hosts = [host for host, caps in hosts]
179 just_hosts.sort()
180 for index, host in zip([0, 2, 4, 6, 9], just_hosts):
181 self.assertEquals('host%02d' % index, host)
182
183 raw = ['and',
184 ['>=', '$compute.host_memory_free', 30],
185 ]
186 cooked = json.dumps(raw)
187 hosts = hf.filter_hosts(self.zone_manager, cooked)
188
189 self.assertEquals(3, len(hosts))
190 just_hosts = [host for host, caps in hosts]
191 just_hosts.sort()
192 for index, host in zip([3, 5, 8], just_hosts):
193 self.assertEquals('host%02d' % index, host)
194
195 raw = ['and',
196 ['>=', '$compute.host_memory_free', 30],
197 ['=', '$trust_state.trust_lvl', 'Untrusted']
198 ]
199 cooked = json.dumps(raw)
200 hosts = hf.filter_hosts(self.zone_manager, cooked)
201
202 self.assertEquals(3, len(hosts))
203 just_hosts = [host for host, caps in hosts]
204 just_hosts.sort()
205 for index, host in zip([3, 5, 8], just_hosts):
206 self.assertEquals('host%02d' % index, host)
207
208 raw = ['and',
209 ['>=', '$trust_state.trust_lvl', 'trusted'],
210 ['>=', '$compute.host_memory_free', 30]
211 ]
212 cooked = json.dumps(raw)
213 hosts = hf.filter_hosts(self.zone_manager, cooked)
214
215 self.assertEquals(4, len(hosts))
216 just_hosts = [host for host, caps in hosts]
217 just_hosts.sort()
218 for index, host in zip([2, 4, 6, 9], just_hosts):
219 self.assertEquals('host%02d' % index, host)
220
221 # Try some bogus input ...
222 raw = ['unknown command', ]
223 cooked = json.dumps(raw)
224 try:
225 hf.filter_hosts(self.zone_manager, cooked)
226 self.fail("Should give KeyError")
227 except KeyError, e:
228 pass
229
230 self.assertTrue(hf.filter_hosts(self.zone_manager, json.dumps([])))
231 self.assertTrue(hf.filter_hosts(self.zone_manager, json.dumps({})))
232 self.assertTrue(hf.filter_hosts(self.zone_manager, json.dumps(
233 ['not', True, False, True, False])))
234
235 try:
236 hf.filter_hosts(self.zone_manager, json.dumps(
237 'not', True, False, True, False))
238 self.fail("Should give KeyError")
239 except KeyError, e:
240 pass
241
242 self.assertFalse(hf.filter_hosts(self.zone_manager,
243 json.dumps(['=', '$foo', 100])))
244 self.assertFalse(hf.filter_hosts(self.zone_manager,
245 json.dumps(['=', '$.....', 100])))
246 self.assertFalse(hf.filter_hosts(self.zone_manager,
247 json.dumps(
248 ['>', ['and', ['or', ['not', ['<', ['>=', ['<=', ['in', ]]]]]]]])))
249
250 self.assertFalse(hf.filter_hosts(self.zone_manager,
251 json.dumps(['=', {}, ['>', '$missing....foo']])))
0252
=== added file 'nova/tests/scheduler/test_manager_integrity.py'
--- nova/tests/scheduler/test_manager_integrity.py 1970-01-01 00:00:00 +0000
+++ nova/tests/scheduler/test_manager_integrity.py 2011-09-20 01:13:28 +0000
@@ -0,0 +1,202 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2010 United States Government as represented by the
4# Administrator of the National Aeronautics and Space Administration.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18"""
19Tests For Scheduler
20"""
21
22import time
23import datetime
24import mox
25import novaclient.exceptions
26import stubout
27import webob
28
29from mox import IgnoreArg
30from nova import context
31from nova import db
32from nova import exception
33from nova import flags
34from nova import service
35from nova import test
36from nova import rpc
37from nova import utils
38from nova.auth import manager as auth_manager
39from nova.scheduler import api
40from nova.scheduler import manager
41from nova.scheduler import manager_integrity
42from nova.scheduler import driver
43from nova.compute import power_state
44from nova.db.sqlalchemy import models
45from nova import log as logging
46
47
48FLAGS = flags.FLAGS
49
50class TestDriver(driver.Scheduler):
51 """Scheduler Driver for Tests"""
52 def schedule(context, topic, *args, **kwargs):
53 return 'fallback_host'
54
55 def schedule_named_method(context, topic, num):
56 return 'named_host'
57
58
59class SchedulerTestCase(test.TestCase):
60 """Test case for scheduler"""
61 def setUp(self):
62 super(SchedulerTestCase, self).setUp()
63 driver = 'nova.tests.scheduler.test_scheduler.TestDriver'
64 self.flags(scheduler_driver=driver)
65
66 self.old_interval_flag = FLAGS.report_interval
67 FLAGS.report_interval = 2
68 self.old_down_flag = FLAGS.service_down_time
69 FLAGS.service_down_time = 4
70
71
72 def tearDown(self):
73 self.stubs.UnsetAll()
74 FLAGS.report_interval = self.old_interval_flag
75 FLAGS.service_down_time = self.old_down_flag
76 super(SchedulerTestCase, self).tearDown()
77
78 def _create_ncompute_service(self, n):
79 """Create compute-manager(ComputeNode and Service record)."""
80 self.hosts=n
81 ctxt = context.get_admin_context()
82 now = utils.utcnow() - datetime.timedelta(0)
83 dic1 = {'host': 'dummy', 'binary': 'nova-compute', 'topic': 'compute',
84 'report_count': 0, 'updated_at':0, 'created_at':0,
85 'availability_zone': 'dummyzone'}
86
87 dic2 = {'service_id': 0,
88 'vcpus': 16, 'memory_mb': 32, 'local_gb': 100,
89 'vcpus_used': 16, 'memory_mb_used': 32, 'local_gb_used': 10,
90 'hypervisor_type': 'qemu', 'hypervisor_version': 12003,
91 'cpu_info': ''}
92
93 for idx in range(n):
94 dic1['host'] = 'host%02d' % idx
95 dic1['report_count'] = idx * 2
96 dic1['updated_at'] = now
97 dic1['created_at'] = now
98 s_ref = db.service_create(ctxt, dic1)
99 dic2['service_id'] = s_ref['id']
100 db.compute_node_create(ctxt, dic2)
101
102 def _create_integrity_db(self, n):
103 tlvl = {'trust_lvl':'trusted', 'vTime':0}
104 utc = utils.utcnow() - datetime.timedelta(0)
105 db = {}
106 for idx in range(n):
107 host = 'host%02d' % idx
108 if idx % 2:
109 lvl = 'lvl10'
110 else:
111 lvl = 'lvl00'
112 db[host] = {'trust_lvl':lvl, 'vTime':utc}
113 self.integrity_db = db
114
115 def _create_compute_service(self):
116 """Create compute-manager(ComputeNode and Service record)."""
117 ctxt = context.get_admin_context()
118 dic = {'host': 'dummy', 'binary': 'nova-compute', 'topic': 'compute',
119 'report_count': 0, 'availability_zone': 'dummyzone'}
120 s_ref = db.service_create(ctxt, dic)
121
122 dic = {'service_id': s_ref['id'],
123 'vcpus': 16, 'memory_mb': 32, 'local_gb': 100,
124 'vcpus_used': 16, 'memory_mb_used': 32, 'local_gb_used': 10,
125 'hypervisor_type': 'qemu', 'hypervisor_version': 12003,
126 'cpu_info': ''}
127 db.compute_node_create(ctxt, dic)
128
129 return db.service_get(ctxt, s_ref['id'])
130
131 def _create_db(self, ctxt, scheduler, n):
132 self._create_ncompute_service(n)
133
134 """
135 services=db.service_get_all_by_topic(ctxt, 'compute')
136 for service in services:
137 logging.debug(_("Host %s"), service.host)
138 logging.debug(_("Services %s "), service.report_count)
139 logging.debug(_("Updated %s "), service.updated_at)
140 """
141
142 def _do_attestation(self, cmd, hosts):
143 utc = utils.utcnow() - datetime.timedelta(0)
144 self.query = hosts
145 db = self.integrity_db
146 report = {}
147 for host in hosts:
148 lvl = db[host].get('trust_lvl', {})
149 txt = {'trust_lvl':lvl, 'vtime':utc}
150 report[host] = txt
151 return report
152
153 def _update_ticks(self, ctx, skips, count, dec, down=None):
154 now = utils.utcnow() - datetime.timedelta(0)
155 for idx in range(self.hosts):
156 if idx % skips:
157 d = 0
158 else:
159 if down:
160 continue
161 d = dec
162 h = 'host%02d' % idx
163 ref = db.service_get_by_args(ctx, h, 'nova-compute')
164 ref['updated_at'] = now
165 ref['report_count'] += count - d
166 db.service_update(ctx, ref['id'], ref)
167
168 def test_trust(self):
169 hosts = 10
170 scheduler = manager_integrity.SchedulerManager()
171 scheduler.setup_test_attestation(self._do_attestation)
172 ctx = context.get_admin_context()
173 self._create_db(ctx, scheduler, hosts)
174 self._create_integrity_db(hosts)
175 scheduler.periodic_tasks(ctx)
176 self.assertEqual(10, len(self.query))
177 time.sleep(FLAGS.report_interval)
178
179 self._update_ticks(ctx, 1, 1, 0)
180 scheduler.periodic_tasks(ctx)
181
182 time.sleep(FLAGS.service_down_time)
183 self._update_ticks(ctx, 3, 2, 2)
184 scheduler.periodic_tasks(ctx)
185 self.assertEqual(4, len(self.query))
186 time.sleep(FLAGS.service_down_time * 2)
187 down = 1
188 self._update_ticks(ctx, 3, 4, 4, down)
189 scheduler.periodic_tasks(ctx)
190 for idx in [0, 3, 6, 9]:
191 txt = scheduler.zone_manager.service_states.get("host%2d" % idx,{})
192 self.assertEqual(0, len(txt))
193 for idx in [1, 2, 4, 5, 7, 8]:
194 host = "host%02d" %idx
195 service = scheduler.zone_manager.service_states[host]
196 txt = service.get('trust_state', {})
197 lvl = txt.get('trust_lvl')
198 if idx % 2:
199 self.assertEqual(lvl, 'lvl10')
200 else:
201 self.assertEqual(lvl, 'lvl00')
202