From 87a2257477619407cc2d84c21549668b2775b358 Mon Sep 17 00:00:00 2001
From: Isaku Yamahata <isaku.yamahata@intel.com>
Date: Fri, 27 Jun 2014 18:07:43 +0900
Subject: [PATCH] implement servicevm related command

Change-Id: Ib606eb3e4721ec9a255092bf4581f3b576c3714a
---
 {neutronclient => tackerclient}/shell.py      | 270 ++----------
 tackerclient/tacker/v1_0/vm/__init__.py       |   0
 tackerclient/tacker/v1_0/vm/device.py         | 137 ++++++
 .../tacker/v1_0/vm/device_template.py         |  94 ++++
 .../tacker/v1_0/vm/service_instance.py        | 164 +++++++
 .../unit/{test_cli20.py => test_cli10.py}     |   0
 ...extensions.py => test_cli10_extensions.py} |   0
 tackerclient/tests/unit/vm/__init__.py        |   0
 .../tests/unit/vm/test_cli10_device.py        | 126 ++++++
 .../unit/vm/test_cli10_device_template.py     | 132 ++++++
 .../unit/vm/test_cli10_service_instance.py    | 155 +++++++
 tackerclient/v1_0/client.py                   | 411 ++++++++++++++++++
 12 files changed, 1260 insertions(+), 229 deletions(-)
 rename {neutronclient => tackerclient}/shell.py (59%)
 create mode 100644 tackerclient/tacker/v1_0/vm/__init__.py
 create mode 100644 tackerclient/tacker/v1_0/vm/device.py
 create mode 100644 tackerclient/tacker/v1_0/vm/device_template.py
 create mode 100644 tackerclient/tacker/v1_0/vm/service_instance.py
 rename tackerclient/tests/unit/{test_cli20.py => test_cli10.py} (100%)
 rename tackerclient/tests/unit/{test_cli20_extensions.py => test_cli10_extensions.py} (100%)
 create mode 100644 tackerclient/tests/unit/vm/__init__.py
 create mode 100644 tackerclient/tests/unit/vm/test_cli10_device.py
 create mode 100644 tackerclient/tests/unit/vm/test_cli10_device_template.py
 create mode 100644 tackerclient/tests/unit/vm/test_cli10_service_instance.py
 create mode 100644 tackerclient/v1_0/client.py

diff --git a/neutronclient/shell.py b/tackerclient/shell.py
similarity index 59%
rename from neutronclient/shell.py
rename to tackerclient/shell.py
index 31e23193..9f6ab0fa 100644
--- a/neutronclient/shell.py
+++ b/tackerclient/shell.py
@@ -15,7 +15,7 @@
 #
 
 """
-Command-line interface to the Neutron APIs
+Command-line interface to the Tacker APIs
 """
 
 from __future__ import print_function
@@ -28,46 +28,20 @@ import sys
 from cliff import app
 from cliff import commandmanager
 
-from neutronclient.common import clientmanager
-from neutronclient.common import exceptions as exc
-from neutronclient.common import utils
-from neutronclient.neutron.v2_0 import agent
-from neutronclient.neutron.v2_0 import agentscheduler
-from neutronclient.neutron.v2_0 import credential
-from neutronclient.neutron.v2_0 import extension
-from neutronclient.neutron.v2_0 import floatingip
-from neutronclient.neutron.v2_0.fw import firewall
-from neutronclient.neutron.v2_0.fw import firewallpolicy
-from neutronclient.neutron.v2_0.fw import firewallrule
-from neutronclient.neutron.v2_0.lb import healthmonitor as lb_healthmonitor
-from neutronclient.neutron.v2_0.lb import member as lb_member
-from neutronclient.neutron.v2_0.lb import pool as lb_pool
-from neutronclient.neutron.v2_0.lb import vip as lb_vip
-from neutronclient.neutron.v2_0 import metering
-from neutronclient.neutron.v2_0.nec import packetfilter
-from neutronclient.neutron.v2_0 import netpartition
-from neutronclient.neutron.v2_0 import network
-from neutronclient.neutron.v2_0 import networkprofile
-from neutronclient.neutron.v2_0.nsx import networkgateway
-from neutronclient.neutron.v2_0.nsx import qos_queue
-from neutronclient.neutron.v2_0 import policyprofile
-from neutronclient.neutron.v2_0 import port
-from neutronclient.neutron.v2_0 import quota
-from neutronclient.neutron.v2_0 import router
-from neutronclient.neutron.v2_0 import securitygroup
-from neutronclient.neutron.v2_0 import servicetype
-from neutronclient.neutron.v2_0 import subnet
-from neutronclient.neutron.v2_0.vpn import ikepolicy
-from neutronclient.neutron.v2_0.vpn import ipsec_site_connection
-from neutronclient.neutron.v2_0.vpn import ipsecpolicy
-from neutronclient.neutron.v2_0.vpn import vpnservice
-from neutronclient.openstack.common.gettextutils import _
-from neutronclient.openstack.common import strutils
-from neutronclient.version import __version__
+from tackerclient.common import clientmanager
+from tackerclient.common import exceptions as exc
+from tackerclient.common import utils
+from tackerclient.openstack.common.gettextutils import _
+from tackerclient.openstack.common import strutils
+from tackerclient.tacker.v1_0 import extension
+from tackerclient.tacker.v1_0.vm import device
+from tackerclient.tacker.v1_0.vm import device_template
+from tackerclient.tacker.v1_0.vm import service_instance
+from tackerclient.version import __version__
 
 
-VERSION = '2.0'
-NEUTRON_API_VERSION = '2.0'
+VERSION = '1.0'
+TACKER_API_VERSION = '1.0'
 
 
 def run_command(cmd, cmd_parser, sub_argv):
@@ -97,189 +71,27 @@ def env(*_vars, **kwargs):
     return kwargs.get('default', '')
 
 
-COMMAND_V2 = {
-    'net-list': network.ListNetwork,
-    'net-external-list': network.ListExternalNetwork,
-    'net-show': network.ShowNetwork,
-    'net-create': network.CreateNetwork,
-    'net-delete': network.DeleteNetwork,
-    'net-update': network.UpdateNetwork,
-    'subnet-list': subnet.ListSubnet,
-    'subnet-show': subnet.ShowSubnet,
-    'subnet-create': subnet.CreateSubnet,
-    'subnet-delete': subnet.DeleteSubnet,
-    'subnet-update': subnet.UpdateSubnet,
-    'port-list': port.ListPort,
-    'port-show': port.ShowPort,
-    'port-create': port.CreatePort,
-    'port-delete': port.DeletePort,
-    'port-update': port.UpdatePort,
-    'quota-list': quota.ListQuota,
-    'quota-show': quota.ShowQuota,
-    'quota-delete': quota.DeleteQuota,
-    'quota-update': quota.UpdateQuota,
+COMMAND_V1 = {
     'ext-list': extension.ListExt,
     'ext-show': extension.ShowExt,
-    'router-list': router.ListRouter,
-    'router-port-list': port.ListRouterPort,
-    'router-show': router.ShowRouter,
-    'router-create': router.CreateRouter,
-    'router-delete': router.DeleteRouter,
-    'router-update': router.UpdateRouter,
-    'router-interface-add': router.AddInterfaceRouter,
-    'router-interface-delete': router.RemoveInterfaceRouter,
-    'router-gateway-set': router.SetGatewayRouter,
-    'router-gateway-clear': router.RemoveGatewayRouter,
-    'floatingip-list': floatingip.ListFloatingIP,
-    'floatingip-show': floatingip.ShowFloatingIP,
-    'floatingip-create': floatingip.CreateFloatingIP,
-    'floatingip-delete': floatingip.DeleteFloatingIP,
-    'floatingip-associate': floatingip.AssociateFloatingIP,
-    'floatingip-disassociate': floatingip.DisassociateFloatingIP,
-    'security-group-list': securitygroup.ListSecurityGroup,
-    'security-group-show': securitygroup.ShowSecurityGroup,
-    'security-group-create': securitygroup.CreateSecurityGroup,
-    'security-group-delete': securitygroup.DeleteSecurityGroup,
-    'security-group-update': securitygroup.UpdateSecurityGroup,
-    'security-group-rule-list': securitygroup.ListSecurityGroupRule,
-    'security-group-rule-show': securitygroup.ShowSecurityGroupRule,
-    'security-group-rule-create': securitygroup.CreateSecurityGroupRule,
-    'security-group-rule-delete': securitygroup.DeleteSecurityGroupRule,
-    'lb-vip-list': lb_vip.ListVip,
-    'lb-vip-show': lb_vip.ShowVip,
-    'lb-vip-create': lb_vip.CreateVip,
-    'lb-vip-update': lb_vip.UpdateVip,
-    'lb-vip-delete': lb_vip.DeleteVip,
-    'lb-pool-list': lb_pool.ListPool,
-    'lb-pool-show': lb_pool.ShowPool,
-    'lb-pool-create': lb_pool.CreatePool,
-    'lb-pool-update': lb_pool.UpdatePool,
-    'lb-pool-delete': lb_pool.DeletePool,
-    'lb-pool-stats': lb_pool.RetrievePoolStats,
-    'lb-member-list': lb_member.ListMember,
-    'lb-member-show': lb_member.ShowMember,
-    'lb-member-create': lb_member.CreateMember,
-    'lb-member-update': lb_member.UpdateMember,
-    'lb-member-delete': lb_member.DeleteMember,
-    'lb-healthmonitor-list': lb_healthmonitor.ListHealthMonitor,
-    'lb-healthmonitor-show': lb_healthmonitor.ShowHealthMonitor,
-    'lb-healthmonitor-create': lb_healthmonitor.CreateHealthMonitor,
-    'lb-healthmonitor-update': lb_healthmonitor.UpdateHealthMonitor,
-    'lb-healthmonitor-delete': lb_healthmonitor.DeleteHealthMonitor,
-    'lb-healthmonitor-associate': lb_healthmonitor.AssociateHealthMonitor,
-    'lb-healthmonitor-disassociate': (
-        lb_healthmonitor.DisassociateHealthMonitor
-    ),
-    'queue-create': qos_queue.CreateQoSQueue,
-    'queue-delete': qos_queue.DeleteQoSQueue,
-    'queue-show': qos_queue.ShowQoSQueue,
-    'queue-list': qos_queue.ListQoSQueue,
-    'agent-list': agent.ListAgent,
-    'agent-show': agent.ShowAgent,
-    'agent-delete': agent.DeleteAgent,
-    'agent-update': agent.UpdateAgent,
-    'net-gateway-create': networkgateway.CreateNetworkGateway,
-    'net-gateway-update': networkgateway.UpdateNetworkGateway,
-    'net-gateway-delete': networkgateway.DeleteNetworkGateway,
-    'net-gateway-show': networkgateway.ShowNetworkGateway,
-    'net-gateway-list': networkgateway.ListNetworkGateway,
-    'net-gateway-connect': networkgateway.ConnectNetworkGateway,
-    'net-gateway-disconnect': networkgateway.DisconnectNetworkGateway,
-    'gateway-device-create': networkgateway.CreateGatewayDevice,
-    'gateway-device-update': networkgateway.UpdateGatewayDevice,
-    'gateway-device-delete': networkgateway.DeleteGatewayDevice,
-    'gateway-device-show': networkgateway.ShowGatewayDevice,
-    'gateway-device-list': networkgateway.ListGatewayDevice,
-    'dhcp-agent-network-add': agentscheduler.AddNetworkToDhcpAgent,
-    'dhcp-agent-network-remove': agentscheduler.RemoveNetworkFromDhcpAgent,
-    'net-list-on-dhcp-agent': agentscheduler.ListNetworksOnDhcpAgent,
-    'dhcp-agent-list-hosting-net': agentscheduler.ListDhcpAgentsHostingNetwork,
-    'l3-agent-router-add': agentscheduler.AddRouterToL3Agent,
-    'l3-agent-router-remove': agentscheduler.RemoveRouterFromL3Agent,
-    'router-list-on-l3-agent': agentscheduler.ListRoutersOnL3Agent,
-    'l3-agent-list-hosting-router': agentscheduler.ListL3AgentsHostingRouter,
-    'lb-pool-list-on-agent': agentscheduler.ListPoolsOnLbaasAgent,
-    'lb-agent-hosting-pool': agentscheduler.GetLbaasAgentHostingPool,
-    'service-provider-list': servicetype.ListServiceProvider,
-    'firewall-rule-list': firewallrule.ListFirewallRule,
-    'firewall-rule-show': firewallrule.ShowFirewallRule,
-    'firewall-rule-create': firewallrule.CreateFirewallRule,
-    'firewall-rule-update': firewallrule.UpdateFirewallRule,
-    'firewall-rule-delete': firewallrule.DeleteFirewallRule,
-    'firewall-policy-list': firewallpolicy.ListFirewallPolicy,
-    'firewall-policy-show': firewallpolicy.ShowFirewallPolicy,
-    'firewall-policy-create': firewallpolicy.CreateFirewallPolicy,
-    'firewall-policy-update': firewallpolicy.UpdateFirewallPolicy,
-    'firewall-policy-delete': firewallpolicy.DeleteFirewallPolicy,
-    'firewall-policy-insert-rule': firewallpolicy.FirewallPolicyInsertRule,
-    'firewall-policy-remove-rule': firewallpolicy.FirewallPolicyRemoveRule,
-    'firewall-list': firewall.ListFirewall,
-    'firewall-show': firewall.ShowFirewall,
-    'firewall-create': firewall.CreateFirewall,
-    'firewall-update': firewall.UpdateFirewall,
-    'firewall-delete': firewall.DeleteFirewall,
-    'cisco-credential-list': credential.ListCredential,
-    'cisco-credential-show': credential.ShowCredential,
-    'cisco-credential-create': credential.CreateCredential,
-    'cisco-credential-delete': credential.DeleteCredential,
-    'cisco-network-profile-list': networkprofile.ListNetworkProfile,
-    'cisco-network-profile-show': networkprofile.ShowNetworkProfile,
-    'cisco-network-profile-create': networkprofile.CreateNetworkProfile,
-    'cisco-network-profile-delete': networkprofile.DeleteNetworkProfile,
-    'cisco-network-profile-update': networkprofile.UpdateNetworkProfile,
-    'cisco-policy-profile-list': policyprofile.ListPolicyProfile,
-    'cisco-policy-profile-show': policyprofile.ShowPolicyProfile,
-    'cisco-policy-profile-update': policyprofile.UpdatePolicyProfile,
-    'ipsec-site-connection-list': (
-        ipsec_site_connection.ListIPsecSiteConnection
-    ),
-    'ipsec-site-connection-show': (
-        ipsec_site_connection.ShowIPsecSiteConnection
-    ),
-    'ipsec-site-connection-create': (
-        ipsec_site_connection.CreateIPsecSiteConnection
-    ),
-    'ipsec-site-connection-update': (
-        ipsec_site_connection.UpdateIPsecSiteConnection
-    ),
-    'ipsec-site-connection-delete': (
-        ipsec_site_connection.DeleteIPsecSiteConnection
-    ),
-    'vpn-service-list': vpnservice.ListVPNService,
-    'vpn-service-show': vpnservice.ShowVPNService,
-    'vpn-service-create': vpnservice.CreateVPNService,
-    'vpn-service-update': vpnservice.UpdateVPNService,
-    'vpn-service-delete': vpnservice.DeleteVPNService,
-    'vpn-ipsecpolicy-list': ipsecpolicy.ListIPsecPolicy,
-    'vpn-ipsecpolicy-show': ipsecpolicy.ShowIPsecPolicy,
-    'vpn-ipsecpolicy-create': ipsecpolicy.CreateIPsecPolicy,
-    'vpn-ipsecpolicy-update': ipsecpolicy.UpdateIPsecPolicy,
-    'vpn-ipsecpolicy-delete': ipsecpolicy.DeleteIPsecPolicy,
-    'vpn-ikepolicy-list': ikepolicy.ListIKEPolicy,
-    'vpn-ikepolicy-show': ikepolicy.ShowIKEPolicy,
-    'vpn-ikepolicy-create': ikepolicy.CreateIKEPolicy,
-    'vpn-ikepolicy-update': ikepolicy.UpdateIKEPolicy,
-    'vpn-ikepolicy-delete': ikepolicy.DeleteIKEPolicy,
-    'meter-label-create': metering.CreateMeteringLabel,
-    'meter-label-list': metering.ListMeteringLabel,
-    'meter-label-show': metering.ShowMeteringLabel,
-    'meter-label-delete': metering.DeleteMeteringLabel,
-    'meter-label-rule-create': metering.CreateMeteringLabelRule,
-    'meter-label-rule-list': metering.ListMeteringLabelRule,
-    'meter-label-rule-show': metering.ShowMeteringLabelRule,
-    'meter-label-rule-delete': metering.DeleteMeteringLabelRule,
-    'nuage-netpartition-list': netpartition.ListNetPartition,
-    'nuage-netpartition-show': netpartition.ShowNetPartition,
-    'nuage-netpartition-create': netpartition.CreateNetPartition,
-    'nuage-netpartition-delete': netpartition.DeleteNetPartition,
-    'nec-packet-filter-list': packetfilter.ListPacketFilter,
-    'nec-packet-filter-show': packetfilter.ShowPacketFilter,
-    'nec-packet-filter-create': packetfilter.CreatePacketFilter,
-    'nec-packet-filter-update': packetfilter.UpdatePacketFilter,
-    'nec-packet-filter-delete': packetfilter.DeletePacketFilter,
+    'device-template-create': device_template.CreateDeviceTemplate,
+    'device-template-list': device_template.ListDeviceTemplate,
+    'device-template-show': device_template.ShowDeviceTemplate,
+    'device-template-update': device_template.UpdateDeviceTemplate,
+    'device-template-delete': device_template.DeleteDeviceTemplate,
+    'service-instance-create': service_instance.CreateServiceInstance,
+    'service-instance-list': service_instance.ListServiceInstance,
+    'service-instance-show': service_instance.ShowServiceInstance,
+    'service-instance-update': service_instance.UpdateServiceInstance,
+    'service-instance-delete': service_instance.DeleteServiceInstance,
+    'device-create': device.CreateDevice,
+    'device-list': device.ListDevice,
+    'device-show': device.ShowDevice,
+    'device-update': device.UpdateDevice,
+    'device-delete': device.DeleteDevice,
 }
 
-COMMANDS = {'2.0': COMMAND_V2}
+COMMANDS = {'1.0': COMMAND_V1}
 
 
 class HelpAction(argparse.Action):
@@ -307,7 +119,7 @@ class HelpAction(argparse.Action):
         sys.exit(0)
 
 
-class NeutronShell(app.App):
+class TackerShell(app.App):
 
     # verbose logging levels
     WARNING_LEVEL = 0
@@ -318,10 +130,10 @@ class NeutronShell(app.App):
     log = logging.getLogger(__name__)
 
     def __init__(self, apiversion):
-        super(NeutronShell, self).__init__(
+        super(TackerShell, self).__init__(
             description=__doc__.strip(),
             version=VERSION,
-            command_manager=commandmanager.CommandManager('neutron.cli'), )
+            command_manager=commandmanager.CommandManager('tacker.cli'), )
         self.commands = COMMANDS
         for k, v in self.commands[apiversion].items():
             self.command_manager.add_command(k, v)
@@ -373,8 +185,8 @@ class NeutronShell(app.App):
             '--os-auth-strategy', metavar='<auth-strategy>',
             default=env('OS_AUTH_STRATEGY', default='keystone'),
             help=_('Authentication strategy (Env: OS_AUTH_STRATEGY'
-            ', default keystone). For now, any other value will'
-            ' disable the authentication'))
+                   ', default keystone). For now, any other value will'
+                   ' disable the authentication'))
         parser.add_argument(
             '--os_auth_strategy',
             help=argparse.SUPPRESS)
@@ -466,8 +278,8 @@ class NeutronShell(app.App):
         parser.add_argument(
             '--insecure',
             action='store_true',
-            default=env('NEUTRONCLIENT_INSECURE', default=False),
-            help=_("Explicitly allow neutronclient to perform \"insecure\" "
+            default=env('TACKERCLIENT_INSECURE', default=False),
+            help=_("Explicitly allow tackerclient to perform \"insecure\" "
                    "SSL (https) requests. The server's certificate will "
                    "not be verified against any certificate authorities. "
                    "This option should be used with caution."))
@@ -650,7 +462,7 @@ class NeutronShell(app.App):
         * validate authentication info
         """
 
-        super(NeutronShell, self).initialize_app(argv)
+        super(TackerShell, self).initialize_app(argv)
 
         self.api_version = {'network': self.api_version}
 
@@ -693,9 +505,9 @@ class NeutronShell(app.App):
 
 def main(argv=sys.argv[1:]):
     try:
-        return NeutronShell(NEUTRON_API_VERSION).run(map(strutils.safe_decode,
-                                                         argv))
-    except exc.NeutronClientException:
+        return TackerShell(TACKER_API_VERSION).run(map(strutils.safe_decode,
+                                                   argv))
+    except exc.TackerClientException:
         return 1
     except Exception as e:
         print(unicode(e))
diff --git a/tackerclient/tacker/v1_0/vm/__init__.py b/tackerclient/tacker/v1_0/vm/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tackerclient/tacker/v1_0/vm/device.py b/tackerclient/tacker/v1_0/vm/device.py
new file mode 100644
index 00000000..55e42757
--- /dev/null
+++ b/tackerclient/tacker/v1_0/vm/device.py
@@ -0,0 +1,137 @@
+#
+# Copyright 2013 Intel
+# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
+#                               <isaku.yamahata at gmail com>
+# All Rights Reserved.
+#
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Isaku Yamahata, Intel
+
+from tackerclient.common import exceptions
+from tackerclient.openstack.common.gettextutils import _
+from tackerclient.tacker import v1_0 as tackerV10
+
+
+_DEVICE = 'device'
+
+
+class ListDevice(tackerV10.ListCommand):
+    """List device that belong to a given tenant."""
+
+    resource = _DEVICE
+
+
+class ShowDevice(tackerV10.ShowCommand):
+    """show information of a given Device."""
+
+    resource = _DEVICE
+
+
+class CreateDevice(tackerV10.CreateCommand):
+    """create a Device."""
+
+    resource = _DEVICE
+
+    def add_known_arguments(self, parser):
+        parser.add_argument(
+            '--device-template-id',
+            required=True,
+            help='device template id to create device based on')
+        parser.add_argument(
+            '--kwargs',
+            metavar='<key>=<value>',
+            action='append',
+            dest='kwargs',
+            default=[],
+            help='instance specific argument')
+        parser.add_argument(
+            '--service-context',
+            metavar='<network-id=network-uuid,subnet-id=subnet-uuid,'
+            'port-id=port-uuid,router-id=router-uuid,'
+            'role=role-string,index=int>',
+            action='append',
+            dest='service_context',
+            default=[],
+            help='service context to insert service')
+
+    def args2body(self, parsed_args):
+        body = {
+            self.resource: {
+                'template_id': parsed_args.device_template_id,
+            }
+        }
+        if parsed_args.kwargs:
+            try:
+                kwargs = dict(key_value.split('=', 1)
+                              for key_value in parsed_args.kwargs)
+            except ValueError:
+                msg = (_('invalid argument for --kwargs %s') %
+                       parsed_args.kwargs)
+                raise exceptions.TackerCLIError(msg)
+            if kwargs:
+                body[self.resource]['kwargs'] = kwargs
+        if parsed_args.service_context:
+            try:
+                service_context = [dict(
+                    (k.replace('-', '_'), v)
+                    for k, v in (key_value.split('=', 1)
+                                 for key_value in entry_string.split(',')))
+                    for entry_string in parsed_args.service_context]
+            except ValueError:
+                msg = (_('invalid argument for --service-context %s') %
+                       parsed_args.service_context)
+                raise exceptions.TackerCLIError(msg)
+
+            if service_context:
+                body[self.resource]['service_context'] = service_context
+
+        tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
+        return body
+
+
+class UpdateDevice(tackerV10.UpdateCommand):
+    """Update a given Device."""
+
+    resource = _DEVICE
+
+    def add_known_arguments(self, parser):
+        parser.add_argument(
+            '--kwargs',
+            metavar='<key>=<value>',
+            action='append',
+            dest='kwargs',
+            default=[],
+            help='instance specific argument')
+
+    def args2body(self, parsed_args):
+        body = {self.resource: {}}
+        if parsed_args.kwargs:
+            try:
+                kwargs = dict(key_value.split('=', 1)
+                              for key_value in parsed_args.kwargs)
+            except ValueError:
+                msg = (_('invalid argument for --kwargs %s') %
+                       parsed_args.kwargs)
+                raise exceptions.TackerCLIError(msg)
+            if kwargs:
+                body[self.resource]['kwargs'] = kwargs
+        tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
+        return body
+
+
+class DeleteDevice(tackerV10.DeleteCommand):
+    """Delete a given Device."""
+
+    resource = _DEVICE
diff --git a/tackerclient/tacker/v1_0/vm/device_template.py b/tackerclient/tacker/v1_0/vm/device_template.py
new file mode 100644
index 00000000..2ac9a714
--- /dev/null
+++ b/tackerclient/tacker/v1_0/vm/device_template.py
@@ -0,0 +1,94 @@
+#
+# Copyright 2013 Intel
+# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
+#                               <isaku.yamahata at gmail com>
+# All Rights Reserved.
+#
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Isaku Yamahata, Intel
+
+from tackerclient.tacker import v1_0 as tackerV10
+
+
+_DEVICE_TEMPLATE = "device_template"
+
+
+class ListDeviceTemplate(tackerV10.ListCommand):
+    """List device template that belong to a given tenant."""
+
+    resource = _DEVICE_TEMPLATE
+
+
+class ShowDeviceTemplate(tackerV10.ShowCommand):
+    """show information of a given DeviceTemplate."""
+
+    resource = _DEVICE_TEMPLATE
+
+
+class CreateDeviceTemplate(tackerV10.CreateCommand):
+    """create a DeviceTemplate."""
+
+    resource = _DEVICE_TEMPLATE
+
+    def add_known_arguments(self, parser):
+        parser.add_argument(
+            '--name',
+            help='Set a name for the devicetemplate')
+        parser.add_argument(
+            '--description',
+            help='Set a description for the devicetemplate')
+        parser.add_argument(
+            '--template-service-type',
+            action='append',
+            help='Add a servicetype for the devicetemplate')
+        parser.add_argument(
+            '--device-driver',
+            help='Set a device driver name for the devicetemplate')
+        parser.add_argument(
+            '--mgmt-driver',
+            help='Set a manegement driver name for the devicetemplate')
+        parser.add_argument(
+            '--attribute',
+            nargs=2,
+            action='append',
+            help='Set a servicetypes for the devicetemplate')
+
+    def args2body(self, parsed_args):
+        body = {
+            self.resource: {
+                'service_types': [
+                    {'service_type': service_type}
+                    for service_type in parsed_args.template_service_type],
+                'device_driver': parsed_args.device_driver,
+                'mgmt_driver': parsed_args.mgmt_driver,
+            }
+        }
+        if parsed_args.attribute:
+            body[self.resource]['attributes'] = dict(parsed_args.attribute)
+        tackerV10.update_dict(parsed_args, body[self.resource],
+                              ['tenant_id', 'name', 'description'])
+        return body
+
+
+class UpdateDeviceTemplate(tackerV10.UpdateCommand):
+    """Update a given DeviceTemplate."""
+
+    resource = _DEVICE_TEMPLATE
+    allow_names = False
+
+
+class DeleteDeviceTemplate(tackerV10.DeleteCommand):
+    """Delete a given DeviceTemplate."""
+    resource = _DEVICE_TEMPLATE
diff --git a/tackerclient/tacker/v1_0/vm/service_instance.py b/tackerclient/tacker/v1_0/vm/service_instance.py
new file mode 100644
index 00000000..b68ebcf0
--- /dev/null
+++ b/tackerclient/tacker/v1_0/vm/service_instance.py
@@ -0,0 +1,164 @@
+#
+# Copyright 2013 Intel
+# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
+#                               <isaku.yamahata at gmail com>
+# All Rights Reserved.
+#
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Isaku Yamahata, Intel
+
+from tackerclient.common import exceptions
+from tackerclient.openstack.common.gettextutils import _
+from tackerclient.tacker import v1_0 as tackerV10
+
+
+_SERVICE_INSTANCE = 'service_instance'
+
+
+class ListServiceInstance(tackerV10.ListCommand):
+    """List service instance that belong to a given tenant."""
+
+    resource = _SERVICE_INSTANCE
+
+
+class ShowServiceInstance(tackerV10.ShowCommand):
+    """show information of a given ServiceInstance."""
+
+    resource = _SERVICE_INSTANCE
+
+
+class CreateServiceInstance(tackerV10.CreateCommand):
+    """create a ServiceInstance."""
+
+    resource = _SERVICE_INSTANCE
+
+    def add_known_arguments(self, parser):
+        parser.add_argument(
+            '--name',
+            default=None,
+            help='Set a name for the devicetemplate')
+        parser.add_argument(
+            '--service-type-id',
+            required=True,
+            help='service type id to create service instance based on')
+        parser.add_argument(
+            '--service-table-id',
+            required=True,
+            help='service type id to create service instance based on')
+        parser.add_argument(
+            '--mgmt-driver',
+            default=None,
+            help='Set a manegement driver name for the service instance')
+        parser.add_argument(
+            '--service-context',
+            metavar='<network-id=network-uuid,subnet-id=subnet-uuid,'
+            'port-id=port-uuid,router-id=router-uuid,'
+            'role=role-string,index=int>',
+            action='append',
+            dest='service_context',
+            default=[],
+            help='service context to insert service')
+        parser.add_argument(
+            '--device',
+            required=True,
+            help='Set a device for the service instance to create on')
+        parser.add_argument(
+            '--kwargs',
+            metavar='<key>=<value>',
+            action='append',
+            dest='kwargs',
+            default=[],
+            help='instance specific argument')
+
+    def args2body(self, parsed_args):
+        body = {
+            self.resource: {
+                'service_type_id': parsed_args.service_type_id,
+                'service_table_id': parsed_args.service_table_id,
+                'devices': [parsed_args.device],
+            }
+        }
+        if parsed_args.name is not None:
+            body[self.resource]['name'] = parsed_args.name
+        if parsed_args.mgmt_driver is not None:
+            body[self.resource]['mgmt_driver'] = parsed_args.mgmt_driver
+        if parsed_args.kwargs:
+            try:
+                kwargs = dict(key_value.split('=', 1)
+                              for key_value in parsed_args.kwargs)
+            except ValueError:
+                msg = (_('invalid argument for --kwargs %s') %
+                       parsed_args.kwargs)
+                raise exceptions.TackerCLIError(msg)
+            if kwargs:
+                body[self.resource]['kwargs'] = kwargs
+        if parsed_args.service_context:
+            try:
+                service_context = [dict(
+                    (k.replace('-', '_'), v)
+                    for k, v in (key_value.split('=', 1)
+                                 for key_value in entry_string.split(',')))
+                    for entry_string in parsed_args.service_context]
+            except ValueError:
+                msg = (_('invalid argument for --service-context %s') %
+                       parsed_args.service_context)
+                raise exceptions.TackerCLIError(msg)
+
+            if service_context:
+                body[self.resource]['service_context'] = service_context
+
+        tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
+        return body
+
+
+class UpdateServiceInstance(tackerV10.UpdateCommand):
+    """Update a given ServiceInstance."""
+
+    resource = _SERVICE_INSTANCE
+
+    def add_known_arguments(self, parser):
+        parser.add_argument(
+            '--name',
+            help='Set a name for the devicetemplate')
+        parser.add_argument(
+            '--kwargs',
+            metavar='<key>=<value>',
+            action='append',
+            dest='kwargs',
+            default=[],
+            help='instance specific argument')
+
+    def args2body(self, parsed_args):
+        body = {self.resource: {}}
+        if parsed_args.name:
+            body[self.resource]['name'] = parsed_args.name
+        if parsed_args.kwargs:
+            try:
+                kwargs = dict(key_value.split('=', 1)
+                              for key_value in parsed_args.kwargs)
+            except ValueError:
+                msg = (_('invalid argument for --kwargs %s') %
+                       parsed_args.kwargs)
+                raise exceptions.TackerCLIError(msg)
+            if kwargs:
+                body[self.resource]['kwargs'] = kwargs
+        tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
+        return body
+
+
+class DeleteServiceInstance(tackerV10.DeleteCommand):
+    """Delete a given ServiceInstance."""
+
+    resource = _SERVICE_INSTANCE
diff --git a/tackerclient/tests/unit/test_cli20.py b/tackerclient/tests/unit/test_cli10.py
similarity index 100%
rename from tackerclient/tests/unit/test_cli20.py
rename to tackerclient/tests/unit/test_cli10.py
diff --git a/tackerclient/tests/unit/test_cli20_extensions.py b/tackerclient/tests/unit/test_cli10_extensions.py
similarity index 100%
rename from tackerclient/tests/unit/test_cli20_extensions.py
rename to tackerclient/tests/unit/test_cli10_extensions.py
diff --git a/tackerclient/tests/unit/vm/__init__.py b/tackerclient/tests/unit/vm/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tackerclient/tests/unit/vm/test_cli10_device.py b/tackerclient/tests/unit/vm/test_cli10_device.py
new file mode 100644
index 00000000..49998264
--- /dev/null
+++ b/tackerclient/tests/unit/vm/test_cli10_device.py
@@ -0,0 +1,126 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2014 Intel
+# Copyright 2014 Isaku Yamahata <isaku.yamahata at intel com>
+#                               <isaku.yamahata at gmail com>
+# All Rights Reserved.
+#
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Isaku Yamahata, Intel
+
+import sys
+
+from tackerclient.tacker.v1_0.vm import device
+from tackerclient.tests.unit import test_cli10
+
+
+class CLITestV10VmDeviceJSON(test_cli10.CLITestV10Base):
+    _RESOURCE = 'device'
+    _RESOURCES = 'devices'
+
+    def setUp(self):
+        plurals = {'devices': 'device'}
+        super(CLITestV10VmDeviceJSON, self).setUp(plurals=plurals)
+
+    def test_create_device_all_params(self):
+        cmd = device.CreateDevice(test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        template_id = 'template_id'
+        key = 'key'
+        value = 'value'
+        network_id = 'network_id'
+        subnet_id = 'subnet_id'
+        port_id = 'port_id'
+        router_id = 'router_id'
+        role = 'role'
+        index = 1
+
+        args = [
+            '--device-template-id', template_id,
+            '--kwargs', '%s=%s' % (key, value),
+            '--service-context',
+            ('network-id=%s,subnet-id=%s,port-id=%s,router-id=%s,'
+             'role=%s,index=%s' % (network_id, subnet_id, port_id, router_id,
+                                   role, index))
+        ]
+        position_names = ['template_id']
+        position_values = [template_id]
+        extra_body = {
+            'kwargs': {
+                key: value
+            },
+            'service_context': [{
+                'network_id': network_id,
+                'subnet_id': subnet_id,
+                'port_id': port_id,
+                'router_id': router_id,
+                'role': role,
+                'index': str(index),
+            }],
+        }
+        self._test_create_resource(self._RESOURCE, cmd, None, my_id,
+                                   args, position_names, position_values,
+                                   extra_body=extra_body)
+
+    def test_create_device_with_mandatory_params(self):
+        cmd = device.CreateDevice(test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        template_id = 'template_id'
+        args = [
+            '--device-template-id', template_id,
+        ]
+        position_names = ['template_id']
+        position_values = [template_id]
+        self._test_create_resource(self._RESOURCE, cmd, None, my_id,
+                                   args, position_names, position_values)
+
+    def test_list_devices(self):
+        cmd = device.ListDevice(test_cli10.MyApp(sys.stdout), None)
+        self._test_list_resources(self._RESOURCES, cmd, True)
+
+    def test_list_devices_pagenation(self):
+        cmd = device.ListDevice(test_cli10.MyApp(sys.stdout), None)
+        self._test_list_resources(self._RESOURCES, cmd, True)
+
+    def test_show_device_id(self):
+        cmd = device.ShowDevice(test_cli10.MyApp(sys.stdout), None)
+        args = ['--fields', 'id', self.test_id]
+        self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
+                                 ['id'])
+
+    def test_show_device_id_name(self):
+        cmd = device.ShowDevice(test_cli10.MyApp(sys.stdout), None)
+        args = ['--fields', 'id', '--fields', 'name', self.test_id]
+        self._test_show_resource(self._RESOURCE, cmd, self.test_id,
+                                 args, ['id', 'name'])
+
+    def test_update_device(self):
+        cmd = device.UpdateDevice(test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        key = 'new-key'
+        value = 'new-value'
+        self._test_update_resource(self._RESOURCE, cmd, my_id,
+                                   [my_id, '--kwargs', '%s=%s' % (key, value)],
+                                   {'kwargs': {key: value}})
+
+    def test_delete_device(self):
+        cmd = device.DeleteDevice(test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        args = [my_id]
+        self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
+
+
+class CLITestV10VmDeviceXML(CLITestV10VmDeviceJSON):
+    format = 'xml'
diff --git a/tackerclient/tests/unit/vm/test_cli10_device_template.py b/tackerclient/tests/unit/vm/test_cli10_device_template.py
new file mode 100644
index 00000000..389cd41b
--- /dev/null
+++ b/tackerclient/tests/unit/vm/test_cli10_device_template.py
@@ -0,0 +1,132 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2014 Intel
+# Copyright 2014 Isaku Yamahata <isaku.yamahata at intel com>
+#                               <isaku.yamahata at gmail com>
+# All Rights Reserved.
+#
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Isaku Yamahata, Intel
+
+import sys
+
+from tackerclient.tacker.v1_0.vm import device_template
+from tackerclient.tests.unit import test_cli10
+
+
+class CLITestV10VmDeviceTemplateJSON(test_cli10.CLITestV10Base):
+    _RESOURCE = 'device_template'
+    _RESOURCES = 'device_templates'
+
+    def setUp(self):
+        plurals = {'device_templates': 'device_template'}
+        super(CLITestV10VmDeviceTemplateJSON, self).setUp(plurals=plurals)
+
+    def test_create_device_template_all_params(self):
+        cmd = device_template.CreateDeviceTemplate(
+            test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        name = 'my-name'
+        description = 'my-description'
+        service_type = 'MY-SERVICE'
+        device_driver = 'device-driver'
+        mgmt_driver = 'mgmt-driver'
+        attr_key = 'attr-key'
+        attr_val = 'attr-val'
+        args = [
+            '--name', name,
+            '--description', description,
+            '--template-service-type', service_type,
+            '--device-driver', device_driver,
+            '--mgmt-driver', mgmt_driver,
+            '--attribute', attr_key, attr_val,
+        ]
+        position_names = ['name', 'description',
+                          'device_driver', 'mgmt_driver']
+        position_values = [name, description, device_driver, mgmt_driver]
+        extra_body = {
+            'service_types': [{'service_type': service_type}],
+            'attributes': {attr_key: attr_val},
+        }
+        self._test_create_resource(self._RESOURCE, cmd, None, my_id,
+                                   args, position_names, position_values,
+                                   extra_body=extra_body)
+
+    def test_create_device_template_with_mandatory_params(self):
+        cmd = device_template.CreateDeviceTemplate(
+            test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        service_type = 'MY-SERVICE'
+        device_driver = 'device-driver'
+        mgmt_driver = 'mgmt-driver'
+        args = [
+            '--template-service-type', service_type,
+            '--device-driver', device_driver,
+            '--mgmt-driver', mgmt_driver,
+        ]
+        position_names = ['device_driver', 'mgmt_driver']
+        position_values = [device_driver, mgmt_driver]
+        extra_body = {
+            'service_types': [{'service_type': service_type}],
+        }
+        self._test_create_resource(self._RESOURCE, cmd, None, my_id,
+                                   args, position_names, position_values,
+                                   extra_body=extra_body)
+
+    def test_list_device_templates(self):
+        cmd = device_template.ListDeviceTemplate(test_cli10.MyApp(sys.stdout),
+                                                 None)
+        self._test_list_resources(self._RESOURCES, cmd, True)
+
+    def test_list_device_templates_pagenation(self):
+        cmd = device_template.ListDeviceTemplate(test_cli10.MyApp(sys.stdout),
+                                                 None)
+        self._test_list_resources(self._RESOURCES, cmd, True)
+
+    def test_show_device_template_id(self):
+        cmd = device_template.ShowDeviceTemplate(test_cli10.MyApp(sys.stdout),
+                                                 None)
+        args = ['--fields', 'id', self.test_id]
+        self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
+                                 ['id'])
+
+    def test_show_device_template_id_name(self):
+        cmd = device_template.ShowDeviceTemplate(test_cli10.MyApp(sys.stdout),
+                                                 None)
+        args = ['--fields', 'id', '--fields', 'name', self.test_id]
+        self._test_show_resource(self._RESOURCE, cmd, self.test_id,
+                                 args, ['id', 'name'])
+
+    def test_update_device_template(self):
+        cmd = device_template.UpdateDeviceTemplate(
+            test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        name = 'new-name'
+        description = 'new-description'
+        self._test_update_resource(self._RESOURCE, cmd, my_id,
+                                   [my_id, '--name', name,
+                                    '--description', description],
+                                   {'name': name, 'description': description})
+
+    def test_delete_device_tempalte(self):
+        cmd = device_template.DeleteDeviceTemplate(
+            test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        args = [my_id]
+        self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
+
+
+class CLITestV10VmDeviceTemplateXML(CLITestV10VmDeviceTemplateJSON):
+    format = 'xml'
diff --git a/tackerclient/tests/unit/vm/test_cli10_service_instance.py b/tackerclient/tests/unit/vm/test_cli10_service_instance.py
new file mode 100644
index 00000000..67c6ec79
--- /dev/null
+++ b/tackerclient/tests/unit/vm/test_cli10_service_instance.py
@@ -0,0 +1,155 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2014 Intel
+# Copyright 2014 Isaku Yamahata <isaku.yamahata at intel com>
+#                               <isaku.yamahata at gmail com>
+# All Rights Reserved.
+#
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Isaku Yamahata, Intel
+
+import sys
+
+from tackerclient.tacker.v1_0.vm import service_instance
+from tackerclient.tests.unit import test_cli10
+
+
+class CLITestV10VmServiceInstanceJSON(test_cli10.CLITestV10Base):
+    _RESOURCE = 'service_instance'
+    _RESOURCES = 'service_instances'
+
+    def setUp(self):
+        plurals = {'service_instances': 'service_instance'}
+        super(CLITestV10VmServiceInstanceJSON, self).setUp(plurals=plurals)
+
+    def test_create_service_instance_all_params(self):
+        cmd = service_instance.CreateServiceInstance(
+            test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        name = 'my-name'
+        service_type_id = 'service-type-id'
+        service_table_id = 'service-table-id'
+        mgmt_driver = 'mgmt-driver'
+        network_id = 'network_id'
+        subnet_id = 'subnet_id'
+        port_id = 'port_id'
+        router_id = 'router_id'
+        role = 'role'
+        index = 1
+
+        device = 'my-device'
+
+        key = 'key'
+        value = 'value'
+
+        args = [
+            '--name', name,
+            '--service-type-id', service_type_id,
+            '--service-table-id', service_table_id,
+            '--mgmt-driver', mgmt_driver,
+            '--service-context',
+            ('network-id=%s,subnet-id=%s,port-id=%s,router-id=%s,'
+             'role=%s,index=%s' % (network_id, subnet_id, port_id, router_id,
+                                   role, index)),
+            '--device', device,
+            '--kwargs', '%s=%s' % (key, value),
+        ]
+        position_names = ['name', 'service_type_id', 'service_table_id',
+                          'mgmt_driver']
+        position_values = [name, service_type_id, service_table_id,
+                           mgmt_driver]
+        extra_body = {
+            'devices': [device],
+            'service_context': [{
+                'network_id': network_id,
+                'subnet_id': subnet_id,
+                'port_id': port_id,
+                'router_id': router_id,
+                'role': role,
+                'index': str(index),
+            }],
+            'kwargs': {
+                key: value
+            },
+        }
+        self._test_create_resource(self._RESOURCE, cmd, None, my_id,
+                                   args, position_names, position_values,
+                                   extra_body=extra_body)
+
+    def test_create_service_instance_with_mandatory_params(self):
+        cmd = service_instance.CreateServiceInstance(
+            test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        service_type_id = 'service-type-id'
+        service_table_id = 'service-table-id'
+        device = 'my-device'
+        args = [
+            '--service-type-id', service_type_id,
+            '--service-table-id', service_table_id,
+            '--device', device,
+        ]
+        position_names = ['service_type_id', 'service_table_id']
+        position_values = [service_type_id, service_table_id]
+        extra_body = {
+            'devices': [device],
+        }
+        self._test_create_resource(self._RESOURCE, cmd, None, my_id,
+                                   args, position_names, position_values,
+                                   extra_body=extra_body)
+
+    def test_list_service_instances(self):
+        cmd = service_instance.ListServiceInstance(
+            test_cli10.MyApp(sys.stdout), None)
+        self._test_list_resources(self._RESOURCES, cmd, True)
+
+    def test_list_service_instances_pagenation(self):
+        cmd = service_instance.ListServiceInstance(
+            test_cli10.MyApp(sys.stdout), None)
+        self._test_list_resources(self._RESOURCES, cmd, True)
+
+    def test_show_service_instance_id(self):
+        cmd = service_instance.ShowServiceInstance(
+            test_cli10.MyApp(sys.stdout), None)
+        args = ['--fields', 'id', self.test_id]
+        self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
+                                 ['id'])
+
+    def test_show_service_instance_id_name(self):
+        cmd = service_instance.ShowServiceInstance(
+            test_cli10.MyApp(sys.stdout), None)
+        args = ['--fields', 'id', '--fields', 'name', self.test_id]
+        self._test_show_resource(self._RESOURCE, cmd, self.test_id,
+                                 args, ['id', 'name'])
+
+    def test_update_service_instance(self):
+        cmd = service_instance.UpdateServiceInstance(
+            test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        key = 'new-key'
+        value = 'new-value'
+        self._test_update_resource(self._RESOURCE, cmd, my_id,
+                                   [my_id, '--kwargs', '%s=%s' % (key, value)],
+                                   {'kwargs': {key: value}})
+
+    def test_delete_service_instance(self):
+        cmd = service_instance.DeleteServiceInstance(
+            test_cli10.MyApp(sys.stdout), None)
+        my_id = 'my-id'
+        args = [my_id]
+        self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
+
+
+class CLITestV10VmServiceInstanceXML(CLITestV10VmServiceInstanceJSON):
+    format = 'xml'
diff --git a/tackerclient/v1_0/client.py b/tackerclient/v1_0/client.py
new file mode 100644
index 00000000..b10a57ce
--- /dev/null
+++ b/tackerclient/v1_0/client.py
@@ -0,0 +1,411 @@
+# Copyright 2012 OpenStack Foundation.
+# All Rights Reserved
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+
+import logging
+import time
+import urllib
+
+import requests
+import six.moves.urllib.parse as urlparse
+
+from tackerclient import client
+from tackerclient.common import _
+from tackerclient.common import constants
+from tackerclient.common import exceptions
+from tackerclient.common import serializer
+from tackerclient.common import utils
+
+
+_logger = logging.getLogger(__name__)
+
+
+def exception_handler_v10(status_code, error_content):
+    """Exception handler for API v1.0 client
+
+       This routine generates the appropriate
+       Tacker exception according to the contents of the
+       response body
+
+       :param status_code: HTTP error status code
+       :param error_content: deserialized body of error response
+    """
+    error_dict = None
+    if isinstance(error_content, dict):
+        error_dict = error_content.get('TackerError')
+    # Find real error type
+    bad_tacker_error_flag = False
+    if error_dict:
+        # If Tacker key is found, it will definitely contain
+        # a 'message' and 'type' keys?
+        try:
+            error_type = error_dict['type']
+            error_message = error_dict['message']
+            if error_dict['detail']:
+                error_message += "\n" + error_dict['detail']
+        except Exception:
+            bad_tacker_error_flag = True
+        if not bad_tacker_error_flag:
+            # If corresponding exception is defined, use it.
+            client_exc = getattr(exceptions, '%sClient' % error_type, None)
+            # Otherwise look up per status-code client exception
+            if not client_exc:
+                client_exc = exceptions.HTTP_EXCEPTION_MAP.get(status_code)
+            if client_exc:
+                raise client_exc(message=error_message,
+                                 status_code=status_code)
+            else:
+                raise exceptions.TackerClientException(
+                    status_code=status_code, message=error_message)
+        else:
+            raise exceptions.TackerClientException(status_code=status_code,
+                                                   message=error_dict)
+    else:
+        message = None
+        if isinstance(error_content, dict):
+            message = error_content.get('message')
+        if message:
+            raise exceptions.TackerClientException(status_code=status_code,
+                                                   message=message)
+
+    # If we end up here the exception was not a tacker error
+    msg = "%s-%s" % (status_code, error_content)
+    raise exceptions.TackerClientException(status_code=status_code,
+                                           message=msg)
+
+
+class APIParamsCall(object):
+    """A Decorator to add support for format and tenant overriding
+       and filters
+    """
+    def __init__(self, function):
+        self.function = function
+
+    def __get__(self, instance, owner):
+        def with_params(*args, **kwargs):
+            _format = instance.format
+            if 'format' in kwargs:
+                instance.format = kwargs['format']
+            ret = self.function(instance, *args, **kwargs)
+            instance.format = _format
+            return ret
+        return with_params
+
+
+class Client(object):
+    """Client for the OpenStack Tacker v1.0 API.
+
+    :param string username: Username for authentication. (optional)
+    :param string user_id: User ID for authentication. (optional)
+    :param string password: Password for authentication. (optional)
+    :param string token: Token for authentication. (optional)
+    :param string tenant_name: Tenant name. (optional)
+    :param string tenant_id: Tenant id. (optional)
+    :param string auth_url: Keystone service endpoint for authorization.
+    :param string service_type: Network service type to pull from the
+                                keystone catalog (e.g. 'network') (optional)
+    :param string endpoint_type: Network service endpoint type to pull from the
+                                 keystone catalog (e.g. 'publicURL',
+                                 'internalURL', or 'adminURL') (optional)
+    :param string region_name: Name of a region to select when choosing an
+                               endpoint from the service catalog.
+    :param string endpoint_url: A user-supplied endpoint URL for the tacker
+                            service.  Lazy-authentication is possible for API
+                            service calls if endpoint is set at
+                            instantiation.(optional)
+    :param integer timeout: Allows customization of the timeout for client
+                            http requests. (optional)
+    :param bool insecure: SSL certificate validation. (optional)
+    :param string ca_cert: SSL CA bundle file to use. (optional)
+
+    Example::
+
+        from tackerclient.v1_0 import client
+        tacker = client.Client(username=USER,
+                                password=PASS,
+                                tenant_name=TENANT_NAME,
+                                auth_url=KEYSTONE_URL)
+
+        nets = tacker.list_networks()
+        ...
+
+    """
+
+    extensions_path = "/extensions"
+    extension_path = "/extensions/%s"
+
+    device_templates_path = '/device-templates'
+    device_template_path = '/device-templates/%s'
+    service_instances_path = '/service-instances'
+    service_instance_path = '/service-instances/%s'
+    devices_path = '/devices'
+    device_path = '/devices/%s'
+
+    # API has no way to report plurals, so we have to hard code them
+    EXTED_PLURALS = {}
+    # 8192 Is the default max URI len for eventlet.wsgi.server
+    MAX_URI_LEN = 8192
+
+    def get_attr_metadata(self):
+        if self.format == 'json':
+            return {}
+        old_request_format = self.format
+        self.format = 'json'
+        exts = self.list_extensions()['extensions']
+        self.format = old_request_format
+        ns = dict([(ext['alias'], ext['namespace']) for ext in exts])
+        self.EXTED_PLURALS.update(constants.PLURALS)
+        return {'plurals': self.EXTED_PLURALS,
+                'xmlns': constants.XML_NS_V10,
+                constants.EXT_NS: ns}
+
+    @APIParamsCall
+    def list_extensions(self, **_params):
+        """Fetch a list of all exts on server side."""
+        return self.get(self.extensions_path, params=_params)
+
+    @APIParamsCall
+    def show_extension(self, ext_alias, **_params):
+        """Fetch a list of all exts on server side."""
+        return self.get(self.extension_path % ext_alias, params=_params)
+
+    def list_device_templates(self, retrieve_all=True, **_params):
+        return self.list('device_templates', self.device_templates_path,
+                         retrieve_all, **_params)
+
+    @APIParamsCall
+    def show_device_template(self, device_template, **_params):
+        return self.get(self.device_template_path % device_template,
+                        params=_params)
+
+    @APIParamsCall
+    def update_device_template(self, device_template, body=None):
+        return self.put(self.device_template_path % device_template, body=body)
+
+    @APIParamsCall
+    def create_device_template(self, body=None):
+        return self.post(self.device_templates_path, body=body)
+
+    @APIParamsCall
+    def delete_device_template(self, device_template):
+        return self.delete(self.device_template_path % device_template)
+
+    @APIParamsCall
+    def list_service_instances(self, retrieve_all=True, **_params):
+        return self.list('service_instances', self.service_instances_path,
+                         retrieve_all, **_params)
+
+    @APIParamsCall
+    def show_service_instance(self, service_instance, **_params):
+        return self.get(self.service_instance_path % service_instance,
+                        params=_params)
+
+    @APIParamsCall
+    def update_service_instance(self, service_instance, body=None):
+        return self.put(self.service_instance_path % service_instance,
+                        body=body)
+
+    @APIParamsCall
+    def create_service_instance(self, body=None):
+        return self.post(self.service_instances_path, body=body)
+
+    @APIParamsCall
+    def delete_service_instance(self, service_instance):
+        return self.delete(self.service_instance_path % service_instance)
+
+    @APIParamsCall
+    def list_devices(self, retrieve_all=True, **_params):
+        return self.list('devices', self.devices_path, retrieve_all, **_params)
+
+    @APIParamsCall
+    def show_device(self, device, **_params):
+        return self.get(self.device_path % device, params=_params)
+
+    @APIParamsCall
+    def update_device(self, device, body=None):
+        return self.put(self.device_path % device, body=body)
+
+    @APIParamsCall
+    def create_device(self, body=None):
+        return self.post(self.devices_path, body=body)
+
+    @APIParamsCall
+    def delete_device(self, device):
+        return self.delete(self.device_path % device)
+
+    def __init__(self, **kwargs):
+        """Initialize a new client for the Tacker v1.0 API."""
+        super(Client, self).__init__()
+        self.httpclient = client.HTTPClient(**kwargs)
+        self.version = '1.0'
+        self.format = 'json'
+        self.action_prefix = "/v%s" % (self.version)
+        self.retries = 0
+        self.retry_interval = 1
+
+    def _handle_fault_response(self, status_code, response_body):
+        # Create exception with HTTP status code and message
+        _logger.debug(_("Error message: %s"), response_body)
+        # Add deserialized error message to exception arguments
+        try:
+            des_error_body = self.deserialize(response_body, status_code)
+        except Exception:
+            # If unable to deserialized body it is probably not a
+            # Tacker error
+            des_error_body = {'message': response_body}
+        # Raise the appropriate exception
+        exception_handler_v10(status_code, des_error_body)
+
+    def _check_uri_length(self, action):
+        uri_len = len(self.httpclient.endpoint_url) + len(action)
+        if uri_len > self.MAX_URI_LEN:
+            raise exceptions.RequestURITooLong(
+                excess=uri_len - self.MAX_URI_LEN)
+
+    def do_request(self, method, action, body=None, headers=None, params=None):
+        # Add format and tenant_id
+        action += ".%s" % self.format
+        action = self.action_prefix + action
+        if type(params) is dict and params:
+            params = utils.safe_encode_dict(params)
+            action += '?' + urllib.urlencode(params, doseq=1)
+        # Ensure client always has correct uri - do not guesstimate anything
+        self.httpclient.authenticate_and_fetch_endpoint_url()
+        self._check_uri_length(action)
+
+        if body:
+            body = self.serialize(body)
+        self.httpclient.content_type = self.content_type()
+        resp, replybody = self.httpclient.do_request(action, method, body=body)
+        status_code = self.get_status_code(resp)
+        if status_code in (requests.codes.ok,
+                           requests.codes.created,
+                           requests.codes.accepted,
+                           requests.codes.no_content):
+            return self.deserialize(replybody, status_code)
+        else:
+            if not replybody:
+                replybody = resp.reason
+            self._handle_fault_response(status_code, replybody)
+
+    def get_auth_info(self):
+        return self.httpclient.get_auth_info()
+
+    def get_status_code(self, response):
+        """Returns the integer status code from the response.
+
+        Either a Webob.Response (used in testing) or requests.Response
+        is returned.
+        """
+        if hasattr(response, 'status_int'):
+            return response.status_int
+        else:
+            return response.status_code
+
+    def serialize(self, data):
+        """Serializes a dictionary into either xml or json.
+
+        A dictionary with a single key can be passed and
+        it can contain any structure.
+        """
+        if data is None:
+            return None
+        elif type(data) is dict:
+            return serializer.Serializer(
+                self.get_attr_metadata()).serialize(data, self.content_type())
+        else:
+            raise Exception(_("Unable to serialize object of type = '%s'") %
+                            type(data))
+
+    def deserialize(self, data, status_code):
+        """Deserializes an xml or json string into a dictionary."""
+        if status_code == 204:
+            return data
+        return serializer.Serializer(self.get_attr_metadata()).deserialize(
+            data, self.content_type())['body']
+
+    def content_type(self, _format=None):
+        """Returns the mime-type for either 'xml' or 'json'.
+
+        Defaults to the currently set format.
+        """
+        _format = _format or self.format
+        return "application/%s" % (_format)
+
+    def retry_request(self, method, action, body=None,
+                      headers=None, params=None):
+        """Call do_request with the default retry configuration.
+
+        Only idempotent requests should retry failed connection attempts.
+        :raises: ConnectionFailed if the maximum # of retries is exceeded
+        """
+        max_attempts = self.retries + 1
+        for i in range(max_attempts):
+            try:
+                return self.do_request(method, action, body=body,
+                                       headers=headers, params=params)
+            except exceptions.ConnectionFailed:
+                # Exception has already been logged by do_request()
+                if i < self.retries:
+                    _logger.debug(_('Retrying connection to Tacker service'))
+                    time.sleep(self.retry_interval)
+
+        raise exceptions.ConnectionFailed(reason=_("Maximum attempts reached"))
+
+    def delete(self, action, body=None, headers=None, params=None):
+        return self.retry_request("DELETE", action, body=body,
+                                  headers=headers, params=params)
+
+    def get(self, action, body=None, headers=None, params=None):
+        return self.retry_request("GET", action, body=body,
+                                  headers=headers, params=params)
+
+    def post(self, action, body=None, headers=None, params=None):
+        # Do not retry POST requests to avoid the orphan objects problem.
+        return self.do_request("POST", action, body=body,
+                               headers=headers, params=params)
+
+    def put(self, action, body=None, headers=None, params=None):
+        return self.retry_request("PUT", action, body=body,
+                                  headers=headers, params=params)
+
+    def list(self, collection, path, retrieve_all=True, **params):
+        if retrieve_all:
+            res = []
+            for r in self._pagination(collection, path, **params):
+                res.extend(r[collection])
+            return {collection: res}
+        else:
+            return self._pagination(collection, path, **params)
+
+    def _pagination(self, collection, path, **params):
+        if params.get('page_reverse', False):
+            linkrel = 'previous'
+        else:
+            linkrel = 'next'
+        next = True
+        while next:
+            res = self.get(path, params=params)
+            yield res
+            next = False
+            try:
+                for link in res['%s_links' % collection]:
+                    if link['rel'] == linkrel:
+                        query_str = urlparse.urlparse(link['href']).query
+                        params = urlparse.parse_qs(query_str)
+                        next = True
+                        break
+            except KeyError:
+                break