[PATCH V2 0/6] template supports networks

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> V1 -> V2: update mockmodel and test case add 'networks' option for template get/create/update ShaoHe Feng (6): template supports networks: let template xml support more networks template supports networks: update API template supports networks: update controller and json schema template supports networks: update model template supports networks: update mockmodel template supports networks: update test case docs/API.md | 4 +++ src/kimchi/API.json | 14 ++++++++ src/kimchi/controller.py | 3 +- src/kimchi/mockmodel.py | 7 +++- src/kimchi/model.py | 6 ++++ src/kimchi/osinfo.py | 5 +-- src/kimchi/vmtemplate.py | 20 ++++++++--- tests/test_model.py | 87 +++++++++++++++++++++++++++++++++++++----------- tests/test_rest.py | 68 +++++++++++++++++++++++++++++++++++++ 9 files changed, 187 insertions(+), 27 deletions(-) -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> If a VM just in one network, it may not access other hosts in different networks. In most product environment, more networks are needed. such as a management network, this network just let a service can manage all the VMs in this this network. And a more publick network, a VM can access the internet. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/osinfo.py | 5 +++-- src/kimchi/vmtemplate.py | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/kimchi/osinfo.py b/src/kimchi/osinfo.py index 20a93a6..3e980d6 100644 --- a/src/kimchi/osinfo.py +++ b/src/kimchi/osinfo.py @@ -170,8 +170,9 @@ isolinks = { }, } -defaults = {'network': 'default', 'storagepool': '/storagepools/default', - 'domain': 'kvm', 'arch': os.uname()[4] +defaults = {'networks': ['default'], + 'storagepool': '/storagepools/default', + 'domain': 'kvm', 'arch': os.uname()[4] } def lookup(distro, version): diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index dd43faa..5d31f2a 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -182,11 +182,26 @@ class VMTemplate(object): ret.append(info) return ret + def _get_networks_xml(self): + network = """ + <interface type='network'> + <source network='%(network)s'/> + <model type='%(nic_model)s'/> + </interface> + """ + networks = "" + net_info = {"nic_model": self.info['nic_model']} + for nw in self.info['networks']: + net_info['network'] = nw + networks += network % net_info + return networks + def to_vm_xml(self, vm_name, vm_uuid, libvirt_stream = False, qemu_stream_dns = False): params = dict(self.info) params['name'] = vm_name params['uuid'] = vm_uuid params['disks'] = self._get_disks_xml(vm_uuid) + params['networks'] = self._get_networks_xml() params['qemu-namespace'] = '' params['cdroms'] = '' params['qemu-stream-cmdline'] = '' @@ -220,10 +235,7 @@ class VMTemplate(object): <devices> %(disks)s %(cdroms)s - <interface type='network'> - <source network='%(network)s'/> - <model type='%(nic_model)s'/> - </interface> + %(networks)s <graphics type='vnc' /> <sound model='ich6' /> <memballoon model='virtio' /> -- 1.8.4.2

On 12/27/2013 07:21 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
If a VM just in one network, it may not access other hosts in different networks.
typo: VM is just s/hosts/machines
In most product environment, more networks are needed.
such as a management network, this network just let a service can manage all the
network management
VMs in this this network.
typo: this this
And a more publick network, a VM can access the internet.
typo: publick
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/osinfo.py | 5 +++-- src/kimchi/vmtemplate.py | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/src/kimchi/osinfo.py b/src/kimchi/osinfo.py index 20a93a6..3e980d6 100644 --- a/src/kimchi/osinfo.py +++ b/src/kimchi/osinfo.py @@ -170,8 +170,9 @@ isolinks = { }, }
-defaults = {'network': 'default', 'storagepool': '/storagepools/default', - 'domain': 'kvm', 'arch': os.uname()[4] +defaults = {'networks': ['default'], + 'storagepool': '/storagepools/default', + 'domain': 'kvm', 'arch': os.uname()[4] }
def lookup(distro, version): diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index dd43faa..5d31f2a 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -182,11 +182,26 @@ class VMTemplate(object): ret.append(info) return ret
+ def _get_networks_xml(self): + network = """ + <interface type='network'> + <source network='%(network)s'/> + <model type='%(nic_model)s'/> + </interface> + """ + networks = "" + net_info = {"nic_model": self.info['nic_model']} + for nw in self.info['networks']: + net_info['network'] = nw + networks += network % net_info + return networks + def to_vm_xml(self, vm_name, vm_uuid, libvirt_stream = False, qemu_stream_dns = False): params = dict(self.info) params['name'] = vm_name params['uuid'] = vm_uuid params['disks'] = self._get_disks_xml(vm_uuid) + params['networks'] = self._get_networks_xml() params['qemu-namespace'] = '' params['cdroms'] = '' params['qemu-stream-cmdline'] = '' @@ -220,10 +235,7 @@ class VMTemplate(object): <devices> %(disks)s %(cdroms)s - <interface type='network'> - <source network='%(network)s'/> - <model type='%(nic_model)s'/> - </interface> + %(networks)s <graphics type='vnc' /> <sound model='ich6' /> <memballoon model='virtio' />

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> add 'networks' option for template get/create/update Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- docs/API.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/API.md b/docs/API.md index 9edc551..a2b35bb 100644 --- a/docs/API.md +++ b/docs/API.md @@ -120,6 +120,8 @@ Represents a snapshot of the Virtual Machine's primary monitor. * cdrom *(required)*: A volume name or URI to an ISO image. * storagepool *(optional)*: URI of the storagepool. Default is '/storagepools/default' + * networks *(optional)*: name list of which networks will be assigned to the a new VM. + Default is '[default]' * disks *(optional)*: An array of requested disks with the following optional fields (either *size* or *volume* must be specified): * index: The device index @@ -144,6 +146,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. * memory: The amount of memory assigned to the VM * cdrom: A volume name or URI to an ISO image * storagepool: URI of the storagepool where template allocates vm storage. + * networks *(optional)*: name list of which networks will be assigned to the a new VM. * disks: An array of requested disks with the following optional fields (either *size* or *volume* must be specified): * index: The device index @@ -162,6 +165,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. * memory: The amount of memory assigned to the VM * cdrom: A volume name or URI to an ISO image * storagepool: URI of the storagepool where template allocates vm storage. + * networks *(optional)*: name list of which networks will be assigned to the a new VM. * disks: An array of requested disks with the following optional fields (either *size* or *volume* must be specified): * index: The device index -- 1.8.4.2

On 12/27/2013 07:21 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
add 'networks' option for template get/create/update
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- docs/API.md | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/docs/API.md b/docs/API.md index 9edc551..a2b35bb 100644 --- a/docs/API.md +++ b/docs/API.md @@ -120,6 +120,8 @@ Represents a snapshot of the Virtual Machine's primary monitor. * cdrom *(required)*: A volume name or URI to an ISO image. * storagepool *(optional)*: URI of the storagepool. Default is '/storagepools/default' + * networks *(optional)*: name list of which networks will be assigned to the a new VM.
s/name list/list
+ Default is '[default]' * disks *(optional)*: An array of requested disks with the following optional fields (either *size* or *volume* must be specified): * index: The device index @@ -144,6 +146,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. * memory: The amount of memory assigned to the VM * cdrom: A volume name or URI to an ISO image * storagepool: URI of the storagepool where template allocates vm storage. + * networks *(optional)*: name list of which networks will be assigned to the a new VM.
s/name list/list
* disks: An array of requested disks with the following optional fields (either *size* or *volume* must be specified): * index: The device index @@ -162,6 +165,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. * memory: The amount of memory assigned to the VM * cdrom: A volume name or URI to an ISO image * storagepool: URI of the storagepool where template allocates vm storage. + * networks *(optional)*: name list of which networks will be assigned to the a new VM.
s/name list/list
* disks: An array of requested disks with the following optional fields (either *size* or *volume* must be specified): * index: The device index

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> for json schema, verify the 'networks' option when create template or update template. for controller, add 'networks' attribute when GET template. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/API.json | 14 ++++++++++++++ src/kimchi/controller.py | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 7b90826..6f72d13 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -125,6 +125,13 @@ "type": "string", "pattern": "^/storagepools/[^/]+/?$" }, + "networks": { + "description": "name list of which networks will be assigned to the a new VM.", + "type": "array", + "minItems": 1, + "items": { "type": "string" }, + "uniqueItems": true + }, "folder": { "description": "Folder", "type": "array", @@ -198,6 +205,13 @@ "type": "string", "pattern": "^/storagepools/[^/]+/?$" }, + "networks": { + "description": "name list of which networks will be assigned to the a new VM.", + "type": "array", + "minItems": 1, + "items": { "type": "string" }, + "uniqueItems": true + }, "folder": { "description": "Folder", "type": "array", diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..4a88a2d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -406,7 +406,7 @@ class Template(Resource): super(Template, self).__init__(model, ident) self.update_params = ["name", "folder", "icon", "os_distro", "storagepool", "os_version", "cpus", - "memory", "cdrom", "disks"] + "memory", "cdrom", "disks", "networks"] self.uri_fmt = "/templates/%s" @property @@ -420,6 +420,7 @@ class Template(Resource): 'cdrom': self.info['cdrom'], 'disks': self.info['disks'], 'storagepool': self.info['storagepool'], + 'networks': self.info['networks'], 'folder': self.info.get('folder', [])} -- 1.8.4.2

On 12/27/2013 07:21 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
for json schema, verify the 'networks' option when create template or update template.
when creating or updating
for controller, add 'networks' attribute when GET template.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/API.json | 14 ++++++++++++++ src/kimchi/controller.py | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 7b90826..6f72d13 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -125,6 +125,13 @@ "type": "string", "pattern": "^/storagepools/[^/]+/?$" }, + "networks": { + "description": "name list of which networks will be assigned to the a new VM.",
s/name list/list
+ "type": "array", + "minItems": 1,
Can't I create a VM without network?
+ "items": { "type": "string" }, + "uniqueItems": true + }, "folder": { "description": "Folder", "type": "array", @@ -198,6 +205,13 @@ "type": "string", "pattern": "^/storagepools/[^/]+/?$" }, + "networks": {
+ "description": "name list of which networks will be assigned to the a new VM.", + "type": "array", + "minItems": 1,
Same as I commented above
+ "items": { "type": "string" }, + "uniqueItems": true + }, "folder": { "description": "Folder", "type": "array", diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..4a88a2d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -406,7 +406,7 @@ class Template(Resource): super(Template, self).__init__(model, ident) self.update_params = ["name", "folder", "icon", "os_distro", "storagepool", "os_version", "cpus", - "memory", "cdrom", "disks"] + "memory", "cdrom", "disks", "networks"] self.uri_fmt = "/templates/%s"
@property @@ -420,6 +420,7 @@ class Template(Resource): 'cdrom': self.info['cdrom'], 'disks': self.info['disks'], 'storagepool': self.info['storagepool'], + 'networks': self.info['networks'], 'folder': self.info.get('folder', [])}

On 12/30/2013 07:18 PM, Aline Manera wrote:
On 12/27/2013 07:21 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
for json schema, verify the 'networks' option when create template or update template.
when creating or updating
for controller, add 'networks' attribute when GET template.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/API.json | 14 ++++++++++++++ src/kimchi/controller.py | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 7b90826..6f72d13 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -125,6 +125,13 @@ "type": "string", "pattern": "^/storagepools/[^/]+/?$" }, + "networks": { + "description": "name list of which networks will be assigned to the a new VM.",
s/name list/list
+ "type": "array", + "minItems": 1,
Can't I create a VM without network? if we support create a VM without network, then I will remove the "minItems" option.
+ "items": { "type": "string" }, + "uniqueItems": true + }, "folder": { "description": "Folder", "type": "array", @@ -198,6 +205,13 @@ "type": "string", "pattern": "^/storagepools/[^/]+/?$" }, + "networks": {
+ "description": "name list of which networks will be assigned to the a new VM.", + "type": "array", + "minItems": 1,
Same as I commented above
+ "items": { "type": "string" }, + "uniqueItems": true + }, "folder": { "description": "Folder", "type": "array", diff --git a/src/kimchi/controller.py b/src/kimchi/controller.py index 2940278..4a88a2d 100644 --- a/src/kimchi/controller.py +++ b/src/kimchi/controller.py @@ -406,7 +406,7 @@ class Template(Resource): super(Template, self).__init__(model, ident) self.update_params = ["name", "folder", "icon", "os_distro", "storagepool", "os_version", "cpus", - "memory", "cdrom", "disks"] + "memory", "cdrom", "disks", "networks"] self.uri_fmt = "/templates/%s"
@property @@ -420,6 +420,7 @@ class Template(Resource): 'cdrom': self.info['cdrom'], 'disks': self.info['disks'], 'storagepool': self.info['storagepool'], + 'networks': self.info['networks'], 'folder': self.info.get('folder', [])}
-- Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> check all networks exist Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/model.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 1a547ec..ea28fec 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -678,6 +678,9 @@ class Model(object): def templates_create(self, params): name = params['name'] + for net_name in params.get(u'networks', []): + self._get_network(net_name) + with self.objstore as session: if name in session.get_list('template'): raise InvalidOperation("Template already exists") @@ -698,6 +701,9 @@ class Model(object): except Exception as e: raise InvalidParameter("Storagepool specified is not valid: %s." % e.message) + for net_name in params.get(u'networks', []): + self._get_network(net_name) + self.template_delete(name) try: ident = self.templates_create(new_t) -- 1.8.4.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 12/27/2013 07:21 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
check all networks exist
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/model.py | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 1a547ec..ea28fec 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -678,6 +678,9 @@ class Model(object):
def templates_create(self, params): name = params['name'] + for net_name in params.get(u'networks', []): + self._get_network(net_name) + with self.objstore as session: if name in session.get_list('template'): raise InvalidOperation("Template already exists") @@ -698,6 +701,9 @@ class Model(object): except Exception as e: raise InvalidParameter("Storagepool specified is not valid: %s." % e.message)
+ for net_name in params.get(u'networks', []): + self._get_network(net_name) + self.template_delete(name) try: ident = self.templates_create(new_t)

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> check all networks exist Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 348127a..8344bd3 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -200,6 +200,8 @@ class MockModel(object): name = params['name'] if name in self._mock_templates: raise InvalidOperation("Template already exists") + for net_name in params.get(u'networks', []): + self._get_network(net_name) t = MockVMTemplate(params, self) self._mock_templates[name] = t return name @@ -217,6 +219,9 @@ class MockModel(object): except Exception as e: raise InvalidParameter("Storagepool specified is not valid: %s." % e.message) + for net_name in params.get(u'networks', []): + self._get_network(net_name) + self.template_delete(name) try: ident = self.templates_create(new_t) @@ -457,7 +462,7 @@ class MockModel(object): try: return self._mock_networks[name] except KeyError: - raise NotFoundError() + raise NotFoundError("Network '%s'" % name) def network_lookup(self, name): network = self._get_network(name) -- 1.8.4.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 12/27/2013 07:21 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
check all networks exist
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 348127a..8344bd3 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -200,6 +200,8 @@ class MockModel(object): name = params['name'] if name in self._mock_templates: raise InvalidOperation("Template already exists") + for net_name in params.get(u'networks', []): + self._get_network(net_name) t = MockVMTemplate(params, self) self._mock_templates[name] = t return name @@ -217,6 +219,9 @@ class MockModel(object): except Exception as e: raise InvalidParameter("Storagepool specified is not valid: %s." % e.message)
+ for net_name in params.get(u'networks', []): + self._get_network(net_name) + self.template_delete(name) try: ident = self.templates_create(new_t) @@ -457,7 +462,7 @@ class MockModel(object): try: return self._mock_networks[name] except KeyError: - raise NotFoundError() + raise NotFoundError("Network '%s'" % name)
def network_lookup(self, name): network = self._get_network(name)

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> update test_rest and test_model Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- tests/test_model.py | 87 +++++++++++++++++++++++++++++++++++++++++------------ tests/test_rest.py | 68 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 19 deletions(-) diff --git a/tests/test_model.py b/tests/test_model.py index fb7d6dd..d1e516e 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -253,8 +253,10 @@ class ModelTests(unittest.TestCase): disk_path = '/tmp/kimchi-images/%s-0.img' % vm_info['uuid'] self.assertTrue(os.access(disk_path, os.F_OK)) + @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_create(self): - inst = kimchi.model.Model('test:///default', objstore_loc=self.tmp_store) + inst = kimchi.model.Model('qemu:///system', + objstore_loc=self.tmp_store) # Test non-exist path raises InvalidParameter params = {'name': 'test', 'cdrom': '/non-exsitent.iso'} @@ -264,27 +266,74 @@ class ModelTests(unittest.TestCase): params['cdrom'] = os.path.abspath(__file__) self.assertRaises(InvalidParameter, inst.templates_create, params) - @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') - def test_template_update(self): - inst = kimchi.model.Model('qemu:///system', objstore_loc=self.tmp_store) - - orig_params = {'name': 'test', 'memory': '1024', 'cpus': '1'} - inst.templates_create(orig_params) + with utils.RollbackContext() as rollback: + net_name = 'test-network' + net_args = {'name': net_name, + 'connection': 'nat', + 'subnet': '127.0.100.0/24'} + inst.networks_create(net_args) + rollback.prependDefer(inst.network_delete, net_name) + + params = {'name': 'test', 'memory': '1024', 'cpus': '1'} + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, 'test') + info = inst.template_lookup('test') + for key in params.keys(): + self.assertEquals(params[key], info[key]) + self.assertEquals("default", info["networks"][0]) - params = {'name': 'new-test'} - self.assertEquals('new-test', inst.template_update('test', params)) + # create template with with non-existent network + params['name'] = 'new-test' + params['networks'] = ["no-exist"] + self.assertRaises(NotFoundError, inst.templates_create, params) - params = {'name': 'new-test', 'memory': '512', 'cpus': '2'} - inst.template_update('new-test', params) - info = inst.template_lookup('new-test') - for key in params.keys(): - self.assertEquals(params[key], info[key]) + params['networks'] = ['default', 'test-network'] + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, params['name']) + info = inst.template_lookup(params['name']) + for key in params.keys(): + self.assertEquals(params[key], info[key]) - params = {'name': 'new-test', 'memory': 1024, 'cpus': 1} - inst.template_update('new-test', params) - info = inst.template_lookup('new-test') - for key in params.keys(): - self.assertEquals(params[key], info[key]) + @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') + def test_template_update(self): + inst = kimchi.model.Model('qemu:///system', + objstore_loc=self.tmp_store) + with utils.RollbackContext() as rollback: + net_name = 'test-network' + net_args = {'name': net_name, + 'connection': 'nat', + 'subnet': '127.0.100.0/24'} + inst.networks_create(net_args) + rollback.prependDefer(inst.network_delete, net_name) + + orig_params = {'name': 'test', 'memory': '1024', 'cpus': '1'} + inst.templates_create(orig_params) + + params = {'name': 'new-test'} + self.assertEquals('new-test', inst.template_update('test', params)) + self.assertRaises(NotFoundError, inst.template_delete, + 'test', params) + + params = {'name': 'new-test', 'memory': '512', 'cpus': '2'} + inst.template_update('new-test', params) + info = inst.template_lookup('new-test') + for key in params.keys(): + self.assertEquals(params[key], info[key]) + self.assertEquals("default", info["networks"][0]) + + params = {'name': 'new-test', 'memory': 1024, 'cpus': 1, + 'networks': ['default', 'test-network']} + inst.template_update('new-test', params) + info = inst.template_lookup('new-test') + for key in params.keys(): + self.assertEquals(params[key], info[key]) + + # test update with with non-existent network + params = {'networks': ["no-exist"]} + self.assertRaises(NotFoundError, inst.template_update, + 'new-test', params) + + inst.template_delete('new-test') def test_vm_edit(self): inst = kimchi.model.Model('qemu:///system', objstore_loc=self.tmp_store) diff --git a/tests/test_rest.py b/tests/test_rest.py index f597796..3602aae 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -345,6 +345,74 @@ class RestTests(unittest.TestCase): # Verify the volume was deleted self.assertHTTPStatus(404, vol_uri) + def test_template_customise_network(self): + tmpl = {'name': 'test', 'cdrom': '/nonexistent.iso', + 'disks': [{'size': 1}]} + req = json.dumps(tmpl) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + tmpl_res = json.loads(resp.read()) + self.assertTrue(type(tmpl_res['networks']) is list) + self.assertEquals("default", tmpl_res['networks'][0]) + + tmpl['name'] = "failed_tmpl" + # Create a Template with non-array network fails with 400 + tmpl['networks'] = "test-network" + req = json.dumps(tmpl) + resp = self.request('/templates', req, 'POST') + self.assertEquals(400, resp.status) + + # Create a Template with empty-array network fails with 400 + tmpl['networks'] = [] + req = json.dumps(tmpl) + resp = self.request('/templates', req, 'POST') + self.assertEquals(400, resp.status) + + # Create a Template with non-existent network fails with 404 + tmpl['networks'] = ["test-network"] + req = json.dumps(tmpl) + resp = self.request('/templates', req, 'POST') + self.assertEquals(404, resp.status) + + # Create a network + req = json.dumps({'name': 'test-network', + 'connection': 'nat', + 'net': '127.0.1.0/24'}) + resp = request(host, port, '/networks', req, 'POST') + self.assertEquals(201, resp.status) + + tmpl['name'] = "test" + # Update a Template with non-array network fails with 400 + tmpl['networks'] = "bad-network" + req = json.dumps(tmpl) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(400, resp.status) + + # Update a Template with empty-array network fails with 400 + tmpl['networks'] = [] + req = json.dumps(tmpl) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(400, resp.status) + + # Update a Template with non-existent network fails with 404 + tmpl['networks'] = ["bad-network"] + req = json.dumps(tmpl) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(404, resp.status) + + # Update a Template with existent network, successful + tmpl['networks'] = ["default", "test-network"] + req = json.dumps(tmpl) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(200, resp.status) + tmpl_res = json.loads(resp.read()) + self.assertTrue(type(tmpl_res['networks']) is list) + self.assertEquals(tmpl['networks'], tmpl_res['networks']) + + # Delete the network + resp = request(host, port, '/networks/test-network', '{}', 'DELETE') + self.assertEquals(204, resp.status) + def test_unnamed_vms(self): # Create a Template req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) -- 1.8.4.2

On 12/27/2013 07:21 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
update test_rest and test_model
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- tests/test_model.py | 87 +++++++++++++++++++++++++++++++++++++++++------------ tests/test_rest.py | 68 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 19 deletions(-)
diff --git a/tests/test_model.py b/tests/test_model.py index fb7d6dd..d1e516e 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -253,8 +253,10 @@ class ModelTests(unittest.TestCase): disk_path = '/tmp/kimchi-images/%s-0.img' % vm_info['uuid'] self.assertTrue(os.access(disk_path, os.F_OK))
+ @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_template_create(self): - inst = kimchi.model.Model('test:///default', objstore_loc=self.tmp_store) + inst = kimchi.model.Model('qemu:///system', + objstore_loc=self.tmp_store) # Test non-exist path raises InvalidParameter params = {'name': 'test', 'cdrom': '/non-exsitent.iso'} @@ -264,27 +266,74 @@ class ModelTests(unittest.TestCase): params['cdrom'] = os.path.abspath(__file__) self.assertRaises(InvalidParameter, inst.templates_create, params)
- @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') - def test_template_update(self): - inst = kimchi.model.Model('qemu:///system', objstore_loc=self.tmp_store) - - orig_params = {'name': 'test', 'memory': '1024', 'cpus': '1'} - inst.templates_create(orig_params) + with utils.RollbackContext() as rollback: + net_name = 'test-network' + net_args = {'name': net_name, + 'connection': 'nat', + 'subnet': '127.0.100.0/24'} + inst.networks_create(net_args) + rollback.prependDefer(inst.network_delete, net_name) + + params = {'name': 'test', 'memory': '1024', 'cpus': '1'}
memory and cpus should be integer, right?
+ inst.templates_create(params) + rollback.prependDefer(inst.template_delete, 'test') + info = inst.template_lookup('test') + for key in params.keys(): + self.assertEquals(params[key], info[key]) + self.assertEquals("default", info["networks"][0])
- params = {'name': 'new-test'} - self.assertEquals('new-test', inst.template_update('test', params)) + # create template with with non-existent network + params['name'] = 'new-test' + params['networks'] = ["no-exist"] + self.assertRaises(NotFoundError, inst.templates_create, params)
- params = {'name': 'new-test', 'memory': '512', 'cpus': '2'} - inst.template_update('new-test', params) - info = inst.template_lookup('new-test') - for key in params.keys(): - self.assertEquals(params[key], info[key]) + params['networks'] = ['default', 'test-network'] + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, params['name']) + info = inst.template_lookup(params['name']) + for key in params.keys(): + self.assertEquals(params[key], info[key])
- params = {'name': 'new-test', 'memory': 1024, 'cpus': 1} - inst.template_update('new-test', params) - info = inst.template_lookup('new-test') - for key in params.keys(): - self.assertEquals(params[key], info[key]) + @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') + def test_template_update(self): + inst = kimchi.model.Model('qemu:///system', + objstore_loc=self.tmp_store) + with utils.RollbackContext() as rollback: + net_name = 'test-network' + net_args = {'name': net_name, + 'connection': 'nat', + 'subnet': '127.0.100.0/24'} + inst.networks_create(net_args) + rollback.prependDefer(inst.network_delete, net_name) + + orig_params = {'name': 'test', 'memory': '1024', 'cpus': '1'}
Same here.
+ inst.templates_create(orig_params) + + params = {'name': 'new-test'} + self.assertEquals('new-test', inst.template_update('test', params)) + self.assertRaises(NotFoundError, inst.template_delete, + 'test', params) + + params = {'name': 'new-test', 'memory': '512', 'cpus': '2'}
And here
+ inst.template_update('new-test', params) + info = inst.template_lookup('new-test') + for key in params.keys(): + self.assertEquals(params[key], info[key]) + self.assertEquals("default", info["networks"][0]) + + params = {'name': 'new-test', 'memory': 1024, 'cpus': 1,
And here
+ 'networks': ['default', 'test-network']} + inst.template_update('new-test', params) + info = inst.template_lookup('new-test') + for key in params.keys(): + self.assertEquals(params[key], info[key]) + + # test update with with non-existent network + params = {'networks': ["no-exist"]} + self.assertRaises(NotFoundError, inst.template_update, + 'new-test', params) + + inst.template_delete('new-test')
def test_vm_edit(self): inst = kimchi.model.Model('qemu:///system', objstore_loc=self.tmp_store) diff --git a/tests/test_rest.py b/tests/test_rest.py index f597796..3602aae 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -345,6 +345,74 @@ class RestTests(unittest.TestCase): # Verify the volume was deleted self.assertHTTPStatus(404, vol_uri)
+ def test_template_customise_network(self): + tmpl = {'name': 'test', 'cdrom': '/nonexistent.iso', + 'disks': [{'size': 1}]} + req = json.dumps(tmpl) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + tmpl_res = json.loads(resp.read()) + self.assertTrue(type(tmpl_res['networks']) is list) + self.assertEquals("default", tmpl_res['networks'][0]) + + tmpl['name'] = "failed_tmpl" + # Create a Template with non-array network fails with 400 + tmpl['networks'] = "test-network" + req = json.dumps(tmpl) + resp = self.request('/templates', req, 'POST') + self.assertEquals(400, resp.status) + + # Create a Template with empty-array network fails with 400 + tmpl['networks'] = [] + req = json.dumps(tmpl) + resp = self.request('/templates', req, 'POST') + self.assertEquals(400, resp.status) + + # Create a Template with non-existent network fails with 404 + tmpl['networks'] = ["test-network"] + req = json.dumps(tmpl) + resp = self.request('/templates', req, 'POST') + self.assertEquals(404, resp.status) + + # Create a network + req = json.dumps({'name': 'test-network', + 'connection': 'nat', + 'net': '127.0.1.0/24'}) + resp = request(host, port, '/networks', req, 'POST') + self.assertEquals(201, resp.status) + + tmpl['name'] = "test" + # Update a Template with non-array network fails with 400 + tmpl['networks'] = "bad-network" + req = json.dumps(tmpl) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(400, resp.status) + + # Update a Template with empty-array network fails with 400 + tmpl['networks'] = [] + req = json.dumps(tmpl) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(400, resp.status) + + # Update a Template with non-existent network fails with 404 + tmpl['networks'] = ["bad-network"] + req = json.dumps(tmpl) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(404, resp.status) + + # Update a Template with existent network, successful + tmpl['networks'] = ["default", "test-network"] + req = json.dumps(tmpl) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(200, resp.status) + tmpl_res = json.loads(resp.read()) + self.assertTrue(type(tmpl_res['networks']) is list) + self.assertEquals(tmpl['networks'], tmpl_res['networks']) + + # Delete the network + resp = request(host, port, '/networks/test-network', '{}', 'DELETE') + self.assertEquals(204, resp.status) + def test_unnamed_vms(self): # Create a Template req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'})
participants (3)
-
Aline Manera
-
shaohef@linux.vnet.ibm.com
-
Sheldon