Merge lp:~ewanmellor/nova/xenapi into lp:~hudson-openstack/nova/trunk
- xenapi
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Jay Pipes |
Approved revision: | 176 |
Merged at revision: | 240 |
Proposed branch: | lp:~ewanmellor/nova/xenapi |
Merge into: | lp:~hudson-openstack/nova/trunk |
Diff against target: |
274 lines (+142/-21) 5 files modified
doc/source/getting.started.rst (+1/-0) nova/virt/connection.py (+9/-0) nova/virt/fake.py (+128/-1) nova/virt/libvirt_conn.py (+0/-20) plugins/xenapi/README (+4/-0) |
To merge this branch: | bzr merge lp:~ewanmellor/nova/xenapi |
Related bugs: | |
Related blueprints: |
XenAPI support
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Soren Hansen (community) | Approve | ||
Jay Pipes (community) | Approve | ||
Review via email: mp+32582@code.launchpad.net |
Commit message
Added documentation for the nova.virt connection interface, a note about the need to chmod the objectstore script, and a reference for the XenAPI module.
Description of the change
Added documentation for the nova.virt connection interface, a note about the need to chmod the objectstore script, and a reference for the XenAPI module.
Soren Hansen (soren) wrote : | # |
Yay for more documentation.
Soren Hansen (soren) : | # |
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~ewanmellor/nova/xenapi into lp:nova failed.Below is the output from the failed tests.
nova.tests.
AccessTestCase
test_
test_
test_
test_
test_
nova.tests.
ApiEc2TestCase
test_
test_
nova.tests.
AuthTestCase
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
nova.tests.
CloudTestCase
test_
test_
test_
Jay Pipes (jaypipes) wrote : | # |
grr...
non-deterministic testing--
setting to approve again...this is just docs, shouldn't be any test failures. :(
Preview Diff
1 | === modified file 'doc/source/getting.started.rst' | |||
2 | --- doc/source/getting.started.rst 2010-07-25 01:06:22 +0000 | |||
3 | +++ doc/source/getting.started.rst 2010-08-13 13:39:41 +0000 | |||
4 | @@ -40,6 +40,7 @@ | |||
5 | 40 | 40 | ||
6 | 41 | * M2Crypto: python library interface for openssl | 41 | * M2Crypto: python library interface for openssl |
7 | 42 | * curl | 42 | * curl |
8 | 43 | * XenAPI: Needed only for Xen Cloud Platform or XenServer support. Available from http://wiki.xensource.com/xenwiki/XCP_SDK or http://community.citrix.com/cdn/xs/sdks. | ||
9 | 43 | 44 | ||
10 | 44 | Vendored python libaries (don't require any installation) | 45 | Vendored python libaries (don't require any installation) |
11 | 45 | 46 | ||
12 | 46 | 47 | ||
13 | === modified file 'nova/virt/connection.py' | |||
14 | --- nova/virt/connection.py 2010-07-18 17:28:21 +0000 | |||
15 | +++ nova/virt/connection.py 2010-08-13 13:39:41 +0000 | |||
16 | @@ -27,6 +27,15 @@ | |||
17 | 27 | 27 | ||
18 | 28 | 28 | ||
19 | 29 | def get_connection(read_only=False): | 29 | def get_connection(read_only=False): |
20 | 30 | """Returns an object representing the connection to a virtualization | ||
21 | 31 | platform. This could be nova.virt.fake.FakeConnection in test mode, | ||
22 | 32 | a connection to KVM or QEMU via libvirt, or a connection to XenServer | ||
23 | 33 | or Xen Cloud Platform via XenAPI. | ||
24 | 34 | |||
25 | 35 | Any object returned here must conform to the interface documented by | ||
26 | 36 | FakeConnection. | ||
27 | 37 | """ | ||
28 | 38 | |||
29 | 30 | # TODO(termie): maybe lazy load after initial check for permissions | 39 | # TODO(termie): maybe lazy load after initial check for permissions |
30 | 31 | # TODO(termie): check whether we can be disconnected | 40 | # TODO(termie): check whether we can be disconnected |
31 | 32 | t = FLAGS.connection_type | 41 | t = FLAGS.connection_type |
32 | 33 | 42 | ||
33 | === modified file 'nova/virt/fake.py' | |||
34 | --- nova/virt/fake.py 2010-07-18 17:15:12 +0000 | |||
35 | +++ nova/virt/fake.py 2010-08-13 13:39:41 +0000 | |||
36 | @@ -19,6 +19,7 @@ | |||
37 | 19 | 19 | ||
38 | 20 | """ | 20 | """ |
39 | 21 | A fake (in-memory) hypervisor+api. Allows nova testing w/o a hypervisor. | 21 | A fake (in-memory) hypervisor+api. Allows nova testing w/o a hypervisor. |
40 | 22 | This module also documents the semantics of real hypervisor connections. | ||
41 | 22 | """ | 23 | """ |
42 | 23 | 24 | ||
43 | 24 | import logging | 25 | import logging |
44 | @@ -32,6 +33,38 @@ | |||
45 | 32 | 33 | ||
46 | 33 | 34 | ||
47 | 34 | class FakeConnection(object): | 35 | class FakeConnection(object): |
48 | 36 | """ | ||
49 | 37 | The interface to this class talks in terms of 'instances' (Amazon EC2 and | ||
50 | 38 | internal Nova terminology), by which we mean 'running virtual machine' | ||
51 | 39 | (XenAPI terminology) or domain (Xen or libvirt terminology). | ||
52 | 40 | |||
53 | 41 | An instance has an ID, which is the identifier chosen by Nova to represent | ||
54 | 42 | the instance further up the stack. This is unfortunately also called a | ||
55 | 43 | 'name' elsewhere. As far as this layer is concerned, 'instance ID' and | ||
56 | 44 | 'instance name' are synonyms. | ||
57 | 45 | |||
58 | 46 | Note that the instance ID or name is not human-readable or | ||
59 | 47 | customer-controlled -- it's an internal ID chosen by Nova. At the | ||
60 | 48 | nova.virt layer, instances do not have human-readable names at all -- such | ||
61 | 49 | things are only known higher up the stack. | ||
62 | 50 | |||
63 | 51 | Most virtualization platforms will also have their own identity schemes, | ||
64 | 52 | to uniquely identify a VM or domain. These IDs must stay internal to the | ||
65 | 53 | platform-specific layer, and never escape the connection interface. The | ||
66 | 54 | platform-specific layer is responsible for keeping track of which instance | ||
67 | 55 | ID maps to which platform-specific ID, and vice versa. | ||
68 | 56 | |||
69 | 57 | In contrast, the list_disks and list_interfaces calls may return | ||
70 | 58 | platform-specific IDs. These identify a specific virtual disk or specific | ||
71 | 59 | virtual network interface, and these IDs are opaque to the rest of Nova. | ||
72 | 60 | |||
73 | 61 | Some methods here take an instance of nova.compute.service.Instance. This | ||
74 | 62 | is the datastructure used by nova.compute to store details regarding an | ||
75 | 63 | instance, and pass them into this layer. This layer is responsible for | ||
76 | 64 | translating that generic datastructure into terms that are specific to the | ||
77 | 65 | virtualization platform. | ||
78 | 66 | """ | ||
79 | 67 | |||
80 | 35 | def __init__(self): | 68 | def __init__(self): |
81 | 36 | self.instances = {} | 69 | self.instances = {} |
82 | 37 | 70 | ||
83 | @@ -42,20 +75,59 @@ | |||
84 | 42 | return cls._instance | 75 | return cls._instance |
85 | 43 | 76 | ||
86 | 44 | def list_instances(self): | 77 | def list_instances(self): |
87 | 78 | """ | ||
88 | 79 | Return the names of all the instances known to the virtualization | ||
89 | 80 | layer, as a list. | ||
90 | 81 | """ | ||
91 | 45 | return self.instances.keys() | 82 | return self.instances.keys() |
92 | 46 | 83 | ||
93 | 47 | def spawn(self, instance): | 84 | def spawn(self, instance): |
94 | 85 | """ | ||
95 | 86 | Create a new instance/VM/domain on the virtualization platform. | ||
96 | 87 | |||
97 | 88 | The given parameter is an instance of nova.compute.service.Instance. | ||
98 | 89 | This function should use the data there to guide the creation of | ||
99 | 90 | the new instance. | ||
100 | 91 | |||
101 | 92 | Once this function successfully completes, the instance should be | ||
102 | 93 | running (power_state.RUNNING). | ||
103 | 94 | |||
104 | 95 | If this function fails, any partial instance should be completely | ||
105 | 96 | cleaned up, and the virtualization platform should be in the state | ||
106 | 97 | that it was before this call began. | ||
107 | 98 | """ | ||
108 | 99 | |||
109 | 48 | fake_instance = FakeInstance() | 100 | fake_instance = FakeInstance() |
110 | 49 | self.instances[instance.name] = fake_instance | 101 | self.instances[instance.name] = fake_instance |
111 | 50 | fake_instance._state = power_state.RUNNING | 102 | fake_instance._state = power_state.RUNNING |
112 | 51 | 103 | ||
113 | 52 | def reboot(self, instance): | 104 | def reboot(self, instance): |
114 | 105 | """ | ||
115 | 106 | Reboot the specified instance. | ||
116 | 107 | |||
117 | 108 | The given parameter is an instance of nova.compute.service.Instance, | ||
118 | 109 | and so the instance is being specified as instance.name. | ||
119 | 110 | """ | ||
120 | 53 | pass | 111 | pass |
122 | 54 | 112 | ||
123 | 55 | def destroy(self, instance): | 113 | def destroy(self, instance): |
124 | 114 | """ | ||
125 | 115 | Destroy (shutdown and delete) the specified instance. | ||
126 | 116 | |||
127 | 117 | The given parameter is an instance of nova.compute.service.Instance, | ||
128 | 118 | and so the instance is being specified as instance.name. | ||
129 | 119 | """ | ||
130 | 56 | del self.instances[instance.name] | 120 | del self.instances[instance.name] |
131 | 57 | 121 | ||
132 | 58 | def get_info(self, instance_id): | 122 | def get_info(self, instance_id): |
133 | 123 | """ | ||
134 | 124 | Get a block of information about the given instance. This is returned | ||
135 | 125 | as a dictionary containing 'state': The power_state of the instance, | ||
136 | 126 | 'max_mem': The maximum memory for the instance, in KiB, 'mem': The | ||
137 | 127 | current memory the instance has, in KiB, 'num_cpu': The current number | ||
138 | 128 | of virtual CPUs the instance has, 'cpu_time': The total CPU time used | ||
139 | 129 | by the instance, in nanoseconds. | ||
140 | 130 | """ | ||
141 | 59 | i = self.instances[instance_id] | 131 | i = self.instances[instance_id] |
142 | 60 | return {'state': i._state, | 132 | return {'state': i._state, |
143 | 61 | 'max_mem': 0, | 133 | 'max_mem': 0, |
144 | @@ -64,15 +136,70 @@ | |||
145 | 64 | 'cpu_time': 0} | 136 | 'cpu_time': 0} |
146 | 65 | 137 | ||
147 | 66 | def list_disks(self, instance_id): | 138 | def list_disks(self, instance_id): |
148 | 139 | """ | ||
149 | 140 | Return the IDs of all the virtual disks attached to the specified | ||
150 | 141 | instance, as a list. These IDs are opaque to the caller (they are | ||
151 | 142 | only useful for giving back to this layer as a parameter to | ||
152 | 143 | disk_stats). These IDs only need to be unique for a given instance. | ||
153 | 144 | |||
154 | 145 | Note that this function takes an instance ID, not a | ||
155 | 146 | compute.service.Instance, so that it can be called by compute.monitor. | ||
156 | 147 | """ | ||
157 | 67 | return ['A_DISK'] | 148 | return ['A_DISK'] |
158 | 68 | 149 | ||
159 | 69 | def list_interfaces(self, instance_id): | 150 | def list_interfaces(self, instance_id): |
160 | 151 | """ | ||
161 | 152 | Return the IDs of all the virtual network interfaces attached to the | ||
162 | 153 | specified instance, as a list. These IDs are opaque to the caller | ||
163 | 154 | (they are only useful for giving back to this layer as a parameter to | ||
164 | 155 | interface_stats). These IDs only need to be unique for a given | ||
165 | 156 | instance. | ||
166 | 157 | |||
167 | 158 | Note that this function takes an instance ID, not a | ||
168 | 159 | compute.service.Instance, so that it can be called by compute.monitor. | ||
169 | 160 | """ | ||
170 | 70 | return ['A_VIF'] | 161 | return ['A_VIF'] |
171 | 71 | 162 | ||
172 | 72 | def block_stats(self, instance_id, disk_id): | 163 | def block_stats(self, instance_id, disk_id): |
173 | 164 | """ | ||
174 | 165 | Return performance counters associated with the given disk_id on the | ||
175 | 166 | given instance_id. These are returned as [rd_req, rd_bytes, wr_req, | ||
176 | 167 | wr_bytes, errs], where rd indicates read, wr indicates write, req is | ||
177 | 168 | the total number of I/O requests made, bytes is the total number of | ||
178 | 169 | bytes transferred, and errs is the number of requests held up due to a | ||
179 | 170 | full pipeline. | ||
180 | 171 | |||
181 | 172 | All counters are long integers. | ||
182 | 173 | |||
183 | 174 | This method is optional. On some platforms (e.g. XenAPI) performance | ||
184 | 175 | statistics can be retrieved directly in aggregate form, without Nova | ||
185 | 176 | having to do the aggregation. On those platforms, this method is | ||
186 | 177 | unused. | ||
187 | 178 | |||
188 | 179 | Note that this function takes an instance ID, not a | ||
189 | 180 | compute.service.Instance, so that it can be called by compute.monitor. | ||
190 | 181 | """ | ||
191 | 73 | return [0L, 0L, 0L, 0L, null] | 182 | return [0L, 0L, 0L, 0L, null] |
192 | 74 | 183 | ||
193 | 75 | def interface_stats(self, instance_id, iface_id): | 184 | def interface_stats(self, instance_id, iface_id): |
194 | 185 | """ | ||
195 | 186 | Return performance counters associated with the given iface_id on the | ||
196 | 187 | given instance_id. These are returned as [rx_bytes, rx_packets, | ||
197 | 188 | rx_errs, rx_drop, tx_bytes, tx_packets, tx_errs, tx_drop], where rx | ||
198 | 189 | indicates receive, tx indicates transmit, bytes and packets indicate | ||
199 | 190 | the total number of bytes or packets transferred, and errs and dropped | ||
200 | 191 | is the total number of packets failed / dropped. | ||
201 | 192 | |||
202 | 193 | All counters are long integers. | ||
203 | 194 | |||
204 | 195 | This method is optional. On some platforms (e.g. XenAPI) performance | ||
205 | 196 | statistics can be retrieved directly in aggregate form, without Nova | ||
206 | 197 | having to do the aggregation. On those platforms, this method is | ||
207 | 198 | unused. | ||
208 | 199 | |||
209 | 200 | Note that this function takes an instance ID, not a | ||
210 | 201 | compute.service.Instance, so that it can be called by compute.monitor. | ||
211 | 202 | """ | ||
212 | 76 | return [0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L] | 203 | return [0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L] |
213 | 77 | 204 | ||
214 | 78 | 205 | ||
215 | 79 | 206 | ||
216 | === modified file 'nova/virt/libvirt_conn.py' | |||
217 | --- nova/virt/libvirt_conn.py 2010-08-10 15:37:07 +0000 | |||
218 | +++ nova/virt/libvirt_conn.py 2010-08-13 13:39:41 +0000 | |||
219 | @@ -261,12 +261,6 @@ | |||
220 | 261 | 261 | ||
221 | 262 | 262 | ||
222 | 263 | def get_disks(self, instance_id): | 263 | def get_disks(self, instance_id): |
223 | 264 | """ | ||
224 | 265 | Note that this function takes an instance ID, not an Instance, so | ||
225 | 266 | that it can be called by monitor. | ||
226 | 267 | |||
227 | 268 | Returns a list of all block devices for this domain. | ||
228 | 269 | """ | ||
229 | 270 | domain = self._conn.lookupByName(instance_id) | 264 | domain = self._conn.lookupByName(instance_id) |
230 | 271 | # TODO(devcamcar): Replace libxml2 with etree. | 265 | # TODO(devcamcar): Replace libxml2 with etree. |
231 | 272 | xml = domain.XMLDesc(0) | 266 | xml = domain.XMLDesc(0) |
232 | @@ -304,12 +298,6 @@ | |||
233 | 304 | 298 | ||
234 | 305 | 299 | ||
235 | 306 | def get_interfaces(self, instance_id): | 300 | def get_interfaces(self, instance_id): |
236 | 307 | """ | ||
237 | 308 | Note that this function takes an instance ID, not an Instance, so | ||
238 | 309 | that it can be called by monitor. | ||
239 | 310 | |||
240 | 311 | Returns a list of all network interfaces for this instance. | ||
241 | 312 | """ | ||
242 | 313 | domain = self._conn.lookupByName(instance_id) | 301 | domain = self._conn.lookupByName(instance_id) |
243 | 314 | # TODO(devcamcar): Replace libxml2 with etree. | 302 | # TODO(devcamcar): Replace libxml2 with etree. |
244 | 315 | xml = domain.XMLDesc(0) | 303 | xml = domain.XMLDesc(0) |
245 | @@ -347,18 +335,10 @@ | |||
246 | 347 | 335 | ||
247 | 348 | 336 | ||
248 | 349 | def block_stats(self, instance_id, disk): | 337 | def block_stats(self, instance_id, disk): |
249 | 350 | """ | ||
250 | 351 | Note that this function takes an instance ID, not an Instance, so | ||
251 | 352 | that it can be called by monitor. | ||
252 | 353 | """ | ||
253 | 354 | domain = self._conn.lookupByName(instance_id) | 338 | domain = self._conn.lookupByName(instance_id) |
254 | 355 | return domain.blockStats(disk) | 339 | return domain.blockStats(disk) |
255 | 356 | 340 | ||
256 | 357 | 341 | ||
257 | 358 | def interface_stats(self, instance_id, interface): | 342 | def interface_stats(self, instance_id, interface): |
258 | 359 | """ | ||
259 | 360 | Note that this function takes an instance ID, not an Instance, so | ||
260 | 361 | that it can be called by monitor. | ||
261 | 362 | """ | ||
262 | 363 | domain = self._conn.lookupByName(instance_id) | 343 | domain = self._conn.lookupByName(instance_id) |
263 | 364 | return domain.interfaceStats(interface) | 344 | return domain.interfaceStats(interface) |
264 | 365 | 345 | ||
265 | === modified file 'plugins/xenapi/README' | |||
266 | --- plugins/xenapi/README 2010-08-02 23:52:06 +0000 | |||
267 | +++ plugins/xenapi/README 2010-08-13 13:39:41 +0000 | |||
268 | @@ -1,2 +1,6 @@ | |||
269 | 1 | This directory contains files that are required for the XenAPI support. They | 1 | This directory contains files that are required for the XenAPI support. They |
270 | 2 | should be installed in the XenServer / Xen Cloud Platform domain 0. | 2 | should be installed in the XenServer / Xen Cloud Platform domain 0. |
271 | 3 | |||
272 | 4 | Also, you need to | ||
273 | 5 | |||
274 | 6 | chmod u+x /etc/xapi.d/plugins/objectstore |
Documentation++