Repository: libcloud
Updated Branches:
  refs/heads/trunk a53974511 -> 5231cecd2


add image member methods to openstackv2

Signed-off-by: Quentin Pradet <quent...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/d5af528a
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/d5af528a
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/d5af528a

Branch: refs/heads/trunk
Commit: d5af528a6e22b51c9703048f727d6691da306144
Parents: a539745
Author: Rick van de Loo <rickvande...@gmail.com>
Authored: Sun Feb 11 10:47:22 2018 +0100
Committer: Quentin Pradet <quent...@apache.org>
Committed: Fri Mar 2 12:46:03 2018 +0400

----------------------------------------------------------------------
 libcloud/compute/base.py                        | 52 ++++++++++++++++
 libcloud/compute/drivers/openstack.py           | 63 ++++++++++++++++++--
 libcloud/compute/types.py                       |  9 +++
 ...d9a_278a_444c_90a6_d24b8c688a63_members.json |  1 +
 ...embers_016926dff12345e8b10329f24c99745b.json |  2 +
 libcloud/test/compute/test_openstack.py         | 46 +++++++++++++-
 6 files changed, 166 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/d5af528a/libcloud/compute/base.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/base.py b/libcloud/compute/base.py
index 7d04509..f89ca7e 100644
--- a/libcloud/compute/base.py
+++ b/libcloud/compute/base.py
@@ -64,6 +64,7 @@ __all__ = [
     'NodeState',
     'NodeSize',
     'NodeImage',
+    'NodeImageMember',
     'NodeLocation',
     'NodeAuthSSHKey',
     'NodeAuthPassword',
@@ -383,6 +384,57 @@ class NodeImage(UuidMixin):
                 % (self.id, self.name, self.driver.name))
 
 
+class NodeImageMember(UuidMixin):
+    """
+    A member of an image. At some cloud providers there is a mechanism
+    to share images. Once an image is shared with another account that
+    user will be a 'member' of the image.
+
+    For example, see the image members schema in the OpenStack Image
+    Service API v2 documentation. https://developer.openstack.org/
+    api-ref/image/v2/index.html#image-members-schema
+
+    NodeImageMember objects are typically returned by the driver for the
+    cloud provider in response to the list_image_members method
+    """
+
+    def __init__(self, id, image_id, state, driver, created=None, extra=None):
+        """
+        :param id: Image member ID.
+        :type id: ``str``
+
+        :param id: The associated image ID.
+        :type id: ``str``
+
+        :param state: State of the NodeImageMember. If not
+                      provided, will default to UNKNOWN.
+        :type state: :class:`.NodeImageMemberState`
+
+        :param driver: Driver this image belongs to.
+        :type driver: :class:`.NodeDriver`
+
+        :param      created: A datetime object that represents when the
+                             image member was created
+        :type       created: ``datetime.datetime``
+
+        :param extra: Optional provided specific attributes associated with
+                      this image.
+        :type extra: ``dict``
+        """
+        self.id = str(id)
+        self.image_id = str(image_id)
+        self.state = state
+        self.driver = driver
+        self.created = created
+        self.extra = extra or {}
+        UuidMixin.__init__(self)
+
+    def __repr__(self):
+        return (('<NodeImageMember: id=%s, image_id=%s, '
+                 'state=%s, driver=%s  ...>')
+                % (self.id, self.image_id, self.state, self.driver.name))
+
+
 class NodeLocation(object):
     """
     A physical location where nodes can be.

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d5af528a/libcloud/compute/drivers/openstack.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/openstack.py 
b/libcloud/compute/drivers/openstack.py
index 020b657..527dce9 100644
--- a/libcloud/compute/drivers/openstack.py
+++ b/libcloud/compute/drivers/openstack.py
@@ -38,7 +38,7 @@ from libcloud.common.openstack import OpenStackDriverMixin
 from libcloud.common.openstack import OpenStackException
 from libcloud.common.openstack import OpenStackResponse
 from libcloud.utils.networking import is_public_subnet
-from libcloud.compute.base import NodeSize, NodeImage
+from libcloud.compute.base import NodeSize, NodeImage, NodeImageMember
 from libcloud.compute.base import (NodeDriver, Node, NodeLocation,
                                    StorageVolume, VolumeSnapshot)
 from libcloud.compute.base import KeyPair
@@ -1320,6 +1320,21 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver):
             )
         )
 
+    def _to_image_member(self, api_image_member):
+        created = api_image_member['created_at']
+        updated = api_image_member.get('updated_at')
+        return NodeImageMember(
+            id=api_image_member['member_id'],
+            image_id=api_image_member['image_id'],
+            state=api_image_member['status'],
+            created=created,
+            driver=self,
+            extra=dict(
+                schema=api_image_member.get('schema'),
+                updated=updated,
+            )
+        )
+
     def _to_nodes(self, obj):
         servers = obj['servers']
         return [self._to_node(server) for server in servers]
@@ -2545,12 +2560,10 @@ class OpenStack_2_NodeDriver(OpenStack_1_1_NodeDriver):
         :param location: Which data center to list the images in. If
                                empty, undefined behavior will be selected.
                                (optional)
-
         :type location: :class:`.NodeLocation`
-        :param ex_only_active: True if list only active (optional)
 
+        :param ex_only_active: True if list only active (optional)
         :type ex_only_active: ``bool``
-
         """
         if location is not None:
             raise NotImplementedError(
@@ -2569,15 +2582,16 @@ class OpenStack_2_NodeDriver(OpenStack_1_1_NodeDriver):
     def ex_update_image(self, image_id, data):
         """
         Patch a NodeImage. Can be used to set visibility
-        :param      image_id: ID of the image which should be used
 
+        :param      image_id: ID of the image which should be used
         :type       image_id: ``str``
+
         :param      data: The data to PATCH, either a dict or a list
         for example: [
           {'op': 'replace', 'path': '/visibility', 'value': 'shared'}
         ]
-
         :type       data: ``dict|list``
+
         :rtype: :class:`NodeImage`
         """
         response = self.image_connection.request(
@@ -2589,6 +2603,43 @@ class OpenStack_2_NodeDriver(OpenStack_1_1_NodeDriver):
         )
         return self._to_image(response.object)
 
+    def ex_list_image_members(self, image_id):
+        """
+        List all members of an image. See
+        https://developer.openstack.org/api-ref/image/v2/index.html#sharing
+
+        :param      image_id: ID of the image of which the members should
+        be listed
+        :type       image_id: ``str``
+
+        :rtype: ``list`` of :class:`NodeImageMember`
+        """
+        response = self.image_connection.request(
+            '/v2/images/%s/members' % (image_id,)
+        )
+        image_members = []
+        for image_member in response.object['members']:
+            image_members.append(self._to_image_member(image_member))
+        return image_members
+
+    def ex_get_image_member(self, image_id, member_id):
+        """
+        Get a member of an image by id
+
+        :param      image_id: ID of the image of which the member should
+        be listed
+        :type       image_id: ``str``
+
+        :param      member_id: ID of the member to list
+        :type       image_id: ``str``
+
+        :rtype: ``list`` of :class:`NodeImageMember`
+        """
+        response = self.image_connection.request(
+            '/v2/images/%s/members/%s' % (image_id, member_id)
+        )
+        return self._to_image_member(response.object)
+
 
 class OpenStack_1_1_FloatingIpPool(object):
     """

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d5af528a/libcloud/compute/types.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py
index 0de0497..89d7d06 100644
--- a/libcloud/compute/types.py
+++ b/libcloud/compute/types.py
@@ -321,6 +321,15 @@ class VolumeSnapshotState(Type):
     UPDATING = 'updating'
 
 
+class NodeImageMemberState(Type):
+    """
+    Standard states of VolumeSnapshots
+    """
+    ACCEPTED = 'accepted'
+    PENDING = 'pending'
+    REJECTED = 'rejected'
+
+
 class Architecture(object):
     """
     Image and size architectures.

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d5af528a/libcloud/test/compute/fixtures/openstack_v1.1/_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/openstack_v1.1/_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members.json
 
b/libcloud/test/compute/fixtures/openstack_v1.1/_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members.json
new file mode 100644
index 0000000..b81f48d
--- /dev/null
+++ 
b/libcloud/test/compute/fixtures/openstack_v1.1/_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members.json
@@ -0,0 +1 @@
+{"members": [{"status": "accepted", "created_at": "2017-01-12T12:31:50Z", 
"updated_at": "2017-01-12T12:31:54Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"016926dff12345e8b10329f24c99745b", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-12T12:43:24Z", "updated_at": 
"2017-01-12T12:43:28Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "0cb96fe6d1a749a3b32733bf1fc5e3d8", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-19T08:06:33Z", "updated_at": "2017-01-19T08:06:58Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"0d6aa4aaa8624fa38621e23b84a583f6", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-12T12:19:38Z", "updated_at": 
"2017-01-12T12:19:41Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "0e3c876745274d13b6c2ab70f82f4503", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": "2017-01-12T12:
 15:44Z", "updated_at": "2017-01-12T12:15:47Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"13c07d734435bb1ea2faeeb23f8574c6", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-02-09T14:08:46Z", "updated_at": 
"2017-02-09T14:08:33Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "1a0ba874fcaf4a0c814dc85d2a2ef9bc", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-27T13:20:08Z", "updated_at": "2017-01-27T13:20:11Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"1ce2f38e23a44ab8bb6a2cc2b409de06", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-12T12:31:48Z", "updated_at": 
"2017-01-12T12:31:52Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "2b269ec4335745ef84562d4f1fabbb50", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-12T10:33:34Z", "updated_at": "2017-01-12T10:33:37Z", "image_id": "d9a9c
 d9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"31eb590cf2014726bd3aa372f1e21383", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-12T12:27:14Z", "updated_at": 
"2017-01-12T12:27:17Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "32ab6a1cfa6f4c2da7233a27c7ab624c", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-19T08:24:39Z", "updated_at": "2017-01-19T08:24:42Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"3865bd651ee141c5b37b81e2333daaef", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-16T14:12:12Z", "updated_at": 
"2017-01-16T14:12:15Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "33a2b8475b3443738c443b94c3be6852", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-27T13:05:16Z", "updated_at": "2017-01-27T13:05:20Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": "4b9aa0de831245e2b
 26f0b1b53d2b70b", "schema": "/v2/schemas/member"}, {"status": "accepted", 
"created_at": "2017-01-12T12:21:31Z", "updated_at": "2017-01-12T12:21:34Z", 
"image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"4f1568969ad84c939cc9c396a43ad757", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-12T12:13:10Z", "updated_at": 
"2017-01-12T12:13:13Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "5a21d13639bc4141a3030c46e95e8ed4", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-12T10:13:37Z", "updated_at": "2017-01-12T10:13:40Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"679d169d15a8448a91cd66b1d34367d6", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-27T15:31:29Z", "updated_at": 
"2017-01-27T15:31:33Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "6c469adc3d464275abe13aabdd28b19a", "schema": 
"/v2/schemas/member"}, {"status": "ac
 cepted", "created_at": "2017-01-12T12:30:29Z", "updated_at": 
"2017-01-12T12:30:32Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "6fbabd8cba81429cb35ff1df4bd2a62e", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-27T12:54:40Z", "updated_at": "2017-01-27T12:54:44Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"77a940af30ac444ea6734fd809138906", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-02-09T14:16:59Z", "updated_at": 
"2017-02-09T14:17:02Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "796deab733b14a3781f3f9cb07c834a5", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-12T12:12:41Z", "updated_at": "2017-01-12T12:12:44Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"84ee139deb75b133b5616571b989e780", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-02-09T14:01:22Z", "updated_at": "201
 7-02-09T14:01:26Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "88af7921eeda432ca47b09c0e6bd4aa3", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-12T10:56:07Z", "updated_at": "2017-01-12T10:56:10Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"ae394c8ecde5b09a8ba04aafefdac1e6", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-12T12:12:04Z", "updated_at": 
"2017-01-12T12:12:07Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "d146a26974104aa1ba3ba56bed8699dc", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-02-07T27:17:35Z", "updated_at": "2017-02-07T27:17:38Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"da1fe8eafd794ef8bda390dcf78c3927", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-12T12:21:27Z", "updated_at": 
"2017-01-12T12:21:21Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c68
 8a63", "member_id": "da52660bdabf4adeab261a0fde666e80", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-19T08:22:21Z", "updated_at": "2017-01-19T08:22:24Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"e883313f4a864ef9933b3574f4ea3c42", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-12T12:31:51Z", "updated_at": 
"2017-01-12T12:31:54Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "ec9b3bcb5bac4f4ea3dd42afff28713a", "schema": 
"/v2/schemas/member"}, {"status": "accepted", "created_at": 
"2017-01-12T12:15:08Z", "updated_at": "2017-01-12T12:15:12Z", "image_id": 
"d9a9cd9a-278a-444c-90a6-d24b8c688a63", "member_id": 
"fdfb81a8ffaa4f1d9d7b712ac3369332", "schema": "/v2/schemas/member"}, {"status": 
"accepted", "created_at": "2017-01-19T08:13:03Z", "updated_at": 
"2017-01-19T08:13:07Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "ffad303302884e96998c8d4564a263c2", "schema":
  "/v2/schemas/member"}], "schema": "/v2/schemas/members"}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d5af528a/libcloud/test/compute/fixtures/openstack_v1.1/_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members_016926dff12345e8b10329f24c99745b.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/openstack_v1.1/_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members_016926dff12345e8b10329f24c99745b.json
 
b/libcloud/test/compute/fixtures/openstack_v1.1/_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members_016926dff12345e8b10329f24c99745b.json
new file mode 100644
index 0000000..2e1024b
--- /dev/null
+++ 
b/libcloud/test/compute/fixtures/openstack_v1.1/_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members_016926dff12345e8b10329f24c99745b.json
@@ -0,0 +1,2 @@
+{"status": "accepted", "created_at": "2017-01-12T12:31:50Z", "updated_at": 
"2017-01-12T12:31:54Z", "image_id": "d9a9cd9a-278a-444c-90a6-d24b8c688a63", 
"member_id": "016926dff12345e8b10329f24c99745b", "schema": "/v2/schemas/member"}
+

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d5af528a/libcloud/test/compute/test_openstack.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_openstack.py 
b/libcloud/test/compute/test_openstack.py
index de04c42..02ed3f3 100644
--- a/libcloud/test/compute/test_openstack.py
+++ b/libcloud/test/compute/test_openstack.py
@@ -38,7 +38,7 @@ from libcloud.common.base import LibcloudConnection
 from libcloud.common.types import InvalidCredsError, MalformedResponseError, \
     LibcloudError
 from libcloud.compute.types import Provider, KeyPairDoesNotExistError, 
StorageVolumeState, \
-    VolumeSnapshotState
+    VolumeSnapshotState, NodeImageMemberState
 from libcloud.compute.providers import get_driver
 from libcloud.compute.drivers.openstack import (
     OpenStack_1_0_NodeDriver,
@@ -1636,6 +1636,34 @@ class OpenStack_2_Tests(OpenStack_1_1_Tests):
         self.assertEqual(image.extra['minRam'], 0)
         self.assertEqual(image.extra['visibility'], "shared")
 
+    def test_ex_list_image_members(self):
+        image_id = 'd9a9cd9a-278a-444c-90a6-d24b8c688a63'
+        image_member_id = '016926dff12345e8b10329f24c99745b'
+        image_members = self.driver.ex_list_image_members(image_id)
+        self.assertEqual(len(image_members), 30, 'Wrong image member count')
+
+        image_member = image_members[0]
+        self.assertEqual(image_member.id, image_member_id)
+        self.assertEqual(image_member.image_id, image_id)
+        self.assertEqual(image_member.state, NodeImageMemberState.ACCEPTED)
+        self.assertEqual(image_member.created, '2017-01-12T12:31:50Z')
+        self.assertEqual(image_member.extra['updated'], '2017-01-12T12:31:54Z')
+        self.assertEqual(image_member.extra['schema'], '/v2/schemas/member')
+
+    def test_ex_get_image_member(self):
+        image_id = 'd9a9cd9a-278a-444c-90a6-d24b8c688a63'
+        image_member_id = '016926dff12345e8b10329f24c99745b'
+        image_member = self.driver.ex_get_image_member(
+            image_id, image_member_id
+        )
+
+        self.assertEqual(image_member.id, image_member_id)
+        self.assertEqual(image_member.image_id, image_id)
+        self.assertEqual(image_member.state, NodeImageMemberState.ACCEPTED)
+        self.assertEqual(image_member.created, '2017-01-12T12:31:50Z')
+        self.assertEqual(image_member.extra['updated'], '2017-01-12T12:31:54Z')
+        self.assertEqual(image_member.extra['schema'], '/v2/schemas/member')
+
 
 class OpenStack_1_1_FactoryMethodTests(OpenStack_1_1_Tests):
     should_list_locations = False
@@ -1790,6 +1818,22 @@ class OpenStack_1_1_MockHttp(MockHttp, 
unittest.TestCase):
         else:
             raise NotImplementedError()
 
+    def _v2_1337_v2_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members(self, 
method, url, body, headers):
+        if method == "GET":
+            body = 
self.fixtures.load('_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members.json')
+            return (httplib.OK, body, self.json_content_headers, 
httplib.responses[httplib.OK])
+        else:
+            raise NotImplementedError()
+
+    def 
_v2_1337_v2_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members_016926dff12345e8b10329f24c99745b(self,
 method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load(
+                
'_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members_016926dff12345e8b10329f24c99745b.json'
+            )
+            return (httplib.OK, body, self.json_content_headers, 
httplib.responses[httplib.OK])
+        else:
+            raise NotImplementedError()
+
     def _v2_1337_v2_images(self, method, url, body, headers):
         if method == "GET":
             body = self.fixtures.load('_images_v2.json')

Reply via email to