Merge "Boot from volume Server side infrastructure"

This commit is contained in:
Jenkins
2014-01-27 16:33:54 +00:00
committed by Gerrit Code Review
3 changed files with 163 additions and 21 deletions

View File

@@ -163,7 +163,8 @@ class ServersClient(AutoMarshallingHTTPClient):
user_data=None, metadata=None, accessIPv4=None,
accessIPv6=None, disk_config=None, networks=None,
admin_pass=None, key_name=None, config_drive=None,
scheduler_hints=None, requestslib_kwargs=None):
scheduler_hints=None, requestslib_kwargs=None,
block_device_mapping=None):
"""
@summary: Creates an instance of a server given the
provided parameters
@@ -178,6 +179,9 @@ class ServersClient(AutoMarshallingHTTPClient):
@param personality: A list of dictionaries for files to be
injected into the server.
@type personality: List
@param block_device_mapping: A list of dictionaries needed for boot
from volume feature
@type block_device_mapping: List
@param user_data: Config Init User data
@type user_data: String
@param accessIPv4: IPv4 address for the server.
@@ -195,8 +199,8 @@ class ServersClient(AutoMarshallingHTTPClient):
server_request_object = CreateServer(
name=name, flavor_ref=flavor_ref, image_ref=image_ref,
personality=personality, user_data=user_data,
metadata=metadata, accessIPv4=accessIPv4,
personality=personality, block_device_mapping=block_device_mapping,
user_data=user_data, metadata=metadata, accessIPv4=accessIPv4,
accessIPv6=accessIPv6, disk_config=disk_config, networks=networks,
admin_pass=admin_pass, key_name=key_name,
config_drive=config_drive, scheduler_hints=scheduler_hints)
@@ -313,8 +317,8 @@ class ServersClient(AutoMarshallingHTTPClient):
def rebuild(self, server_id, image_ref, name=None,
admin_pass=None, disk_config=None, metadata=None,
personality=None, user_data=None,
accessIPv4=None, accessIPv6=None,
personality=None, user_data=None, accessIPv4=None,
accessIPv6=None, block_device_mapping=None,
key_name=None, config_drive=None, requestslib_kwargs=None):
"""
@summary: Rebuilds the server
@@ -331,7 +335,9 @@ class ServersClient(AutoMarshallingHTTPClient):
@param metadata:A metadata key and value pair.
@type metadata: Dictionary
@param personality:The file path and file contents
@type personality: String
@type personality: List
@param block_device_mapping: Block device mapping for boot from volume
@type block_device_mapping: List
@param user_data: Config Init User data
@type user_data: String
@param accessIPv4:The IP version 4 address.
@@ -345,11 +351,14 @@ class ServersClient(AutoMarshallingHTTPClient):
url = '{base_url}/servers/{server_id}/action'.format(
base_url=self.url, server_id=server_id)
rebuild_request_object = Rebuild(name=name, image_ref=image_ref,
rebuild_request_object = Rebuild(
name=name,
image_ref=image_ref,
admin_pass=admin_pass,
disk_config=disk_config,
metadata=metadata,
personality=personality,
block_device_mapping=block_device_mapping,
user_data=user_data,
accessIPv4=accessIPv4,
accessIPv6=accessIPv6,

View File

@@ -26,9 +26,9 @@ class CreateServer(AutoMarshallingModel):
def __init__(self, name, image_ref, flavor_ref, admin_pass=None,
disk_config=None, metadata=None, personality=None,
user_data=None, accessIPv4=None, accessIPv6=None,
networks=None, key_name=None, config_drive=None,
scheduler_hints=None):
block_device_mapping=None, user_data=None, accessIPv4=None,
accessIPv6=None, networks=None, key_name=None,
config_drive=None, scheduler_hints=None):
super(CreateServer, self).__init__()
self.name = name
@@ -38,6 +38,7 @@ class CreateServer(AutoMarshallingModel):
self.admin_pass = admin_pass
self.metadata = metadata
self.personality = personality
self.block_device_mapping = block_device_mapping
self.user_data = user_data
self.accessIPv4 = accessIPv4
self.accessIPv6 = accessIPv6
@@ -57,6 +58,7 @@ class CreateServer(AutoMarshallingModel):
'accessIPv4': self.accessIPv4,
'accessIPv6': self.accessIPv6,
'personality': self.personality,
'block_device_mapping': self.block_device_mapping,
'user_data': self.user_data,
'networks': self.networks,
'key_name': self.key_name,
@@ -100,6 +102,11 @@ class CreateServer(AutoMarshallingModel):
personality_ele = ET.Element('personality')
personality_ele.append(Personality._obj_to_xml(self.personality))
element.append(personality_ele)
if self.block_device_mapping is not None:
block_device_ele = ET.Element('block_device_mapping')
block_device_ele.append(BlockDeviceMapping._obj_to_xml(
self.block_device_mapping))
element.append(block_device_ele)
if self.user_data is not None:
element.set('user_data', self.user_data)
if self.accessIPv4 is not None:
@@ -203,6 +210,49 @@ class Personality(AutoMarshallingModel):
return pers_element
class BlockDeviceMapping(AutoMarshallingModel):
"""
@summary: Block Device Mapping Request Object for Server
"""
ROOT_TAG = 'block_device_mapping'
def __init__(self, volume_id, delete_on_termination, device_name,
size=None, type=None):
super(BlockDeviceMapping, self).__init__()
self.volume_id = volume_id
self.delete_on_termination = delete_on_termination
self.device_name = device_name
self.size = size
self.type = type
@classmethod
def _obj_to_json(self):
body = {
'volume_id': self.volume_id,
'delete_on_termination': self.delete_on_termination,
'device_name': self.device_name,
'size': self.size,
'type': self.type
}
body = self._remove_empty_values(body)
return json.dumps({'block_device_mapping': body})
@classmethod
def _obj_to_xml(self, list_dicts):
for pers_dict in list_dicts:
pers_element = ET.Element('block_device_mapping')
pers_element.set('volume_id', pers_dict.get('volume_id'))
pers_element.set('delete_on_termination',
pers_dict.get('delete_on_termination'))
pers_element.set('device_name', pers_dict.get('device_name'))
if pers_dict.get('size') is not None:
pers_element.set('size', pers_dict.get('size'))
if pers_dict.get('type') is not None:
pers_element.set('type', pers_dict.get('type'))
return pers_element
class Rebuild(AutoMarshallingModel):
"""
@summary: Rebuild Request Object for Server
@@ -210,8 +260,9 @@ class Rebuild(AutoMarshallingModel):
def __init__(self, name, image_ref, admin_pass=None,
disk_config=None, metadata=None, personality=None,
user_data=None, accessIPv4=None, accessIPv6=None,
networks=None, key_name=None, config_drive=None):
block_device_mapping=None, user_data=None, accessIPv4=None,
accessIPv6=None, networks=None, key_name=None,
config_drive=None):
super(Rebuild, self).__init__()
self.name = name
self.image_ref = image_ref
@@ -219,6 +270,7 @@ class Rebuild(AutoMarshallingModel):
self.admin_pass = admin_pass
self.metadata = metadata
self.personality = personality
self.block_device_mapping = block_device_mapping
self.user_data = user_data
self.accessIPv4 = accessIPv4
self.accessIPv6 = accessIPv6
@@ -236,6 +288,7 @@ class Rebuild(AutoMarshallingModel):
'accessIPv4': self.accessIPv4,
'accessIPv6': self.accessIPv6,
'personality': self.personality,
'block_device_mapping': self.block_device_mapping,
'user_data': self.user_data,
'networks': self.networks,
'key_name': self.key_name,
@@ -273,6 +326,11 @@ class Rebuild(AutoMarshallingModel):
personality_ele = ET.Element('personality')
personality_ele.append(Personality._obj_to_xml(self.personality))
element.append(personality_ele)
if self.block_device_mapping is not None:
block_device_ele = ET.Element('block_device_mapping')
block_device_ele.append(BlockDeviceMapping._obj_to_xml(
self.block_device_mapping))
element.append(block_device_ele)
if self.user_data is not None:
element.set('user_data', self.user_data)
if self.accessIPv4 is not None:

View File

@@ -0,0 +1,75 @@
"""
Copyright 2013 Rackspace
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 unittest2 as unittest
import xml.etree.ElementTree as ET
from cloudcafe.compute.servers_api.models.requests import CreateServer
class CreateServerJSONDomainTest(object):
def test_block_device_mapping_json(self):
block = ('"block_device_mapping": [{"device_name": "vda", '
'"delete_on_termination": "0", '
'"volume_id": "sample_id"}]')
self.assertIn(block, self.server_request_json,
msg="Block Mapping was not found in serialized request")
class CreateServerXMLDomainTest(object):
def test_block_device_mapping_xml(self):
file_path = self.server_request_xml_child[0]._children[0]
self.assertEqual(file_path.attrib['device_name'], 'vda')
self.assertEqual(file_path.attrib['delete_on_termination'], '0')
self.assertEqual(file_path.attrib['volume_id'], 'sample_id')
class CreateServerObjectJSON(unittest.TestCase, CreateServerJSONDomainTest):
@classmethod
def setUpClass(cls):
block = [{"volume_id": "sample_id",
"delete_on_termination": "0", "device_name": "vda"}]
server_request_object = CreateServer(
name='cctestserver',
flavor_ref='2',
image_ref='sample-image-ref',
block_device_mapping=block)
cls.server_request_json = server_request_object.serialize('json')
class CreateServerObjectXML(unittest.TestCase, CreateServerXMLDomainTest):
@classmethod
def setUpClass(cls):
block = [{"volume_id": "sample_id",
"delete_on_termination": "0", "device_name": "vda"}]
server_request_object = CreateServer(
name='cctestserver',
flavor_ref='2',
image_ref='sample-image-ref',
block_device_mapping=block)
cls.server_request = server_request_object.serialize('xml')
root = ET.fromstring(cls.server_request)
cls.server_request_xml = root.attrib
cls.server_request_xml_child = root.getchildren()