implement servicevm related command

Change-Id: Ib606eb3e4721ec9a255092bf4581f3b576c3714a
This commit is contained in:
Isaku Yamahata 2014-06-27 18:07:43 +09:00
parent ffa7a582e5
commit 87a2257477
12 changed files with 1260 additions and 229 deletions

View File

@ -15,7 +15,7 @@
# #
""" """
Command-line interface to the Neutron APIs Command-line interface to the Tacker APIs
""" """
from __future__ import print_function from __future__ import print_function
@ -28,46 +28,20 @@ import sys
from cliff import app from cliff import app
from cliff import commandmanager from cliff import commandmanager
from neutronclient.common import clientmanager from tackerclient.common import clientmanager
from neutronclient.common import exceptions as exc from tackerclient.common import exceptions as exc
from neutronclient.common import utils from tackerclient.common import utils
from neutronclient.neutron.v2_0 import agent from tackerclient.openstack.common.gettextutils import _
from neutronclient.neutron.v2_0 import agentscheduler from tackerclient.openstack.common import strutils
from neutronclient.neutron.v2_0 import credential from tackerclient.tacker.v1_0 import extension
from neutronclient.neutron.v2_0 import extension from tackerclient.tacker.v1_0.vm import device
from neutronclient.neutron.v2_0 import floatingip from tackerclient.tacker.v1_0.vm import device_template
from neutronclient.neutron.v2_0.fw import firewall from tackerclient.tacker.v1_0.vm import service_instance
from neutronclient.neutron.v2_0.fw import firewallpolicy from tackerclient.version import __version__
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__
VERSION = '2.0' VERSION = '1.0'
NEUTRON_API_VERSION = '2.0' TACKER_API_VERSION = '1.0'
def run_command(cmd, cmd_parser, sub_argv): def run_command(cmd, cmd_parser, sub_argv):
@ -97,189 +71,27 @@ def env(*_vars, **kwargs):
return kwargs.get('default', '') return kwargs.get('default', '')
COMMAND_V2 = { COMMAND_V1 = {
'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,
'ext-list': extension.ListExt, 'ext-list': extension.ListExt,
'ext-show': extension.ShowExt, 'ext-show': extension.ShowExt,
'router-list': router.ListRouter, 'device-template-create': device_template.CreateDeviceTemplate,
'router-port-list': port.ListRouterPort, 'device-template-list': device_template.ListDeviceTemplate,
'router-show': router.ShowRouter, 'device-template-show': device_template.ShowDeviceTemplate,
'router-create': router.CreateRouter, 'device-template-update': device_template.UpdateDeviceTemplate,
'router-delete': router.DeleteRouter, 'device-template-delete': device_template.DeleteDeviceTemplate,
'router-update': router.UpdateRouter, 'service-instance-create': service_instance.CreateServiceInstance,
'router-interface-add': router.AddInterfaceRouter, 'service-instance-list': service_instance.ListServiceInstance,
'router-interface-delete': router.RemoveInterfaceRouter, 'service-instance-show': service_instance.ShowServiceInstance,
'router-gateway-set': router.SetGatewayRouter, 'service-instance-update': service_instance.UpdateServiceInstance,
'router-gateway-clear': router.RemoveGatewayRouter, 'service-instance-delete': service_instance.DeleteServiceInstance,
'floatingip-list': floatingip.ListFloatingIP, 'device-create': device.CreateDevice,
'floatingip-show': floatingip.ShowFloatingIP, 'device-list': device.ListDevice,
'floatingip-create': floatingip.CreateFloatingIP, 'device-show': device.ShowDevice,
'floatingip-delete': floatingip.DeleteFloatingIP, 'device-update': device.UpdateDevice,
'floatingip-associate': floatingip.AssociateFloatingIP, 'device-delete': device.DeleteDevice,
'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,
} }
COMMANDS = {'2.0': COMMAND_V2} COMMANDS = {'1.0': COMMAND_V1}
class HelpAction(argparse.Action): class HelpAction(argparse.Action):
@ -307,7 +119,7 @@ class HelpAction(argparse.Action):
sys.exit(0) sys.exit(0)
class NeutronShell(app.App): class TackerShell(app.App):
# verbose logging levels # verbose logging levels
WARNING_LEVEL = 0 WARNING_LEVEL = 0
@ -318,10 +130,10 @@ class NeutronShell(app.App):
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def __init__(self, apiversion): def __init__(self, apiversion):
super(NeutronShell, self).__init__( super(TackerShell, self).__init__(
description=__doc__.strip(), description=__doc__.strip(),
version=VERSION, version=VERSION,
command_manager=commandmanager.CommandManager('neutron.cli'), ) command_manager=commandmanager.CommandManager('tacker.cli'), )
self.commands = COMMANDS self.commands = COMMANDS
for k, v in self.commands[apiversion].items(): for k, v in self.commands[apiversion].items():
self.command_manager.add_command(k, v) self.command_manager.add_command(k, v)
@ -466,8 +278,8 @@ class NeutronShell(app.App):
parser.add_argument( parser.add_argument(
'--insecure', '--insecure',
action='store_true', action='store_true',
default=env('NEUTRONCLIENT_INSECURE', default=False), default=env('TACKERCLIENT_INSECURE', default=False),
help=_("Explicitly allow neutronclient to perform \"insecure\" " help=_("Explicitly allow tackerclient to perform \"insecure\" "
"SSL (https) requests. The server's certificate will " "SSL (https) requests. The server's certificate will "
"not be verified against any certificate authorities. " "not be verified against any certificate authorities. "
"This option should be used with caution.")) "This option should be used with caution."))
@ -650,7 +462,7 @@ class NeutronShell(app.App):
* validate authentication info * validate authentication info
""" """
super(NeutronShell, self).initialize_app(argv) super(TackerShell, self).initialize_app(argv)
self.api_version = {'network': self.api_version} self.api_version = {'network': self.api_version}
@ -693,9 +505,9 @@ class NeutronShell(app.App):
def main(argv=sys.argv[1:]): def main(argv=sys.argv[1:]):
try: try:
return NeutronShell(NEUTRON_API_VERSION).run(map(strutils.safe_decode, return TackerShell(TACKER_API_VERSION).run(map(strutils.safe_decode,
argv)) argv))
except exc.NeutronClientException: except exc.TackerClientException:
return 1 return 1
except Exception as e: except Exception as e:
print(unicode(e)) print(unicode(e))

View File

View File

@ -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

View File

@ -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

View File

@ -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

View File

View File

@ -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'

View File

@ -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'

View File

@ -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'

411
tackerclient/v1_0/client.py Normal file
View File

@ -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