[PATCH 0/5 V6] CDROM Management

From: Aline Manera <alinefm@br.ibm.com> Hi all, I helped Daniel to apply the suggestions made on reviews and I am resending his patches. v6: - Addressed Royce's comments made on v5 ------------------------------------------------------------------------------ This patch set implements host storage devices management. It implements full CDROM device add, remove and update functionality. It implements basic Disk functionalities. Changelog: v5: - Addressed Aline's comments from her review in v4 v4: - Imports fix, author fix, pep8 fixes - Cut off HDD support - lxml changes in model/vms.py - test_model.py changes - add, update and remove cdroms - existing device name verification in model/vms.py - changed the code to use the new exception model V3: - Using lxml to parse the XML information in model/vms.py - Addressed comments/suggestions from the ML in other files V2: - Add devices to mockmodel and add test cases to rest API - Assign name automatically to new devices, if not passed by user - Fix minor errors To test this contribution: - get info of all storages (cdrom and disks) of a VM curl -u <user> -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8000/vms/<vm_name>/storages -X GET - add a new cdrom to a VM curl -u <user> -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8000/vms/<vm_name>/storages -X POST -d'{"type": "cdrom", "path": "<path_to_iso>"}' - get specific info from cdrom device curl -u <user> -H "Content-Type: application/json" -H "Accet: application/json" http://localhost:8000/vms/<vm_name>/storages/<dev_name> -X GET - delete a cdrom device from a VM curl -u <user> -H "Content-Type: application/json" -H "Accet: application/json" http://localhost:8000/vms/<vm_name>/storages/<dev_name> -X DELETE - update a cdrom device from a VM curl -u <user> -H "Content-Type: application/json" -H "Accet: application/json" http://localhost:8000/vms/<vm_name>/storages/<dev_name> -X PUT -d '{"path":<path_to_iso>}' Daniel Henrique Barboza (2): CDROM Management: API.md and externalized error messages CDROM Management: changes in tests/test_model.py Rodrigo Trujillo (3): CDROM Management: Update controller and API.json for guest storages CDROM Management: Devices management model implementation CDROM Management: Guest vm storage devices mockmodel and rest api test cases docs/API.md | 21 ++++ po/en_US.po | 70 +++++++++++- po/kimchi.pot | 70 +++++++++++- po/pt_BR.po | 70 +++++++++++- po/zh_CN.po | 70 +++++++++++- src/kimchi/API.json | 37 +++++++ src/kimchi/control/vm/storages.py | 49 ++++++++ src/kimchi/i18n.py | 12 ++ src/kimchi/mockmodel.py | 70 +++++++++++- src/kimchi/model/vmstorages.py | 221 +++++++++++++++++++++++++++++++++++++ tests/test_model.py | 61 ++++++++++ tests/test_rest.py | 75 +++++++++++++ 12 files changed, 817 insertions(+), 9 deletions(-) create mode 100644 src/kimchi/control/vm/storages.py create mode 100644 src/kimchi/model/vmstorages.py -- 1.7.10.4

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> This patch changes API.md with new storage sub-collection/sub-resource information. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> Excluding hard drive support from docs/API.md Adding externalized string to be used in the new exception model Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- docs/API.md | 21 ++++++++++++++++ po/en_US.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/kimchi.pot | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/pt_BR.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/zh_CN.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/kimchi/i18n.py | 12 +++++++++ 6 files changed, 305 insertions(+), 8 deletions(-) diff --git a/docs/API.md b/docs/API.md index 89acd44..2b60d52 100644 --- a/docs/API.md +++ b/docs/API.md @@ -117,6 +117,27 @@ Represents a snapshot of the Virtual Machine's primary monitor. * **GET**: Redirect to the latest screenshot of a Virtual Machine in PNG format + +### Sub-collection: Virtual Machine storages +**URI:** /vms/*:name*/storages +* **GET**: Retrieve a summarized list of all storages of specified guest +* **POST**: Attach a new storage or virtual drive to specified virtual machine. + * dev: The name of the storage in the vm. + * type: The type of the storage (currently supports 'cdrom' only). + * path: Path of cdrom iso. + +### Sub-resource: storage +**URI:** /vms/*:name*/storages/*:dev* +* **GET**: Retrieve storage information + * dev: The name of the storage in the vm. + * type: The type of the storage ('cdrom' only for now). + * path: Path of cdrom iso. +* **PUT**: Update storage information + * path: Path of cdrom iso. Can not be blank. +* **DELETE**: Remove the storage. + + + ### Collection: Templates **URI:** /templates diff --git a/po/en_US.po b/po/en_US.po index 5a5327a..0688c75 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-07-11 17:32-0400\n" "Last-Translator: Crístian Viana <vianac@linux.vnet.ibm.com>\n" "Language-Team: English\n" @@ -728,6 +728,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr "" +msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -909,6 +916,22 @@ msgid "Specify name and type to create a storage pool" msgstr "" #, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr "" @@ -1034,7 +1057,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr "" #, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr "" #, python-format @@ -1060,6 +1083,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr "" #, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr "" @@ -1073,3 +1100,42 @@ msgstr "" msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/kimchi.pot b/po/kimchi.pot index a4b3935..f970d6c 100755 --- a/po/kimchi.pot +++ b/po/kimchi.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -713,6 +713,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr "" +msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -894,6 +901,22 @@ msgid "Specify name and type to create a storage pool" msgstr "" #, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr "" @@ -1019,7 +1042,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr "" #, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr "" #, python-format @@ -1045,6 +1068,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr "" #, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr "" @@ -1058,3 +1085,42 @@ msgstr "" msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po index adf291a..8efa264 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-06-27 10:48+0000\n" "Last-Translator: Crístian Viana <vianac@linux.vnet.ibm.com>\n" "Language-Team: Aline Manera <alinefm@br.ibm.com>\n" @@ -743,6 +743,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr "" +msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -924,6 +931,22 @@ msgid "Specify name and type to create a storage pool" msgstr "" #, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr "" @@ -1049,7 +1072,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr "" #, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr "" #, python-format @@ -1075,6 +1098,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr "" #, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr "" @@ -1088,3 +1115,42 @@ msgstr "" msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 88ef9ed..d0dc7fe 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-06-27 10:48+0000\n" "Last-Translator: ShaoHe Feng <shaohef@linux.vnet.ibm.com>\n" "Language-Team: ShaoHe Feng <shaohef@linux.vnet.ibm.com>\n" @@ -731,6 +731,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr "" +msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -912,6 +919,22 @@ msgid "Specify name and type to create a storage pool" msgstr "" #, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr "" @@ -1037,7 +1060,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr "" #, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr "" #, python-format @@ -1063,6 +1086,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr "" #, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr "" @@ -1076,3 +1103,42 @@ msgstr "" msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index d3f9fcf..b21f572 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -185,4 +185,16 @@ messages = { "KCHUTILS0001E": _("Invalid URI %(uri)s"), "KCHUTILS0002E": _("Timeout while running command '%(cmd)s' after %(seconds)s seconds"), "KCHUTILS0003E": _("Unable to choose a virutal machine name"), + + "KCHCDROM0001E": _("Invalid CDROM device name"), + "KCHCDROM0002E": _("Invalid storage type. Types supported: 'cdrom'"), + "KCHCDROM0003E": _("The path %(value)s is not valid local/remote path for the device"), + "KCHCDROM0004E": _("Device name %(dev_name)s already exists in vm %(vm_name)s"), + "KCHCDROM0005E": _("Must specify a device name"), + "KCHCDROM0006E": _("Can't specify a directory for a CDROM device path"), + "KCHCDROM0007E": _("The storage device %(dev_name)s does not exist in the guest %(vm_name)s"), + "KCHCDROM0008E": _("Error while creating new storage device: %(error)s"), + "KCHCDROM0009E": _("Error while updating storage device: %(error)s"), + "KCHCDROM0010E": _("Error while removing storage device: %(error)s"), + "KCHCDROM0011E": _("Do not support guest CDROM hot plug attachment"), } -- 1.7.10.4

Reviewed-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
This patch changes API.md with new storage sub-collection/sub-resource information.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Excluding hard drive support from docs/API.md Adding externalized string to be used in the new exception model
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- docs/API.md | 21 ++++++++++++++++ po/en_US.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/kimchi.pot | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/pt_BR.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/zh_CN.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/kimchi/i18n.py | 12 +++++++++ 6 files changed, 305 insertions(+), 8 deletions(-)
diff --git a/docs/API.md b/docs/API.md index 89acd44..2b60d52 100644 --- a/docs/API.md +++ b/docs/API.md @@ -117,6 +117,27 @@ Represents a snapshot of the Virtual Machine's primary monitor.
* **GET**: Redirect to the latest screenshot of a Virtual Machine in PNG format
+ +### Sub-collection: Virtual Machine storages +**URI:** /vms/*:name*/storages +* **GET**: Retrieve a summarized list of all storages of specified guest +* **POST**: Attach a new storage or virtual drive to specified virtual machine. + * dev: The name of the storage in the vm. + * type: The type of the storage (currently supports 'cdrom' only). + * path: Path of cdrom iso. + +### Sub-resource: storage +**URI:** /vms/*:name*/storages/*:dev* +* **GET**: Retrieve storage information + * dev: The name of the storage in the vm. + * type: The type of the storage ('cdrom' only for now). + * path: Path of cdrom iso. +* **PUT**: Update storage information + * path: Path of cdrom iso. Can not be blank. +* **DELETE**: Remove the storage. + + + ### Collection: Templates
**URI:** /templates diff --git a/po/en_US.po b/po/en_US.po index 5a5327a..0688c75 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-07-11 17:32-0400\n" "Last-Translator: Crístian Viana <vianac@linux.vnet.ibm.com>\n" "Language-Team: English\n" @@ -728,6 +728,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr ""
+msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -909,6 +916,22 @@ msgid "Specify name and type to create a storage pool" msgstr ""
#, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr ""
@@ -1034,7 +1057,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr ""
#, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr ""
#, python-format @@ -1060,6 +1083,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr ""
#, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr ""
@@ -1073,3 +1100,42 @@ msgstr ""
msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/kimchi.pot b/po/kimchi.pot index a4b3935..f970d6c 100755 --- a/po/kimchi.pot +++ b/po/kimchi.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -713,6 +713,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr ""
+msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -894,6 +901,22 @@ msgid "Specify name and type to create a storage pool" msgstr ""
#, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr ""
@@ -1019,7 +1042,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr ""
#, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr ""
#, python-format @@ -1045,6 +1068,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr ""
#, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr ""
@@ -1058,3 +1085,42 @@ msgstr ""
msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po index adf291a..8efa264 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-06-27 10:48+0000\n" "Last-Translator: Crístian Viana <vianac@linux.vnet.ibm.com>\n" "Language-Team: Aline Manera <alinefm@br.ibm.com>\n" @@ -743,6 +743,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr ""
+msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -924,6 +931,22 @@ msgid "Specify name and type to create a storage pool" msgstr ""
#, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr ""
@@ -1049,7 +1072,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr ""
#, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr ""
#, python-format @@ -1075,6 +1098,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr ""
#, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr ""
@@ -1088,3 +1115,42 @@ msgstr ""
msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 88ef9ed..d0dc7fe 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-06-27 10:48+0000\n" "Last-Translator: ShaoHe Feng <shaohef@linux.vnet.ibm.com>\n" "Language-Team: ShaoHe Feng <shaohef@linux.vnet.ibm.com>\n" @@ -731,6 +731,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr ""
+msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -912,6 +919,22 @@ msgid "Specify name and type to create a storage pool" msgstr ""
#, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr ""
@@ -1037,7 +1060,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr ""
#, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr ""
#, python-format @@ -1063,6 +1086,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr ""
#, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr ""
@@ -1076,3 +1103,42 @@ msgstr ""
msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index d3f9fcf..b21f572 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -185,4 +185,16 @@ messages = { "KCHUTILS0001E": _("Invalid URI %(uri)s"), "KCHUTILS0002E": _("Timeout while running command '%(cmd)s' after %(seconds)s seconds"), "KCHUTILS0003E": _("Unable to choose a virutal machine name"), + + "KCHCDROM0001E": _("Invalid CDROM device name"), + "KCHCDROM0002E": _("Invalid storage type. Types supported: 'cdrom'"), + "KCHCDROM0003E": _("The path %(value)s is not valid local/remote path for the device"), + "KCHCDROM0004E": _("Device name %(dev_name)s already exists in vm %(vm_name)s"), + "KCHCDROM0005E": _("Must specify a device name"), + "KCHCDROM0006E": _("Can't specify a directory for a CDROM device path"), + "KCHCDROM0007E": _("The storage device %(dev_name)s does not exist in the guest %(vm_name)s"), + "KCHCDROM0008E": _("Error while creating new storage device: %(error)s"), + "KCHCDROM0009E": _("Error while updating storage device: %(error)s"), + "KCHCDROM0010E": _("Error while removing storage device: %(error)s"), + "KCHCDROM0011E": _("Do not support guest CDROM hot plug attachment"), }

Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
This patch changes API.md with new storage sub-collection/sub-resource information.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Excluding hard drive support from docs/API.md Adding externalized string to be used in the new exception model
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- docs/API.md | 21 ++++++++++++++++ po/en_US.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/kimchi.pot | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/pt_BR.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- po/zh_CN.po | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/kimchi/i18n.py | 12 +++++++++ 6 files changed, 305 insertions(+), 8 deletions(-)
diff --git a/docs/API.md b/docs/API.md index 89acd44..2b60d52 100644 --- a/docs/API.md +++ b/docs/API.md @@ -117,6 +117,27 @@ Represents a snapshot of the Virtual Machine's primary monitor.
* **GET**: Redirect to the latest screenshot of a Virtual Machine in PNG format
+ +### Sub-collection: Virtual Machine storages +**URI:** /vms/*:name*/storages +* **GET**: Retrieve a summarized list of all storages of specified guest +* **POST**: Attach a new storage or virtual drive to specified virtual machine. + * dev: The name of the storage in the vm. + * type: The type of the storage (currently supports 'cdrom' only). + * path: Path of cdrom iso. + +### Sub-resource: storage +**URI:** /vms/*:name*/storages/*:dev* +* **GET**: Retrieve storage information + * dev: The name of the storage in the vm. + * type: The type of the storage ('cdrom' only for now). + * path: Path of cdrom iso. +* **PUT**: Update storage information + * path: Path of cdrom iso. Can not be blank. +* **DELETE**: Remove the storage. + + + ### Collection: Templates
**URI:** /templates diff --git a/po/en_US.po b/po/en_US.po index 5a5327a..0688c75 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-07-11 17:32-0400\n" "Last-Translator: Crístian Viana <vianac@linux.vnet.ibm.com>\n" "Language-Team: English\n" @@ -728,6 +728,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr ""
+msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -909,6 +916,22 @@ msgid "Specify name and type to create a storage pool" msgstr ""
#, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr ""
@@ -1034,7 +1057,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr ""
#, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr ""
#, python-format @@ -1060,6 +1083,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr ""
#, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr ""
@@ -1073,3 +1100,42 @@ msgstr ""
msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/kimchi.pot b/po/kimchi.pot index a4b3935..f970d6c 100755 --- a/po/kimchi.pot +++ b/po/kimchi.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -713,6 +713,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr ""
+msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -894,6 +901,22 @@ msgid "Specify name and type to create a storage pool" msgstr ""
#, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr ""
@@ -1019,7 +1042,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr ""
#, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr ""
#, python-format @@ -1045,6 +1068,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr ""
#, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr ""
@@ -1058,3 +1085,42 @@ msgstr ""
msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po index adf291a..8efa264 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-06-27 10:48+0000\n" "Last-Translator: Crístian Viana <vianac@linux.vnet.ibm.com>\n" "Language-Team: Aline Manera <alinefm@br.ibm.com>\n" @@ -743,6 +743,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr ""
+msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -924,6 +931,22 @@ msgid "Specify name and type to create a storage pool" msgstr ""
#, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr ""
@@ -1049,7 +1072,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr ""
#, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr ""
#, python-format @@ -1075,6 +1098,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr ""
#, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr ""
@@ -1088,3 +1115,42 @@ msgstr ""
msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 88ef9ed..d0dc7fe 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: kimchi 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-11 15:02-0200\n" +"POT-Creation-Date: 2014-02-14 13:34-0200\n" "PO-Revision-Date: 2013-06-27 10:48+0000\n" "Last-Translator: ShaoHe Feng <shaohef@linux.vnet.ibm.com>\n" "Language-Team: ShaoHe Feng <shaohef@linux.vnet.ibm.com>\n" @@ -731,6 +731,13 @@ msgstr "" msgid "Specify a template to create a virtual machine from" msgstr ""
+msgid "Volume list (LUNs names) not given." +msgstr "" + +msgid "" +"Virtual machine volumes must be a list of strings with distinct LUNs names." +msgstr "" + #, python-format msgid "Interface %(iface)s does not exist in virtual machine %(name)s" msgstr "" @@ -912,6 +919,22 @@ msgid "Specify name and type to create a storage pool" msgstr ""
#, python-format +msgid "" +"%(disk)s is not a valid disk/partition. Could not add it to the pool " +"%(pool)s." +msgstr "" + +#, python-format +msgid "Error while extending logical pool %(pool)s. Details: %(err)s" +msgstr "" + +msgid "The parameter disks only can be updated for logical storage pool." +msgstr "" + +msgid "The SCSI host adapter name must be a string." +msgstr "" + +#, python-format msgid "Storage volume %(name)s already exists" msgstr ""
@@ -1037,7 +1060,7 @@ msgid "Unable to create debug report %(name)s. Details: %(err)s." msgstr ""
#, python-format -msgid "Can not find generated debug report named %(name)s" +msgid "Can not find any generated debug report matching name %(name)s" msgstr ""
#, python-format @@ -1063,6 +1086,10 @@ msgid "Unable to reboot host machine as there are running virtual machines" msgstr ""
#, python-format +msgid "Node device '%(name)s' not found" +msgstr "" + +#, python-format msgid "Unable to find %(item)s in datastore" msgstr ""
@@ -1076,3 +1103,42 @@ msgstr ""
msgid "Unable to choose a virutal machine name" msgstr "" + +msgid "Invalid CDROM device name" +msgstr "" + +msgid "Invalid storage type. Types supported: 'cdrom'" +msgstr "" + +#, python-format +msgid "The path %(value)s is not valid local/remote path for the device" +msgstr "" + +#, python-format +msgid "Device name %(dev_name)s already exists in vm %(vm_name)s" +msgstr "" + +msgid "Must specify a device name" +msgstr "" + +msgid "Can't specify a directory for a CDROM device path" +msgstr "" + +#, python-format +msgid "The storage device %(dev_name)s does not exist in the guest %(vm_name)s" +msgstr "" + +#, python-format +msgid "Error while creating new storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while updating storage device: %(error)s" +msgstr "" + +#, python-format +msgid "Error while removing storage device: %(error)s" +msgstr "" + +msgid "Do not support guest CDROM hot plug attachment" +msgstr "" diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index d3f9fcf..b21f572 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -185,4 +185,16 @@ messages = { "KCHUTILS0001E": _("Invalid URI %(uri)s"), "KCHUTILS0002E": _("Timeout while running command '%(cmd)s' after %(seconds)s seconds"), "KCHUTILS0003E": _("Unable to choose a virutal machine name"), + + "KCHCDROM0001E": _("Invalid CDROM device name"), + "KCHCDROM0002E": _("Invalid storage type. Types supported: 'cdrom'"), + "KCHCDROM0003E": _("The path %(value)s is not valid local/remote path for the device"), + "KCHCDROM0004E": _("Device name %(dev_name)s already exists in vm %(vm_name)s"), + "KCHCDROM0005E": _("Must specify a device name"), + "KCHCDROM0006E": _("Can't specify a directory for a CDROM device path"), + "KCHCDROM0007E": _("The storage device %(dev_name)s does not exist in the guest %(vm_name)s"), + "KCHCDROM0008E": _("Error while creating new storage device: %(error)s"), + "KCHCDROM0009E": _("Error while updating storage device: %(error)s"), + "KCHCDROM0010E": _("Error while removing storage device: %(error)s"), + "KCHCDROM0011E": _("Do not support guest CDROM hot plug attachment"), }

From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> This patch adds the control classes for guest storages Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> Removing hard disk support from API.json Changed API.json declarations to the new exception model Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/API.json | 37 ++++++++++++++++++++++++++++ src/kimchi/control/vm/storages.py | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/kimchi/control/vm/storages.py diff --git a/src/kimchi/API.json b/src/kimchi/API.json index a61e778..c36244c 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -352,6 +352,43 @@ "additionalProperties": false, "error": "KCHAPI0001E" }, + "storages_create": { + "type": "object", + "properties": { + "dev": { + "description": "The storage (cd-rom) device name", + "type": "string", + "pattern": "^hd[b-z]$", + "error": "KCHCDROM0001E" + }, + "type": { + "description": "The storage type", + "type": "string", + "pattern": "^cdrom$", + "required": true, + "error": "KCHCDROM0002E" + }, + "path": { + "description": "Path of iso image file or disk mount point", + "type": "string", + "pattern": "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", + "required": true, + "error": "KCHCDROM0003E" + } + } + }, + "storage_update": { + "type": "object", + "properties": { + "path": { + "description": "Path of iso image file or disk mount point", + "type": "string", + "pattern": "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", + "required": true, + "error": "KCHCDROM0003E" + } + } + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/vm/storages.py b/src/kimchi/control/vm/storages.py new file mode 100644 index 0000000..f0e8fb5 --- /dev/null +++ b/src/kimchi/control/vm/storages.py @@ -0,0 +1,49 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Rodrigo Trujilo <rodrigo.trujillo@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import UrlSubNode + + +@UrlSubNode("storages") +class VMStorages(Collection): + def __init__(self, model, vm): + super(VMStorages, self).__init__(model) + self.resource = VMStorage + self.vm = vm + self.resource_args = [self.vm, ] + self.model_args = [self.vm, ] + + +class VMStorage(Resource): + def __init__(self, model, vm, ident): + super(VMStorage, self).__init__(model, ident) + self.vm = vm + self.ident = ident + self.info = {} + self.model_args = [self.vm, self.ident] + self.uri_fmt = '/vms/%s/storages/%s' + self.update_params = ['path'] + + @property + def data(self): + return self.info -- 1.7.10.4

Reviewed-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
This patch adds the control classes for guest storages
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Removing hard disk support from API.json Changed API.json declarations to the new exception model
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/API.json | 37 ++++++++++++++++++++++++++++ src/kimchi/control/vm/storages.py | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/kimchi/control/vm/storages.py
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index a61e778..c36244c 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -352,6 +352,43 @@ "additionalProperties": false, "error": "KCHAPI0001E" }, + "storages_create": { + "type": "object", + "properties": { + "dev": { + "description": "The storage (cd-rom) device name", + "type": "string", + "pattern": "^hd[b-z]$", + "error": "KCHCDROM0001E" + }, + "type": { + "description": "The storage type", + "type": "string", + "pattern": "^cdrom$", + "required": true, + "error": "KCHCDROM0002E" + }, + "path": { + "description": "Path of iso image file or disk mount point", + "type": "string", + "pattern": "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", + "required": true, + "error": "KCHCDROM0003E" + } + } + }, + "storage_update": { + "type": "object", + "properties": { + "path": { + "description": "Path of iso image file or disk mount point", + "type": "string", + "pattern": "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", + "required": true, + "error": "KCHCDROM0003E" + } + } + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/vm/storages.py b/src/kimchi/control/vm/storages.py new file mode 100644 index 0000000..f0e8fb5 --- /dev/null +++ b/src/kimchi/control/vm/storages.py @@ -0,0 +1,49 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Rodrigo Trujilo <rodrigo.trujillo@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import UrlSubNode + + +@UrlSubNode("storages") +class VMStorages(Collection): + def __init__(self, model, vm): + super(VMStorages, self).__init__(model) + self.resource = VMStorage + self.vm = vm + self.resource_args = [self.vm, ] + self.model_args = [self.vm, ] + + +class VMStorage(Resource): + def __init__(self, model, vm, ident): + super(VMStorage, self).__init__(model, ident) + self.vm = vm + self.ident = ident + self.info = {} + self.model_args = [self.vm, self.ident] + self.uri_fmt = '/vms/%s/storages/%s' + self.update_params = ['path'] + + @property + def data(self): + return self.info

Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
This patch adds the control classes for guest storages
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Removing hard disk support from API.json Changed API.json declarations to the new exception model
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/API.json | 37 ++++++++++++++++++++++++++++ src/kimchi/control/vm/storages.py | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/kimchi/control/vm/storages.py
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index a61e778..c36244c 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -352,6 +352,43 @@ "additionalProperties": false, "error": "KCHAPI0001E" }, + "storages_create": { + "type": "object", + "properties": { + "dev": { + "description": "The storage (cd-rom) device name", + "type": "string", + "pattern": "^hd[b-z]$", + "error": "KCHCDROM0001E" + }, + "type": { + "description": "The storage type", + "type": "string", + "pattern": "^cdrom$", + "required": true, + "error": "KCHCDROM0002E" + }, + "path": { + "description": "Path of iso image file or disk mount point", + "type": "string", + "pattern": "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", + "required": true, + "error": "KCHCDROM0003E" + } + } + }, + "storage_update": { + "type": "object", + "properties": { + "path": { + "description": "Path of iso image file or disk mount point", + "type": "string", + "pattern": "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", + "required": true, + "error": "KCHCDROM0003E" + } + } + }, "template_update": { "type": "object", "properties": { diff --git a/src/kimchi/control/vm/storages.py b/src/kimchi/control/vm/storages.py new file mode 100644 index 0000000..f0e8fb5 --- /dev/null +++ b/src/kimchi/control/vm/storages.py @@ -0,0 +1,49 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Rodrigo Trujilo <rodrigo.trujillo@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.control.utils import UrlSubNode + + +@UrlSubNode("storages") +class VMStorages(Collection): + def __init__(self, model, vm): + super(VMStorages, self).__init__(model) + self.resource = VMStorage + self.vm = vm + self.resource_args = [self.vm, ] + self.model_args = [self.vm, ] + + +class VMStorage(Resource): + def __init__(self, model, vm, ident): + super(VMStorage, self).__init__(model, ident) + self.vm = vm + self.ident = ident + self.info = {} + self.model_args = [self.vm, self.ident] + self.uri_fmt = '/vms/%s/storages/%s' + self.update_params = ['path'] + + @property + def data(self): + return self.info

From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> This patch adds the CREATE, LOOKUP, UPDATE and DELETE functionalities to guest storage devices support. Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> Using lxml and objectify to parse the XML info. Pep8 adjustments in model/vms.py Removing hard disk support from model/vms.py Including xml creation using lxml in _get_storage_xml Including existing device name verification Changed model/vms.py to use the new exception model. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/model/vmstorages.py | 221 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 src/kimchi/model/vmstorages.py diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py new file mode 100644 index 0000000..1597d49 --- /dev/null +++ b/src/kimchi/model/vmstorages.py @@ -0,0 +1,221 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> +# Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import re +import socket +import string +import urlparse + +import libvirt +import lxml.etree as ET +from lxml import etree, objectify +from lxml.builder import E + +from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError +from kimchi.exception import OperationFailed +from kimchi.model.vms import DOM_STATE_MAP, VMModel +from kimchi.utils import check_url_path + +DEV_TYPE_SRC_ATTR_MAP = {'file': 'file', + 'block': 'dev'} + + +class VMStoragesModel(object): + def __init__(self, **kargs): + self.conn = kargs['conn'] + + def _get_storage_xml(self, params): + src_type = params.get('src_type') + disk = E.disk(type=src_type, device=params.get('type')) + disk.append(E.driver(name='qemu', type='raw')) + # Working with url paths + if src_type == 'network': + output = urlparse.urlparse(params.get('path')) + host = E.host(name=output.hostname, port= + output.port or socket.getservbyname(output.scheme)) + source = E.source(protocol=output.scheme, name=output.path) + source.append(host) + disk.append(source) + else: + # Fixing source attribute + source = E.source() + source.set(DEV_TYPE_SRC_ATTR_MAP[src_type], params.get('path')) + disk.append(source) + + disk.append(E.target(dev=params.get('dev'), bus='ide')) + return ET.tostring(disk) + + def create(self, vm_name, params): + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + # Use device name passed or pick next + dev_name = params.get('dev', None) + if dev_name is None: + params['dev'] = self._get_storage_device_name(vm_name) + else: + devices = self.get_list(vm_name) + if dev_name in devices: + raise OperationFailed('KCHCDROM0004E', {'dev_name': dev_name, + 'vm_name': vm_name}) + + # Path will never be blank due to API.json verification. + # There is no need to cover this case here. + path = params['path'] + + # Check if path is an url + if check_url_path(path): + params['src_type'] = 'network' + # Check if path is a valid local path + elif os.path.exists(path): + if os.path.isfile(path): + params['src_type'] = 'file' + else: + # Check if path is a valid cdrom drive + with open('/proc/sys/dev/cdrom/info') as cdinfo: + content = cdinfo.read() + + cds = re.findall("drive name:\t\t(.*)", content) + if not cds: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + drives = [os.path.join('/dev', p) for p in cds[0].split('\t')] + if path not in drives: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + params['src_type'] = 'block' + else: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + # Add device to VM + dev_xml = self._get_storage_xml(params) + try: + conn = self.conn.get() + dom = conn.lookupByName(vm_name) + dom.attachDeviceFlags(dev_xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT) + except Exception as e: + raise OperationFailed("KCHCDROM0008E", {'error': e.message}) + return params['dev'] + + def _get_storage_device_name(self, vm_name): + dev_list = [dev for dev in self.get_list(vm_name) + if dev.startswith('hd')] + if len(dev_list) == 0: + return 'hda' + dev_list.sort() + last_dev = dev_list.pop() + # TODO: Improve to device names "greater then" hdz + next_dev_letter_pos = string.ascii_lowercase.index(last_dev[2]) + 1 + return 'hd' + string.ascii_lowercase[next_dev_letter_pos] + + def get_list(self, vm_name): + dom = VMModel.get_vm(vm_name, self.conn) + xml = dom.XMLDesc(0) + devices = objectify.fromstring(xml).devices + storages = [disk.target.attrib['dev'] + for disk in devices.xpath("./disk[@device='disk']")] + storages += [disk.target.attrib['dev'] + for disk in devices.xpath("./disk[@device='cdrom']")] + return storages + + +class VMStorageModel(object): + def __init__(self, **kargs): + self.conn = kargs['conn'] + self.kargs = kargs + + def _get_device_xml(self, vm_name, dev_name): + # Get VM xml and then devices xml + dom = VMModel.get_vm(vm_name, self.conn) + xml = dom.XMLDesc(0) + devices = objectify.fromstring(xml).devices + disk = devices.xpath("./disk/target[@dev='%s']/.." % dev_name) + if not disk: + return None + return disk[0] + + def lookup(self, vm_name, dev_name): + # Retrieve disk xml and format return dict + disk = self._get_device_xml(vm_name, dev_name) + if disk is None: + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + source = disk.source + path = "" + if source is not None: + src_type = disk.attrib['type'] + if src_type == 'network': + host = source.host + path = (source.attrib['protocol'] + '://' + + host.attrib['name'] + ':' + + host.attrib['port'] + source.attrib['name']) + else: + path = source.attrib[DEV_TYPE_SRC_ATTR_MAP[src_type]] + dev_type = disk.attrib['device'] + return {'dev': dev_name, + 'type': dev_type, + 'path': path} + + def delete(self, vm_name, dev_name): + # Get storage device xml + disk = self._get_device_xml(vm_name, dev_name) + if disk is None: + raise NotFoundError("KCHCDROM0007E", + {'dev_name': dev_name, + 'vm_name': vm_name}) + + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + try: + conn = self.conn.get() + dom = conn.lookupByName(vm_name) + dom.detachDeviceFlags(etree.tostring(disk), + libvirt.VIR_DOMAIN_AFFECT_CURRENT) + except Exception as e: + raise OperationFailed("KCHCDROM0010E", {'error': e.message}) + + def update(self, vm_name, dev_name, params): + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + info = self.lookup(vm_name, dev_name) + backup_params = info.copy() + + info.update(params) + kargs = {'conn': self.conn} + stgModel = VMStoragesModel(**kargs) + try: + self.delete(vm_name, dev_name) + return stgModel.create(vm_name, info) + except Exception as e: + try: + stgModel.create(vm_name, backup_params) + except: + pass + + raise OperationFailed("KCHCDROM0009E", {'error': e.message}) -- 1.7.10.4

Reviewed-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
This patch adds the CREATE, LOOKUP, UPDATE and DELETE functionalities to guest storage devices support.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Using lxml and objectify to parse the XML info. Pep8 adjustments in model/vms.py Removing hard disk support from model/vms.py Including xml creation using lxml in _get_storage_xml Including existing device name verification Changed model/vms.py to use the new exception model.
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/model/vmstorages.py | 221 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 src/kimchi/model/vmstorages.py
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py new file mode 100644 index 0000000..1597d49 --- /dev/null +++ b/src/kimchi/model/vmstorages.py @@ -0,0 +1,221 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> +# Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import re +import socket +import string +import urlparse + +import libvirt +import lxml.etree as ET +from lxml import etree, objectify +from lxml.builder import E + +from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError +from kimchi.exception import OperationFailed +from kimchi.model.vms import DOM_STATE_MAP, VMModel +from kimchi.utils import check_url_path + +DEV_TYPE_SRC_ATTR_MAP = {'file': 'file', + 'block': 'dev'} + + +class VMStoragesModel(object): + def __init__(self, **kargs): + self.conn = kargs['conn'] + + def _get_storage_xml(self, params): + src_type = params.get('src_type') + disk = E.disk(type=src_type, device=params.get('type')) + disk.append(E.driver(name='qemu', type='raw')) + # Working with url paths + if src_type == 'network': + output = urlparse.urlparse(params.get('path')) + host = E.host(name=output.hostname, port= + output.port or socket.getservbyname(output.scheme)) + source = E.source(protocol=output.scheme, name=output.path) + source.append(host) + disk.append(source) + else: + # Fixing source attribute + source = E.source() + source.set(DEV_TYPE_SRC_ATTR_MAP[src_type], params.get('path')) + disk.append(source) + + disk.append(E.target(dev=params.get('dev'), bus='ide')) + return ET.tostring(disk) + + def create(self, vm_name, params): + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + # Use device name passed or pick next + dev_name = params.get('dev', None) + if dev_name is None: + params['dev'] = self._get_storage_device_name(vm_name) + else: + devices = self.get_list(vm_name) + if dev_name in devices: + raise OperationFailed('KCHCDROM0004E', {'dev_name': dev_name, + 'vm_name': vm_name}) + + # Path will never be blank due to API.json verification. + # There is no need to cover this case here. + path = params['path'] + + # Check if path is an url + if check_url_path(path): + params['src_type'] = 'network' + # Check if path is a valid local path + elif os.path.exists(path): + if os.path.isfile(path): + params['src_type'] = 'file' + else: + # Check if path is a valid cdrom drive + with open('/proc/sys/dev/cdrom/info') as cdinfo: + content = cdinfo.read() + + cds = re.findall("drive name:\t\t(.*)", content) + if not cds: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + drives = [os.path.join('/dev', p) for p in cds[0].split('\t')] + if path not in drives: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + params['src_type'] = 'block' + else: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + # Add device to VM + dev_xml = self._get_storage_xml(params) + try: + conn = self.conn.get() + dom = conn.lookupByName(vm_name) + dom.attachDeviceFlags(dev_xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT) + except Exception as e: + raise OperationFailed("KCHCDROM0008E", {'error': e.message}) + return params['dev'] + + def _get_storage_device_name(self, vm_name): + dev_list = [dev for dev in self.get_list(vm_name) + if dev.startswith('hd')] + if len(dev_list) == 0: + return 'hda' + dev_list.sort() + last_dev = dev_list.pop() + # TODO: Improve to device names "greater then" hdz + next_dev_letter_pos = string.ascii_lowercase.index(last_dev[2]) + 1 + return 'hd' + string.ascii_lowercase[next_dev_letter_pos] + + def get_list(self, vm_name): + dom = VMModel.get_vm(vm_name, self.conn) + xml = dom.XMLDesc(0) + devices = objectify.fromstring(xml).devices + storages = [disk.target.attrib['dev'] + for disk in devices.xpath("./disk[@device='disk']")] + storages += [disk.target.attrib['dev'] + for disk in devices.xpath("./disk[@device='cdrom']")] + return storages + + +class VMStorageModel(object): + def __init__(self, **kargs): + self.conn = kargs['conn'] + self.kargs = kargs + + def _get_device_xml(self, vm_name, dev_name): + # Get VM xml and then devices xml + dom = VMModel.get_vm(vm_name, self.conn) + xml = dom.XMLDesc(0) + devices = objectify.fromstring(xml).devices + disk = devices.xpath("./disk/target[@dev='%s']/.." % dev_name) + if not disk: + return None + return disk[0] + + def lookup(self, vm_name, dev_name): + # Retrieve disk xml and format return dict + disk = self._get_device_xml(vm_name, dev_name) + if disk is None: + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + source = disk.source + path = "" + if source is not None: + src_type = disk.attrib['type'] + if src_type == 'network': + host = source.host + path = (source.attrib['protocol'] + '://' + + host.attrib['name'] + ':' + + host.attrib['port'] + source.attrib['name']) + else: + path = source.attrib[DEV_TYPE_SRC_ATTR_MAP[src_type]] + dev_type = disk.attrib['device'] + return {'dev': dev_name, + 'type': dev_type, + 'path': path} + + def delete(self, vm_name, dev_name): + # Get storage device xml + disk = self._get_device_xml(vm_name, dev_name) + if disk is None: + raise NotFoundError("KCHCDROM0007E", + {'dev_name': dev_name, + 'vm_name': vm_name}) + + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + try: + conn = self.conn.get() + dom = conn.lookupByName(vm_name) + dom.detachDeviceFlags(etree.tostring(disk), + libvirt.VIR_DOMAIN_AFFECT_CURRENT) + except Exception as e: + raise OperationFailed("KCHCDROM0010E", {'error': e.message}) + + def update(self, vm_name, dev_name, params): + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + info = self.lookup(vm_name, dev_name) + backup_params = info.copy() + + info.update(params) + kargs = {'conn': self.conn} + stgModel = VMStoragesModel(**kargs) + try: + self.delete(vm_name, dev_name) + return stgModel.create(vm_name, info) + except Exception as e: + try: + stgModel.create(vm_name, backup_params) + except: + pass + + raise OperationFailed("KCHCDROM0009E", {'error': e.message})

Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
This patch adds the CREATE, LOOKUP, UPDATE and DELETE functionalities to guest storage devices support.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Using lxml and objectify to parse the XML info. Pep8 adjustments in model/vms.py Removing hard disk support from model/vms.py Including xml creation using lxml in _get_storage_xml Including existing device name verification Changed model/vms.py to use the new exception model.
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/model/vmstorages.py | 221 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 src/kimchi/model/vmstorages.py
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py new file mode 100644 index 0000000..1597d49 --- /dev/null +++ b/src/kimchi/model/vmstorages.py @@ -0,0 +1,221 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# Authors: +# Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> +# Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import re +import socket +import string +import urlparse + +import libvirt +import lxml.etree as ET +from lxml import etree, objectify +from lxml.builder import E + +from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError +from kimchi.exception import OperationFailed +from kimchi.model.vms import DOM_STATE_MAP, VMModel +from kimchi.utils import check_url_path + +DEV_TYPE_SRC_ATTR_MAP = {'file': 'file', + 'block': 'dev'} + + +class VMStoragesModel(object): + def __init__(self, **kargs): + self.conn = kargs['conn'] + + def _get_storage_xml(self, params): + src_type = params.get('src_type') + disk = E.disk(type=src_type, device=params.get('type')) + disk.append(E.driver(name='qemu', type='raw')) + # Working with url paths + if src_type == 'network': + output = urlparse.urlparse(params.get('path')) + host = E.host(name=output.hostname, port= + output.port or socket.getservbyname(output.scheme)) + source = E.source(protocol=output.scheme, name=output.path) + source.append(host) + disk.append(source) + else: + # Fixing source attribute + source = E.source() + source.set(DEV_TYPE_SRC_ATTR_MAP[src_type], params.get('path')) + disk.append(source) + + disk.append(E.target(dev=params.get('dev'), bus='ide')) + return ET.tostring(disk) + + def create(self, vm_name, params): + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + # Use device name passed or pick next + dev_name = params.get('dev', None) + if dev_name is None: + params['dev'] = self._get_storage_device_name(vm_name) + else: + devices = self.get_list(vm_name) + if dev_name in devices: + raise OperationFailed('KCHCDROM0004E', {'dev_name': dev_name, + 'vm_name': vm_name}) + + # Path will never be blank due to API.json verification. + # There is no need to cover this case here. + path = params['path'] + + # Check if path is an url + if check_url_path(path): + params['src_type'] = 'network' + # Check if path is a valid local path + elif os.path.exists(path): + if os.path.isfile(path): + params['src_type'] = 'file' + else: + # Check if path is a valid cdrom drive + with open('/proc/sys/dev/cdrom/info') as cdinfo: + content = cdinfo.read() + + cds = re.findall("drive name:\t\t(.*)", content) + if not cds: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + drives = [os.path.join('/dev', p) for p in cds[0].split('\t')] + if path not in drives: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + params['src_type'] = 'block' + else: + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + # Add device to VM + dev_xml = self._get_storage_xml(params) + try: + conn = self.conn.get() + dom = conn.lookupByName(vm_name) + dom.attachDeviceFlags(dev_xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT) + except Exception as e: + raise OperationFailed("KCHCDROM0008E", {'error': e.message}) + return params['dev'] + + def _get_storage_device_name(self, vm_name): + dev_list = [dev for dev in self.get_list(vm_name) + if dev.startswith('hd')] + if len(dev_list) == 0: + return 'hda' + dev_list.sort() + last_dev = dev_list.pop() + # TODO: Improve to device names "greater then" hdz + next_dev_letter_pos = string.ascii_lowercase.index(last_dev[2]) + 1 + return 'hd' + string.ascii_lowercase[next_dev_letter_pos] + + def get_list(self, vm_name): + dom = VMModel.get_vm(vm_name, self.conn) + xml = dom.XMLDesc(0) + devices = objectify.fromstring(xml).devices + storages = [disk.target.attrib['dev'] + for disk in devices.xpath("./disk[@device='disk']")] + storages += [disk.target.attrib['dev'] + for disk in devices.xpath("./disk[@device='cdrom']")] + return storages + + +class VMStorageModel(object): + def __init__(self, **kargs): + self.conn = kargs['conn'] + self.kargs = kargs + + def _get_device_xml(self, vm_name, dev_name): + # Get VM xml and then devices xml + dom = VMModel.get_vm(vm_name, self.conn) + xml = dom.XMLDesc(0) + devices = objectify.fromstring(xml).devices + disk = devices.xpath("./disk/target[@dev='%s']/.." % dev_name) + if not disk: + return None + return disk[0] + + def lookup(self, vm_name, dev_name): + # Retrieve disk xml and format return dict + disk = self._get_device_xml(vm_name, dev_name) + if disk is None: + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + source = disk.source + path = "" + if source is not None: + src_type = disk.attrib['type'] + if src_type == 'network': + host = source.host + path = (source.attrib['protocol'] + '://' + + host.attrib['name'] + ':' + + host.attrib['port'] + source.attrib['name']) + else: + path = source.attrib[DEV_TYPE_SRC_ATTR_MAP[src_type]] + dev_type = disk.attrib['device'] + return {'dev': dev_name, + 'type': dev_type, + 'path': path} + + def delete(self, vm_name, dev_name): + # Get storage device xml + disk = self._get_device_xml(vm_name, dev_name) + if disk is None: + raise NotFoundError("KCHCDROM0007E", + {'dev_name': dev_name, + 'vm_name': vm_name}) + + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + try: + conn = self.conn.get() + dom = conn.lookupByName(vm_name) + dom.detachDeviceFlags(etree.tostring(disk), + libvirt.VIR_DOMAIN_AFFECT_CURRENT) + except Exception as e: + raise OperationFailed("KCHCDROM0010E", {'error': e.message}) + + def update(self, vm_name, dev_name, params): + dom = VMModel.get_vm(vm_name, self.conn) + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff': + raise InvalidOperation('KCHCDROM0011E') + + info = self.lookup(vm_name, dev_name) + backup_params = info.copy() + + info.update(params) + kargs = {'conn': self.conn} + stgModel = VMStoragesModel(**kargs) + try: + self.delete(vm_name, dev_name) + return stgModel.create(vm_name, info) + except Exception as e: + try: + stgModel.create(vm_name, backup_params) + except: + pass + + raise OperationFailed("KCHCDROM0009E", {'error': e.message})

From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> This patch implements the mockmodel class to simulate lookup, add, remove, update of devices in a guest vm. Also, it adds test cases to test the rest API. Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> Minor changes/improvements based on ML feedback Removing hard disk tests from tests/tests_rest.py Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 70 ++++++++++++++++++++++++++++++++++++++++++- tests/test_rest.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 988683f..329e17c 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -28,6 +28,7 @@ import ipaddr import os import psutil import random +import string import time import uuid @@ -164,8 +165,23 @@ class MockModel(object): for vol in params['volumes']: vm.disk_paths.append({'pool': pool.name, 'volume': vol}) + else: vm.disk_paths = t.fork_vm_storage(vm_uuid) + + index = 0 + for disk in vm.disk_paths: + storagepath = self._mock_storagepools[disk['pool']].info['path'] + fullpath = os.path.join(storagepath, disk['volume']) + dev_name = "hd" + string.ascii_lowercase[index] + params = {'dev': dev_name, 'path': fullpath, 'type': 'disk'} + vm.storagedevices[dev_name] = MockVMStorageDevice(params) + index += 1 + + cdrom = "hd" + string.ascii_lowercase[index + 1] + cdrom_params = {'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} + vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params) + self._mock_vms[name] = vm return name @@ -570,6 +586,50 @@ class MockModel(object): def networks_get_list(self): return sorted(self._mock_networks.keys()) + def vmstorages_create(self, vm_name, params): + path = params.get('path') + if path.startswith('/') and not os.path.exists(path): + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + dom = self._get_vm(vm_name) + dev = params.get('dev', None) + if dev and dev in self.vmstorages_get_list(vm_name): + return OperationFailed("KCHCDROM0004E", {'dev_name': dev, + 'vm_name': vm_name}) + if not dev: + index = len(dom.storagedevices.keys()) + 1 + params['dev'] = "hd" + string.ascii_lowercase[index] + + vmdev = MockVMStorageDevice(params) + dom.storagedevices[params['dev']] = vmdev + return params['dev'] + + def vmstorages_get_list(self, vm_name): + dom = self._get_vm(vm_name) + return dom.storagedevices.keys() + + def vmstorage_lookup(self, vm_name, dev_name): + dom = self._get_vm(vm_name) + if dev_name not in self.vmstorages_get_list(vm_name): + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + return dom.storagedevices.get(dev_name).info + + def vmstorage_delete(self, vm_name, dev_name): + dom = self._get_vm(vm_name) + if dev_name not in self.vmstorages_get_list(vm_name): + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + dom.storagedevices.pop(dev_name) + + def vmstorage_update(self, vm_name, dev_name, params): + try: + dom = self._get_vm(vm_name) + dom.storagedevices[dev_name].info.update(params) + except Exception as e: + raise OperationFailed("KCHCDROM0009E", {'error': e.message}) + return dev_name + def vmifaces_create(self, vm, params): if (params["type"] == "network" and params["network"] not in self.networks_get_list()): @@ -752,6 +812,13 @@ class MockVMTemplate(VMTemplate): return disk_paths +class MockVMStorageDevice(object): + def __init__(self, params): + self.info = {'dev': params.get('dev'), + 'type': params.get('type'), + 'path': params.get('path')} + + class MockVMIface(object): counter = 0 @@ -777,6 +844,7 @@ class MockVM(object): self.disk_paths = [] self.networks = template_info['networks'] ifaces = [MockVMIface(net) for net in self.networks] + self.storagedevices = {} self.ifaces = dict([(iface.info['mac'], iface) for iface in ifaces]) self.info = {'state': 'shutoff', 'stats': "{'cpu_utilization': 20, 'net_throughput' : 35, \ @@ -898,7 +966,7 @@ def get_mock_environment(): model = MockModel() for i in xrange(5): name = 'test-template-%i' % i - params = {'name': name} + params = {'name': name, 'cdrom': '/file.iso'} t = MockVMTemplate(params, model) model._mock_templates[name] = t diff --git a/tests/test_rest.py b/tests/test_rest.py index deb6fe8..37bfccf 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -354,6 +354,81 @@ class RestTests(unittest.TestCase): resp = self.request('/templates/test', '{}', 'DELETE') self.assertEquals(204, resp.status) + def test_vm_storage_devices(self): + + with RollbackContext() as rollback: + # Create a template as a base for our VMs + req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the template + rollback.prependDefer(self.request, + '/templates/test', '{}', 'DELETE') + + # Create a VM with default args + req = json.dumps({'name': 'test-vm', + 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the VM + rollback.prependDefer(self.request, + '/vms/test-vm', '{}', 'DELETE') + + # Check storage devices + resp = self.request('/vms/test-vm/storages', '{}', 'GET') + devices = json.loads(resp.read()) + self.assertEquals(2, len(devices)) + dev_types = [] + for d in devices: + self.assertIn(u'type', d.keys()) + self.assertIn(u'dev', d.keys()) + self.assertIn(u'path', d.keys()) + dev_types.append(d['type']) + + self.assertEquals(['cdrom', 'disk'], sorted(dev_types)) + + # Attach cdrom with nonexistent iso + req = json.dumps({'dev': 'hdx', + 'type': 'cdrom', + 'path': '/tmp/nonexistent.iso'}) + resp = self.request('/vms/test-vm/storages', req, 'POST') + self.assertEquals(400, resp.status) + + # Attach a cdrom with existent dev name + open('/tmp/existent.iso', 'w').close() + req = json.dumps({'dev': 'hdx', + 'type': 'cdrom', + 'path': '/tmp/existent.iso'}) + resp = self.request('/vms/test-vm/storages', req, 'POST') + self.assertEquals(201, resp.status) + cd_info = json.loads(resp.read()) + self.assertEquals('hdx', cd_info['dev']) + self.assertEquals('cdrom', cd_info['type']) + self.assertEquals('/tmp/existent.iso', cd_info['path']) + # Delete the file and cdrom + rollback.prependDefer(self.request, + '/vms/test-vm/storages/hdx', '{}', 'DELETE') + os.remove('/tmp/existent.iso') + + # Change path of storage cdrom + req = json.dumps({'path': 'http://myserver.com/myiso.iso'}) + resp = self.request('/vms/test-vm/storages/hdx', req, 'PUT') + self.assertEquals(200, resp.status) + cd_info = json.loads(resp.read()) + self.assertEquals('http://myserver.com/myiso.iso', cd_info['path']) + + # Test GET + devs = json.loads(self.request('/vms/test-vm/storages').read()) + self.assertEquals(3, len(devs)) + + # Detach storage cdrom + resp = self.request('/vms/test-vm/storages/hdx', '{}', 'DELETE') + self.assertEquals(204, resp.status) + + # Test GET + devs = json.loads(self.request('/vms/test-vm/storages').read()) + self.assertEquals(2, len(devs)) + def test_vm_iface(self): with RollbackContext() as rollback: -- 1.7.10.4

Reviewed-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
This patch implements the mockmodel class to simulate lookup, add, remove, update of devices in a guest vm. Also, it adds test cases to test the rest API.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Minor changes/improvements based on ML feedback Removing hard disk tests from tests/tests_rest.py
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 70 ++++++++++++++++++++++++++++++++++++++++++- tests/test_rest.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 988683f..329e17c 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -28,6 +28,7 @@ import ipaddr import os import psutil import random +import string import time import uuid
@@ -164,8 +165,23 @@ class MockModel(object): for vol in params['volumes']: vm.disk_paths.append({'pool': pool.name, 'volume': vol}) + else: vm.disk_paths = t.fork_vm_storage(vm_uuid) + + index = 0 + for disk in vm.disk_paths: + storagepath = self._mock_storagepools[disk['pool']].info['path'] + fullpath = os.path.join(storagepath, disk['volume']) + dev_name = "hd" + string.ascii_lowercase[index] + params = {'dev': dev_name, 'path': fullpath, 'type': 'disk'} + vm.storagedevices[dev_name] = MockVMStorageDevice(params) + index += 1 + + cdrom = "hd" + string.ascii_lowercase[index + 1] + cdrom_params = {'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} + vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params) + self._mock_vms[name] = vm return name
@@ -570,6 +586,50 @@ class MockModel(object): def networks_get_list(self): return sorted(self._mock_networks.keys())
+ def vmstorages_create(self, vm_name, params): + path = params.get('path') + if path.startswith('/') and not os.path.exists(path): + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + dom = self._get_vm(vm_name) + dev = params.get('dev', None) + if dev and dev in self.vmstorages_get_list(vm_name): + return OperationFailed("KCHCDROM0004E", {'dev_name': dev, + 'vm_name': vm_name}) + if not dev: + index = len(dom.storagedevices.keys()) + 1 + params['dev'] = "hd" + string.ascii_lowercase[index] + + vmdev = MockVMStorageDevice(params) + dom.storagedevices[params['dev']] = vmdev + return params['dev'] + + def vmstorages_get_list(self, vm_name): + dom = self._get_vm(vm_name) + return dom.storagedevices.keys() + + def vmstorage_lookup(self, vm_name, dev_name): + dom = self._get_vm(vm_name) + if dev_name not in self.vmstorages_get_list(vm_name): + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + return dom.storagedevices.get(dev_name).info + + def vmstorage_delete(self, vm_name, dev_name): + dom = self._get_vm(vm_name) + if dev_name not in self.vmstorages_get_list(vm_name): + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + dom.storagedevices.pop(dev_name) + + def vmstorage_update(self, vm_name, dev_name, params): + try: + dom = self._get_vm(vm_name) + dom.storagedevices[dev_name].info.update(params) + except Exception as e: + raise OperationFailed("KCHCDROM0009E", {'error': e.message}) + return dev_name + def vmifaces_create(self, vm, params): if (params["type"] == "network" and params["network"] not in self.networks_get_list()): @@ -752,6 +812,13 @@ class MockVMTemplate(VMTemplate): return disk_paths
+class MockVMStorageDevice(object): + def __init__(self, params): + self.info = {'dev': params.get('dev'), + 'type': params.get('type'), + 'path': params.get('path')} + + class MockVMIface(object): counter = 0
@@ -777,6 +844,7 @@ class MockVM(object): self.disk_paths = [] self.networks = template_info['networks'] ifaces = [MockVMIface(net) for net in self.networks] + self.storagedevices = {} self.ifaces = dict([(iface.info['mac'], iface) for iface in ifaces]) self.info = {'state': 'shutoff', 'stats': "{'cpu_utilization': 20, 'net_throughput' : 35, \ @@ -898,7 +966,7 @@ def get_mock_environment(): model = MockModel() for i in xrange(5): name = 'test-template-%i' % i - params = {'name': name} + params = {'name': name, 'cdrom': '/file.iso'} t = MockVMTemplate(params, model) model._mock_templates[name] = t
diff --git a/tests/test_rest.py b/tests/test_rest.py index deb6fe8..37bfccf 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -354,6 +354,81 @@ class RestTests(unittest.TestCase): resp = self.request('/templates/test', '{}', 'DELETE') self.assertEquals(204, resp.status)
+ def test_vm_storage_devices(self): + + with RollbackContext() as rollback: + # Create a template as a base for our VMs + req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the template + rollback.prependDefer(self.request, + '/templates/test', '{}', 'DELETE') + + # Create a VM with default args + req = json.dumps({'name': 'test-vm', + 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the VM + rollback.prependDefer(self.request, + '/vms/test-vm', '{}', 'DELETE') + + # Check storage devices + resp = self.request('/vms/test-vm/storages', '{}', 'GET') + devices = json.loads(resp.read()) + self.assertEquals(2, len(devices)) + dev_types = [] + for d in devices: + self.assertIn(u'type', d.keys()) + self.assertIn(u'dev', d.keys()) + self.assertIn(u'path', d.keys()) + dev_types.append(d['type']) + + self.assertEquals(['cdrom', 'disk'], sorted(dev_types)) + + # Attach cdrom with nonexistent iso + req = json.dumps({'dev': 'hdx', + 'type': 'cdrom', + 'path': '/tmp/nonexistent.iso'}) + resp = self.request('/vms/test-vm/storages', req, 'POST') + self.assertEquals(400, resp.status) + + # Attach a cdrom with existent dev name + open('/tmp/existent.iso', 'w').close() + req = json.dumps({'dev': 'hdx', + 'type': 'cdrom', + 'path': '/tmp/existent.iso'}) + resp = self.request('/vms/test-vm/storages', req, 'POST') + self.assertEquals(201, resp.status) + cd_info = json.loads(resp.read()) + self.assertEquals('hdx', cd_info['dev']) + self.assertEquals('cdrom', cd_info['type']) + self.assertEquals('/tmp/existent.iso', cd_info['path']) + # Delete the file and cdrom + rollback.prependDefer(self.request, + '/vms/test-vm/storages/hdx', '{}', 'DELETE') + os.remove('/tmp/existent.iso') + + # Change path of storage cdrom + req = json.dumps({'path': 'http://myserver.com/myiso.iso'}) + resp = self.request('/vms/test-vm/storages/hdx', req, 'PUT') + self.assertEquals(200, resp.status) + cd_info = json.loads(resp.read()) + self.assertEquals('http://myserver.com/myiso.iso', cd_info['path']) + + # Test GET + devs = json.loads(self.request('/vms/test-vm/storages').read()) + self.assertEquals(3, len(devs)) + + # Detach storage cdrom + resp = self.request('/vms/test-vm/storages/hdx', '{}', 'DELETE') + self.assertEquals(204, resp.status) + + # Test GET + devs = json.loads(self.request('/vms/test-vm/storages').read()) + self.assertEquals(2, len(devs)) + def test_vm_iface(self):
with RollbackContext() as rollback:

Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
This patch implements the mockmodel class to simulate lookup, add, remove, update of devices in a guest vm. Also, it adds test cases to test the rest API.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Minor changes/improvements based on ML feedback Removing hard disk tests from tests/tests_rest.py
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 70 ++++++++++++++++++++++++++++++++++++++++++- tests/test_rest.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 988683f..329e17c 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -28,6 +28,7 @@ import ipaddr import os import psutil import random +import string import time import uuid
@@ -164,8 +165,23 @@ class MockModel(object): for vol in params['volumes']: vm.disk_paths.append({'pool': pool.name, 'volume': vol}) + else: vm.disk_paths = t.fork_vm_storage(vm_uuid) + + index = 0 + for disk in vm.disk_paths: + storagepath = self._mock_storagepools[disk['pool']].info['path'] + fullpath = os.path.join(storagepath, disk['volume']) + dev_name = "hd" + string.ascii_lowercase[index] + params = {'dev': dev_name, 'path': fullpath, 'type': 'disk'} + vm.storagedevices[dev_name] = MockVMStorageDevice(params) + index += 1 + + cdrom = "hd" + string.ascii_lowercase[index + 1] + cdrom_params = {'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} + vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params) + self._mock_vms[name] = vm return name
@@ -570,6 +586,50 @@ class MockModel(object): def networks_get_list(self): return sorted(self._mock_networks.keys())
+ def vmstorages_create(self, vm_name, params): + path = params.get('path') + if path.startswith('/') and not os.path.exists(path): + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + dom = self._get_vm(vm_name) + dev = params.get('dev', None) + if dev and dev in self.vmstorages_get_list(vm_name): + return OperationFailed("KCHCDROM0004E", {'dev_name': dev, + 'vm_name': vm_name}) + if not dev: + index = len(dom.storagedevices.keys()) + 1 + params['dev'] = "hd" + string.ascii_lowercase[index] + + vmdev = MockVMStorageDevice(params) + dom.storagedevices[params['dev']] = vmdev + return params['dev'] + + def vmstorages_get_list(self, vm_name): + dom = self._get_vm(vm_name) + return dom.storagedevices.keys() + + def vmstorage_lookup(self, vm_name, dev_name): + dom = self._get_vm(vm_name) + if dev_name not in self.vmstorages_get_list(vm_name): + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + return dom.storagedevices.get(dev_name).info + + def vmstorage_delete(self, vm_name, dev_name): + dom = self._get_vm(vm_name) + if dev_name not in self.vmstorages_get_list(vm_name): + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + dom.storagedevices.pop(dev_name) + + def vmstorage_update(self, vm_name, dev_name, params): + try: + dom = self._get_vm(vm_name) + dom.storagedevices[dev_name].info.update(params) + except Exception as e: + raise OperationFailed("KCHCDROM0009E", {'error': e.message}) + return dev_name + def vmifaces_create(self, vm, params): if (params["type"] == "network" and params["network"] not in self.networks_get_list()): @@ -752,6 +812,13 @@ class MockVMTemplate(VMTemplate): return disk_paths
+class MockVMStorageDevice(object): + def __init__(self, params): + self.info = {'dev': params.get('dev'), + 'type': params.get('type'), + 'path': params.get('path')} + + class MockVMIface(object): counter = 0
@@ -777,6 +844,7 @@ class MockVM(object): self.disk_paths = [] self.networks = template_info['networks'] ifaces = [MockVMIface(net) for net in self.networks] + self.storagedevices = {} self.ifaces = dict([(iface.info['mac'], iface) for iface in ifaces]) self.info = {'state': 'shutoff', 'stats': "{'cpu_utilization': 20, 'net_throughput' : 35, \ @@ -898,7 +966,7 @@ def get_mock_environment(): model = MockModel() for i in xrange(5): name = 'test-template-%i' % i - params = {'name': name} + params = {'name': name, 'cdrom': '/file.iso'} t = MockVMTemplate(params, model) model._mock_templates[name] = t
diff --git a/tests/test_rest.py b/tests/test_rest.py index deb6fe8..37bfccf 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -354,6 +354,81 @@ class RestTests(unittest.TestCase): resp = self.request('/templates/test', '{}', 'DELETE') self.assertEquals(204, resp.status)
+ def test_vm_storage_devices(self): + + with RollbackContext() as rollback: + # Create a template as a base for our VMs + req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the template + rollback.prependDefer(self.request, + '/templates/test', '{}', 'DELETE') + + # Create a VM with default args + req = json.dumps({'name': 'test-vm', + 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the VM + rollback.prependDefer(self.request, + '/vms/test-vm', '{}', 'DELETE') + + # Check storage devices + resp = self.request('/vms/test-vm/storages', '{}', 'GET') + devices = json.loads(resp.read()) + self.assertEquals(2, len(devices)) + dev_types = [] + for d in devices: + self.assertIn(u'type', d.keys()) + self.assertIn(u'dev', d.keys()) + self.assertIn(u'path', d.keys()) + dev_types.append(d['type']) + + self.assertEquals(['cdrom', 'disk'], sorted(dev_types)) + + # Attach cdrom with nonexistent iso + req = json.dumps({'dev': 'hdx', + 'type': 'cdrom', + 'path': '/tmp/nonexistent.iso'}) + resp = self.request('/vms/test-vm/storages', req, 'POST') + self.assertEquals(400, resp.status) + + # Attach a cdrom with existent dev name + open('/tmp/existent.iso', 'w').close() + req = json.dumps({'dev': 'hdx', + 'type': 'cdrom', + 'path': '/tmp/existent.iso'}) + resp = self.request('/vms/test-vm/storages', req, 'POST') + self.assertEquals(201, resp.status) + cd_info = json.loads(resp.read()) + self.assertEquals('hdx', cd_info['dev']) + self.assertEquals('cdrom', cd_info['type']) + self.assertEquals('/tmp/existent.iso', cd_info['path']) + # Delete the file and cdrom + rollback.prependDefer(self.request, + '/vms/test-vm/storages/hdx', '{}', 'DELETE') + os.remove('/tmp/existent.iso') + + # Change path of storage cdrom + req = json.dumps({'path': 'http://myserver.com/myiso.iso'}) + resp = self.request('/vms/test-vm/storages/hdx', req, 'PUT') + self.assertEquals(200, resp.status) + cd_info = json.loads(resp.read()) + self.assertEquals('http://myserver.com/myiso.iso', cd_info['path']) + + # Test GET + devs = json.loads(self.request('/vms/test-vm/storages').read()) + self.assertEquals(3, len(devs)) + + # Detach storage cdrom + resp = self.request('/vms/test-vm/storages/hdx', '{}', 'DELETE') + self.assertEquals(204, resp.status) + + # Test GET + devs = json.loads(self.request('/vms/test-vm/storages').read()) + self.assertEquals(2, len(devs)) + def test_vm_iface(self):
with RollbackContext() as rollback:

From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> Adding model tests to verify the proper behavior of CDROM related model functions - add, remove and update. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- tests/test_model.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/test_model.py b/tests/test_model.py index b374d2d..bf6f27a 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -163,6 +163,67 @@ class ModelTests(unittest.TestCase): self.assertEquals("virtio", iface["model"]) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') + def test_vm_cdrom(self): + inst = model.Model(objstore_loc=self.tmp_store) + with RollbackContext() as rollback: + vm_name = 'kimchi-cdrom' + params = {'name': 'test', 'disks': []} + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, 'test') + params = {'name': vm_name, 'template': '/templates/test'} + inst.vms_create(params) + rollback.prependDefer(inst.vm_delete, vm_name) + + prev_count = len(inst.vmstorages_get_list(vm_name)) + self.assertEquals(1, prev_count) + + # dummy .iso files + iso_path = '/tmp/existent.iso' + iso_path2 = '/tmp/existent2.iso' + open(iso_path, 'w').close() + open(iso_path2, 'w').close() + wrong_iso_path = '/nonexistent.iso' + rollback.prependDefer(os.remove, iso_path) + rollback.prependDefer(os.remove, iso_path2) + + # Create a cdrom + cdrom_args = {"type": "cdrom", + "path": iso_path} + cdrom_dev = inst.vmstorages_create(vm_name, cdrom_args) + storage_list = inst.vmstorages_get_list(vm_name) + self.assertEquals(prev_count + 1, len(storage_list)) + + # Get cdrom info + cd_info = inst.vmstorage_lookup(vm_name, cdrom_dev) + self.assertEquals(u'cdrom', cd_info['type']) + self.assertEquals(iso_path, cd_info['path']) + + # create a cdrom with existing dev_name + cdrom_args['dev'] = cdrom_dev + self.assertRaises(OperationFailed, inst.vmstorages_create, + vm_name, cdrom_args) + + # update path of existing cd with + # non existent iso + self.assertRaises(OperationFailed, inst.vmstorage_update, + vm_name, cdrom_dev, {'path': wrong_iso_path}) + + # update path of existing cd with + # existent iso + inst.vmstorage_update(vm_name, cdrom_dev, {'path': iso_path2}) + cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) + self.assertEquals(iso_path2, cdrom_info['path']) + + # removing non existent cdrom + self.assertRaises(NotFoundError, inst.vmstorage_delete, vm_name, + "fakedev") + + # removing valid cdrom + inst.vmstorage_delete(vm_name, cdrom_dev) + storage_list = inst.vmstorages_get_list(vm_name) + self.assertEquals(prev_count, len(storage_list)) + + @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_storage_provisioning(self): inst = model.Model(objstore_loc=self.tmp_store) -- 1.7.10.4

Reviewed-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
Adding model tests to verify the proper behavior of CDROM related model functions - add, remove and update.
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- tests/test_model.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
diff --git a/tests/test_model.py b/tests/test_model.py index b374d2d..bf6f27a 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -163,6 +163,67 @@ class ModelTests(unittest.TestCase): self.assertEquals("virtio", iface["model"])
@unittest.skipUnless(utils.running_as_root(), 'Must be run as root') + def test_vm_cdrom(self): + inst = model.Model(objstore_loc=self.tmp_store) + with RollbackContext() as rollback: + vm_name = 'kimchi-cdrom' + params = {'name': 'test', 'disks': []} + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, 'test') + params = {'name': vm_name, 'template': '/templates/test'} + inst.vms_create(params) + rollback.prependDefer(inst.vm_delete, vm_name) + + prev_count = len(inst.vmstorages_get_list(vm_name)) + self.assertEquals(1, prev_count) + + # dummy .iso files + iso_path = '/tmp/existent.iso' + iso_path2 = '/tmp/existent2.iso' + open(iso_path, 'w').close() + open(iso_path2, 'w').close() + wrong_iso_path = '/nonexistent.iso' + rollback.prependDefer(os.remove, iso_path) + rollback.prependDefer(os.remove, iso_path2) + + # Create a cdrom + cdrom_args = {"type": "cdrom", + "path": iso_path} + cdrom_dev = inst.vmstorages_create(vm_name, cdrom_args) + storage_list = inst.vmstorages_get_list(vm_name) + self.assertEquals(prev_count + 1, len(storage_list)) + + # Get cdrom info + cd_info = inst.vmstorage_lookup(vm_name, cdrom_dev) + self.assertEquals(u'cdrom', cd_info['type']) + self.assertEquals(iso_path, cd_info['path']) + + # create a cdrom with existing dev_name + cdrom_args['dev'] = cdrom_dev + self.assertRaises(OperationFailed, inst.vmstorages_create, + vm_name, cdrom_args) + + # update path of existing cd with + # non existent iso + self.assertRaises(OperationFailed, inst.vmstorage_update, + vm_name, cdrom_dev, {'path': wrong_iso_path}) + + # update path of existing cd with + # existent iso + inst.vmstorage_update(vm_name, cdrom_dev, {'path': iso_path2}) + cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) + self.assertEquals(iso_path2, cdrom_info['path']) + + # removing non existent cdrom + self.assertRaises(NotFoundError, inst.vmstorage_delete, vm_name, + "fakedev") + + # removing valid cdrom + inst.vmstorage_delete(vm_name, cdrom_dev) + storage_list = inst.vmstorages_get_list(vm_name) + self.assertEquals(prev_count, len(storage_list)) + + @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_storage_provisioning(self): inst = model.Model(objstore_loc=self.tmp_store)

Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
Adding model tests to verify the proper behavior of CDROM related model functions - add, remove and update.
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- tests/test_model.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
diff --git a/tests/test_model.py b/tests/test_model.py index b374d2d..bf6f27a 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -163,6 +163,67 @@ class ModelTests(unittest.TestCase): self.assertEquals("virtio", iface["model"])
@unittest.skipUnless(utils.running_as_root(), 'Must be run as root') + def test_vm_cdrom(self): + inst = model.Model(objstore_loc=self.tmp_store) + with RollbackContext() as rollback: + vm_name = 'kimchi-cdrom' + params = {'name': 'test', 'disks': []} + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, 'test') + params = {'name': vm_name, 'template': '/templates/test'} + inst.vms_create(params) + rollback.prependDefer(inst.vm_delete, vm_name) + + prev_count = len(inst.vmstorages_get_list(vm_name)) + self.assertEquals(1, prev_count) + + # dummy .iso files + iso_path = '/tmp/existent.iso' + iso_path2 = '/tmp/existent2.iso' + open(iso_path, 'w').close() + open(iso_path2, 'w').close() + wrong_iso_path = '/nonexistent.iso' + rollback.prependDefer(os.remove, iso_path) + rollback.prependDefer(os.remove, iso_path2) + + # Create a cdrom + cdrom_args = {"type": "cdrom", + "path": iso_path} + cdrom_dev = inst.vmstorages_create(vm_name, cdrom_args) + storage_list = inst.vmstorages_get_list(vm_name) + self.assertEquals(prev_count + 1, len(storage_list)) + + # Get cdrom info + cd_info = inst.vmstorage_lookup(vm_name, cdrom_dev) + self.assertEquals(u'cdrom', cd_info['type']) + self.assertEquals(iso_path, cd_info['path']) + + # create a cdrom with existing dev_name + cdrom_args['dev'] = cdrom_dev + self.assertRaises(OperationFailed, inst.vmstorages_create, + vm_name, cdrom_args) + + # update path of existing cd with + # non existent iso + self.assertRaises(OperationFailed, inst.vmstorage_update, + vm_name, cdrom_dev, {'path': wrong_iso_path}) + + # update path of existing cd with + # existent iso + inst.vmstorage_update(vm_name, cdrom_dev, {'path': iso_path2}) + cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev) + self.assertEquals(iso_path2, cdrom_info['path']) + + # removing non existent cdrom + self.assertRaises(NotFoundError, inst.vmstorage_delete, vm_name, + "fakedev") + + # removing valid cdrom + inst.vmstorage_delete(vm_name, cdrom_dev) + storage_list = inst.vmstorages_get_list(vm_name) + self.assertEquals(prev_count, len(storage_list)) + + @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_storage_provisioning(self): inst = model.Model(objstore_loc=self.tmp_store)

Reviewed-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> Tested-by: Daniel Barboza <danielhb@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Aline Manera <alinefm@br.ibm.com>
Hi all,
I helped Daniel to apply the suggestions made on reviews and I am resending his patches.
v6: - Addressed Royce's comments made on v5
------------------------------------------------------------------------------ This patch set implements host storage devices management. It implements full CDROM device add, remove and update functionality. It implements basic Disk functionalities.
Changelog:
v5: - Addressed Aline's comments from her review in v4
v4: - Imports fix, author fix, pep8 fixes - Cut off HDD support - lxml changes in model/vms.py - test_model.py changes - add, update and remove cdroms - existing device name verification in model/vms.py - changed the code to use the new exception model
V3: - Using lxml to parse the XML information in model/vms.py - Addressed comments/suggestions from the ML in other files
V2: - Add devices to mockmodel and add test cases to rest API - Assign name automatically to new devices, if not passed by user - Fix minor errors
To test this contribution:
- get info of all storages (cdrom and disks) of a VM
curl -u <user> -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8000/vms/<vm_name>/storages -X GET
- add a new cdrom to a VM
curl -u <user> -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8000/vms/<vm_name>/storages -X POST -d'{"type": "cdrom", "path": "<path_to_iso>"}'
- get specific info from cdrom device
curl -u <user> -H "Content-Type: application/json" -H "Accet: application/json" http://localhost:8000/vms/<vm_name>/storages/<dev_name> -X GET
- delete a cdrom device from a VM
curl -u <user> -H "Content-Type: application/json" -H "Accet: application/json" http://localhost:8000/vms/<vm_name>/storages/<dev_name> -X DELETE
- update a cdrom device from a VM
curl -u <user> -H "Content-Type: application/json" -H "Accet: application/json" http://localhost:8000/vms/<vm_name>/storages/<dev_name> -X PUT -d '{"path":<path_to_iso>}'
Daniel Henrique Barboza (2): CDROM Management: API.md and externalized error messages CDROM Management: changes in tests/test_model.py
Rodrigo Trujillo (3): CDROM Management: Update controller and API.json for guest storages CDROM Management: Devices management model implementation CDROM Management: Guest vm storage devices mockmodel and rest api test cases
docs/API.md | 21 ++++ po/en_US.po | 70 +++++++++++- po/kimchi.pot | 70 +++++++++++- po/pt_BR.po | 70 +++++++++++- po/zh_CN.po | 70 +++++++++++- src/kimchi/API.json | 37 +++++++ src/kimchi/control/vm/storages.py | 49 ++++++++ src/kimchi/i18n.py | 12 ++ src/kimchi/mockmodel.py | 70 +++++++++++- src/kimchi/model/vmstorages.py | 221 +++++++++++++++++++++++++++++++++++++ tests/test_model.py | 61 ++++++++++ tests/test_rest.py | 75 +++++++++++++ 12 files changed, 817 insertions(+), 9 deletions(-) create mode 100644 src/kimchi/control/vm/storages.py create mode 100644 src/kimchi/model/vmstorages.py
participants (3)
-
Aline Manera
-
Daniel H Barboza
-
Rodrigo Trujillo