Merge "BGP Dynamic Routing: neutronclient changes"

This commit is contained in:
Jenkins 2016-02-29 19:09:09 +00:00 committed by Gerrit Code Review
commit 82ec2abb22
11 changed files with 1231 additions and 0 deletions

View File

@ -0,0 +1,117 @@
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
# 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.
#
from __future__ import print_function
from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronV20
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
def add_common_args(parser):
parser.add_argument('dragent_id',
metavar='BGP_DRAGENT_ID',
help=_('ID of the Dynamic Routing agent.'))
parser.add_argument('bgp_speaker',
metavar='BGP_SPEAKER',
help=_('ID or name of the BGP speaker.'))
class AddBGPSpeakerToDRAgent(neutronV20.NeutronCommand):
"""Add a BGP speaker to a Dynamic Routing agent."""
def get_parser(self, prog_name):
parser = super(AddBGPSpeakerToDRAgent, self).get_parser(prog_name)
add_common_args(parser)
return parser
def take_action(self, parsed_args):
neutron_client = self.get_client()
_speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
parsed_args.bgp_speaker)
neutron_client.add_bgp_speaker_to_dragent(
parsed_args.dragent_id, {'bgp_speaker_id': _speaker_id})
print(_('Associated BGP speaker %s to the Dynamic Routing agent.')
% parsed_args.bgp_speaker, file=self.app.stdout)
class RemoveBGPSpeakerFromDRAgent(neutronV20.NeutronCommand):
"""Removes a BGP speaker from a Dynamic Routing agent."""
def get_parser(self, prog_name):
parser = super(RemoveBGPSpeakerFromDRAgent, self).get_parser(
prog_name)
add_common_args(parser)
return parser
def take_action(self, parsed_args):
neutron_client = self.get_client()
_speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
parsed_args.bgp_speaker)
neutron_client.remove_bgp_speaker_from_dragent(parsed_args.dragent_id,
_speaker_id)
print(_('Disassociated BGP speaker %s from the '
'Dynamic Routing agent.')
% parsed_args.bgp_speaker, file=self.app.stdout)
class ListBGPSpeakersOnDRAgent(neutronV20.ListCommand):
"""List BGP speakers hosted by a Dynamic Routing agent."""
list_columns = ['id', 'name', 'local_as', 'ip_version']
resource = 'bgp_speaker'
def get_parser(self, prog_name):
parser = super(ListBGPSpeakersOnDRAgent,
self).get_parser(prog_name)
parser.add_argument(
'dragent_id',
metavar='BGP_DRAGENT_ID',
help=_('ID of the Dynamic Routing agent.'))
return parser
def call_server(self, neutron_client, search_opts, parsed_args):
data = neutron_client.list_bgp_speaker_on_dragent(
parsed_args.dragent_id, **search_opts)
return data
class ListDRAgentsHostingBGPSpeaker(neutronV20.ListCommand):
"""List Dynamic Routing agents hosting a BGP speaker."""
resource = 'agent'
_formatters = {}
list_columns = ['id', 'host', 'admin_state_up', 'alive']
unknown_parts_flag = False
def get_parser(self, prog_name):
parser = super(ListDRAgentsHostingBGPSpeaker,
self).get_parser(prog_name)
parser.add_argument('bgp_speaker',
metavar='BGP_SPEAKER',
help=_('ID or name of the BGP speaker.'))
return parser
def extend_list(self, data, parsed_args):
for agent in data:
agent['alive'] = ":-)" if agent['alive'] else 'xxx'
def call_server(self, neutron_client, search_opts, parsed_args):
_speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
parsed_args.bgp_speaker)
search_opts['bgp_speaker'] = _speaker_id
data = neutron_client.list_dragents_hosting_bgp_speaker(**search_opts)
return data

View File

@ -0,0 +1,127 @@
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
# 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.
#
from neutronclient._i18n import _
from neutronclient.common import exceptions
from neutronclient.common import utils
from neutronclient.common import validators
from neutronclient.neutron import v2_0 as neutronv20
def get_bgp_peer_id(client, id_or_name):
return neutronv20.find_resourceid_by_name_or_id(client,
'bgp_peer',
id_or_name)
def validate_peer_attributes(parsed_args):
# Validate AS number
validators.validate_int_range(parsed_args, 'remote_as',
neutronv20.bgp.speaker.MIN_AS_NUM,
neutronv20.bgp.speaker.MAX_AS_NUM)
# Validate password
if parsed_args.auth_type != 'none' and parsed_args.password is None:
raise exceptions.CommandError(_('Must provide password if auth-type '
'is specified.'))
if parsed_args.auth_type == 'none' and parsed_args.password:
raise exceptions.CommandError(_('Must provide auth-type if password '
'is specified.'))
class ListPeers(neutronv20.ListCommand):
"""List BGP peers."""
resource = 'bgp_peer'
list_columns = ['id', 'name', 'peer_ip', 'remote_as']
pagination_support = True
sorting_support = True
class ShowPeer(neutronv20.ShowCommand):
"""Show information of a given BGP peer."""
resource = 'bgp_peer'
class CreatePeer(neutronv20.CreateCommand):
"""Create a BGP Peer."""
resource = 'bgp_peer'
def add_known_arguments(self, parser):
parser.add_argument(
'name',
metavar='NAME',
help=_('Name of the BGP peer to create.'))
parser.add_argument(
'--peer-ip',
metavar='PEER_IP_ADDRESS',
required=True,
help=_('Peer IP address.'))
parser.add_argument(
'--remote-as',
required=True,
metavar='PEER_REMOTE_AS',
help=_('Peer AS number. (Integer in [%(min_val)s, %(max_val)s] '
'is allowed.)') %
{'min_val': neutronv20.bgp.speaker.MIN_AS_NUM,
'max_val': neutronv20.bgp.speaker.MAX_AS_NUM})
parser.add_argument(
'--auth-type',
metavar='PEER_AUTH_TYPE',
choices=['none', 'md5'],
default='none',
type=utils.convert_to_lowercase,
help=_('Authentication algorithm. Supported algorithms: '
'none(default), md5'))
parser.add_argument(
'--password',
metavar='AUTH_PASSWORD',
help=_('Authentication password.'))
def args2body(self, parsed_args):
body = {}
validate_peer_attributes(parsed_args)
neutronv20.update_dict(parsed_args, body,
['name', 'peer_ip',
'remote_as', 'auth_type', 'password'])
return {self.resource: body}
class UpdatePeer(neutronv20.UpdateCommand):
"""Update BGP Peer's information."""
resource = 'bgp_peer'
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help=_('Updated name of the BGP peer.'))
parser.add_argument(
'--password',
metavar='AUTH_PASSWORD',
help=_('Updated authentication password.'))
def args2body(self, parsed_args):
body = {}
neutronv20.update_dict(parsed_args, body, ['name', 'password'])
return {self.resource: body}
class DeletePeer(neutronv20.DeleteCommand):
"""Delete a BGP peer."""
resource = 'bgp_peer'

View File

@ -0,0 +1,277 @@
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
# 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.
#
from __future__ import print_function
from neutronclient._i18n import _
from neutronclient.common import utils
from neutronclient.common import validators
from neutronclient.neutron import v2_0 as neutronv20
from neutronclient.neutron.v2_0.bgp import peer as bgp_peer
# Allowed BGP Autonomous number range
MIN_AS_NUM = 1
MAX_AS_NUM = 65535
def get_network_id(client, id_or_name):
return neutronv20.find_resourceid_by_name_or_id(client,
'network',
id_or_name)
def get_bgp_speaker_id(client, id_or_name):
return neutronv20.find_resourceid_by_name_or_id(client,
'bgp_speaker',
id_or_name)
def validate_speaker_attributes(parsed_args):
# Validate AS number
validators.validate_int_range(parsed_args, 'local_as',
MIN_AS_NUM, MAX_AS_NUM)
def add_common_arguments(parser):
utils.add_boolean_argument(
parser, '--advertise-floating-ip-host-routes',
help=_('Whether to enable or disable the advertisement '
'of floating-ip host routes by the BGP speaker. '
'By default floating ip host routes will be '
'advertised by the BGP speaker.'))
utils.add_boolean_argument(
parser, '--advertise-tenant-networks',
help=_('Whether to enable or disable the advertisement '
'of tenant network routes by the BGP speaker. '
'By default tenant network routes will be '
'advertised by the BGP speaker.'))
def args2body_common_arguments(body, parsed_args):
neutronv20.update_dict(parsed_args, body,
['name',
'advertise_floating_ip_host_routes',
'advertise_tenant_networks'])
class ListSpeakers(neutronv20.ListCommand):
"""List BGP speakers."""
resource = 'bgp_speaker'
list_columns = ['id', 'name', 'local_as', 'ip_version']
pagination_support = True
sorting_support = True
class ShowSpeaker(neutronv20.ShowCommand):
"""Show information of a given BGP speaker."""
resource = 'bgp_speaker'
class CreateSpeaker(neutronv20.CreateCommand):
"""Create a BGP Speaker."""
resource = 'bgp_speaker'
def add_known_arguments(self, parser):
parser.add_argument(
'name',
metavar='NAME',
help=_('Name of the BGP speaker to create.'))
parser.add_argument(
'--local-as',
metavar='LOCAL_AS',
required=True,
help=_('Local AS number. (Integer in [%(min_val)s, %(max_val)s] '
'is allowed.)') % {'min_val': MIN_AS_NUM,
'max_val': MAX_AS_NUM})
parser.add_argument(
'--ip-version',
type=int, choices=[4, 6],
default=4,
help=_('IP version for the BGP speaker (default is 4).'))
add_common_arguments(parser)
def args2body(self, parsed_args):
body = {}
validate_speaker_attributes(parsed_args)
body['local_as'] = parsed_args.local_as
body['ip_version'] = parsed_args.ip_version
args2body_common_arguments(body, parsed_args)
return {self.resource: body}
class UpdateSpeaker(neutronv20.UpdateCommand):
"""Update BGP Speaker's information."""
resource = 'bgp_speaker'
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help=_('Name of the BGP speaker to update.'))
add_common_arguments(parser)
def args2body(self, parsed_args):
body = {}
args2body_common_arguments(body, parsed_args)
return {self.resource: body}
class DeleteSpeaker(neutronv20.DeleteCommand):
"""Delete a BGP speaker."""
resource = 'bgp_speaker'
class AddPeerToSpeaker(neutronv20.NeutronCommand):
"""Add a peer to the BGP speaker."""
def get_parser(self, prog_name):
parser = super(AddPeerToSpeaker, self).get_parser(prog_name)
parser.add_argument(
'bgp_speaker',
metavar='BGP_SPEAKER',
help=_('ID or name of the BGP speaker.'))
parser.add_argument(
'bgp_peer',
metavar='BGP_PEER',
help=_('ID or name of the BGP peer to add.'))
return parser
def run(self, parsed_args):
neutron_client = self.get_client()
_speaker_id = get_bgp_speaker_id(neutron_client,
parsed_args.bgp_speaker)
_peer_id = bgp_peer.get_bgp_peer_id(neutron_client,
parsed_args.bgp_peer)
neutron_client.add_peer_to_bgp_speaker(_speaker_id,
{'bgp_peer_id': _peer_id})
print(_('Added BGP peer %(peer)s to BGP speaker %(speaker)s.') %
{'peer': parsed_args.bgp_peer,
'speaker': parsed_args.bgp_speaker},
file=self.app.stdout)
class RemovePeerFromSpeaker(neutronv20.NeutronCommand):
"""Remove a peer from the BGP speaker."""
def get_parser(self, prog_name):
parser = super(RemovePeerFromSpeaker, self).get_parser(prog_name)
parser.add_argument(
'bgp_speaker',
metavar='BGP_SPEAKER',
help=_('ID or name of the BGP speaker.'))
parser.add_argument(
'bgp_peer',
metavar='BGP_PEER',
help=_('ID or name of the BGP peer to remove.'))
return parser
def run(self, parsed_args):
neutron_client = self.get_client()
_speaker_id = get_bgp_speaker_id(neutron_client,
parsed_args.bgp_speaker)
_peer_id = bgp_peer.get_bgp_peer_id(neutron_client,
parsed_args.bgp_peer)
neutron_client.remove_peer_from_bgp_speaker(_speaker_id,
{'bgp_peer_id': _peer_id})
print(_('Removed BGP peer %(peer)s from BGP speaker %(speaker)s.') %
{'peer': parsed_args.bgp_peer,
'speaker': parsed_args.bgp_speaker},
file=self.app.stdout)
class AddNetworkToSpeaker(neutronv20.NeutronCommand):
"""Add a network to the BGP speaker."""
def get_parser(self, prog_name):
parser = super(AddNetworkToSpeaker, self).get_parser(prog_name)
parser.add_argument(
'bgp_speaker',
metavar='BGP_SPEAKER',
help=_('ID or name of the BGP speaker.'))
parser.add_argument(
'network',
metavar='NETWORK',
help=_('ID or name of the network to add.'))
return parser
def run(self, parsed_args):
neutron_client = self.get_client()
_speaker_id = get_bgp_speaker_id(neutron_client,
parsed_args.bgp_speaker)
_net_id = get_network_id(neutron_client,
parsed_args.network)
neutron_client.add_network_to_bgp_speaker(_speaker_id,
{'network_id': _net_id})
print(_('Added network %(net)s to BGP speaker %(speaker)s.') %
{'net': parsed_args.network, 'speaker': parsed_args.bgp_speaker},
file=self.app.stdout)
class RemoveNetworkFromSpeaker(neutronv20.NeutronCommand):
"""Remove a network from the BGP speaker."""
def get_parser(self, prog_name):
parser = super(RemoveNetworkFromSpeaker, self).get_parser(prog_name)
parser.add_argument(
'bgp_speaker',
metavar='BGP_SPEAKER',
help=_('ID or name of the BGP speaker.'))
parser.add_argument(
'network',
metavar='NETWORK',
help=_('ID or name of the network to remove.'))
return parser
def run(self, parsed_args):
neutron_client = self.get_client()
_speaker_id = get_bgp_speaker_id(neutron_client,
parsed_args.bgp_speaker)
_net_id = get_network_id(neutron_client,
parsed_args.network)
neutron_client.remove_network_from_bgp_speaker(_speaker_id,
{'network_id': _net_id})
print(_('Removed network %(net)s from BGP speaker %(speaker)s.') %
{'net': parsed_args.network, 'speaker': parsed_args.bgp_speaker},
file=self.app.stdout)
class ListRoutesAdvertisedBySpeaker(neutronv20.ListCommand):
"""List routes advertised by a given BGP speaker."""
list_columns = ['id', 'destination', 'next_hop']
resource = 'advertised_route'
pagination_support = True
sorting_support = True
def get_parser(self, prog_name):
parser = super(ListRoutesAdvertisedBySpeaker,
self).get_parser(prog_name)
parser.add_argument(
'bgp_speaker',
metavar='BGP_SPEAKER',
help=_('ID or name of the BGP speaker.'))
return parser
def call_server(self, neutron_client, search_opts, parsed_args):
_speaker_id = get_bgp_speaker_id(neutron_client,
parsed_args.bgp_speaker)
data = neutron_client.list_route_advertised_from_bgp_speaker(
_speaker_id, **search_opts)
return data

View File

@ -45,6 +45,9 @@ from neutronclient.neutron.v2_0 import agent
from neutronclient.neutron.v2_0 import agentscheduler
from neutronclient.neutron.v2_0 import auto_allocated_topology
from neutronclient.neutron.v2_0 import availability_zone
from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched
from neutronclient.neutron.v2_0.bgp import peer as bgp_peer
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
from neutronclient.neutron.v2_0 import extension
from neutronclient.neutron.v2_0.flavor import flavor
from neutronclient.neutron.v2_0.flavor import flavor_profile
@ -395,6 +398,35 @@ COMMAND_V2 = {
'availability-zone-list': availability_zone.ListAvailabilityZone,
'auto-allocated-topology-show': (
auto_allocated_topology.ShowAutoAllocatedTopology),
'bgp-dragent-speaker-add': (
bgp_drsched.AddBGPSpeakerToDRAgent
),
'bgp-dragent-speaker-remove': (
bgp_drsched.RemoveBGPSpeakerFromDRAgent
),
'bgp-speaker-list-on-dragent': (
bgp_drsched.ListBGPSpeakersOnDRAgent
),
'bgp-dragent-list-hosting-speaker': (
bgp_drsched.ListDRAgentsHostingBGPSpeaker
),
'bgp-speaker-list': bgp_speaker.ListSpeakers,
'bgp-speaker-advertiseroute-list': (
bgp_speaker.ListRoutesAdvertisedBySpeaker
),
'bgp-speaker-show': bgp_speaker.ShowSpeaker,
'bgp-speaker-create': bgp_speaker.CreateSpeaker,
'bgp-speaker-update': bgp_speaker.UpdateSpeaker,
'bgp-speaker-delete': bgp_speaker.DeleteSpeaker,
'bgp-speaker-peer-add': bgp_speaker.AddPeerToSpeaker,
'bgp-speaker-peer-remove': bgp_speaker.RemovePeerFromSpeaker,
'bgp-speaker-network-add': bgp_speaker.AddNetworkToSpeaker,
'bgp-speaker-network-remove': bgp_speaker.RemoveNetworkFromSpeaker,
'bgp-peer-list': bgp_peer.ListPeers,
'bgp-peer-show': bgp_peer.ShowPeer,
'bgp-peer-create': bgp_peer.CreatePeer,
'bgp-peer-update': bgp_peer.UpdatePeer,
'bgp-peer-delete': bgp_peer.DeletePeer,
}
COMMANDS = {'2.0': COMMAND_V2}

View File

View File

@ -0,0 +1,66 @@
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
# 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 sys
from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched
from neutronclient.tests.unit import test_cli20
from neutronclient.tests.unit import test_cli20_agentschedulers as test_as
BGP_DRAGENT_ID = 'bgp_dragent_id1'
BGP_SPEAKER = 'bgp_speaker_id1'
class CLITestV20DRAgentScheduler(test_as.CLITestV20AgentScheduler):
def test_add_bgp_speaker_to_dragent(self):
resource = 'agent'
cmd = bgp_drsched.AddBGPSpeakerToDRAgent(
test_cli20.MyApp(sys.stdout), None)
args = (BGP_DRAGENT_ID, BGP_SPEAKER)
body = {'bgp_speaker_id': BGP_SPEAKER}
result = {'bgp_speaker_id': 'bgp_speaker_id', }
self._test_add_to_agent(resource, cmd, args,
self.client.BGP_DRINSTANCES,
body, result)
def test_remove_bgp_speaker_from_dragent(self):
resource = 'agent'
cmd = bgp_drsched.RemoveBGPSpeakerFromDRAgent(
test_cli20.MyApp(sys.stdout), None)
args = (BGP_DRAGENT_ID, BGP_SPEAKER)
self._test_remove_from_agent(resource, cmd, args,
self.client.BGP_DRINSTANCES)
def test_list_bgp_speakers_on_dragent(self):
resources = 'bgp_speakers'
cmd = bgp_drsched.ListBGPSpeakersOnDRAgent(
test_cli20.MyApp(sys.stdout), None)
path = ((self.client.agent_path + self.client.BGP_DRINSTANCES) %
BGP_DRAGENT_ID)
self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID],
path=path)
def test_list_dragents_hosting_bgp_speaker(self):
resources = 'agent'
cmd = bgp_drsched.ListDRAgentsHostingBGPSpeaker(
test_cli20.MyApp(sys.stdout), None)
path = ((self.client.bgp_speaker_path + self.client.BGP_DRAGENTS) %
BGP_DRAGENT_ID)
contents = {self.id_field: 'myid1', 'alive': True}
self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID],
path=path, response_contents=contents)

View File

@ -0,0 +1,223 @@
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
# 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 sys
from neutronclient.common import exceptions
from neutronclient.neutron.v2_0.bgp import peer as bgp_peer
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
from neutronclient.tests.unit import test_cli20
class CLITestV20BGPPeerJSON(test_cli20.CLITestV20Base):
non_admin_status_resources = ['bgp_peer']
def test_create_bgp_peer_with_mandatory_params(self):
# Create BGP peer with mandatory params.
resource = 'bgp_peer'
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
peerip = '1.1.1.1'
remote_asnum = '1'
args = [name,
'--peer-ip', peerip,
'--remote-as', remote_asnum, ]
position_names = ['name', 'peer_ip', 'remote_as',
'auth_type']
position_values = [name, peerip, remote_asnum, 'none']
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values)
def test_create_bgp_peer_with_all_params(self):
# Create BGP peer with all params.
resource = 'bgp_peer'
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
peerip = '1.1.1.1'
remote_asnum = '65535'
authType = 'md5'
password = 'abc'
args = [name,
'--peer-ip', peerip,
'--remote-as', remote_asnum,
'--auth-type', authType,
'--password', password]
position_names = ['name', 'peer_ip', 'remote_as',
'auth_type', 'password']
position_values = [name, peerip, remote_asnum, authType, password]
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values)
def test_create_bgp_peer_with_invalid_min_remote_asnum(self):
# Create BGP peer with invalid minimum remote-asnum.
resource = 'bgp_peer'
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
peerip = '1.1.1.1'
remote_asnum = '0'
args = [name,
'--peer-ip', peerip,
'--remote-as', remote_asnum, ]
position_names = ['name', 'peer_ip', 'remote_as', ]
position_values = [name, peerip, remote_asnum, ]
exc = self.assertRaises(exceptions.CommandError,
self._test_create_resource,
resource, cmd, name, my_id, args,
position_names, position_values)
self.assertEqual('remote-as "0" should be an integer [%s:%s].' %
(bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM),
str(exc))
def test_create_bgp_peer_with_invalid_max_remote_asnum(self):
# Create BGP peer with invalid maximum remote-asnum.
resource = 'bgp_peer'
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
peerip = '1.1.1.1'
remote_asnum = '65536'
args = [name,
'--peer-ip', peerip,
'--remote-as', remote_asnum, ]
position_names = ['name', 'peer_ip', 'remote_as',
'auth_type', 'password']
position_values = [name, peerip, remote_asnum, 'none', '']
exc = self.assertRaises(exceptions.CommandError,
self._test_create_resource,
resource, cmd, name, my_id, args,
position_names, position_values)
self.assertEqual('remote-as "65536" should be an integer [%s:%s].' %
(bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM),
str(exc))
def test_create_authenticated_bgp_peer_without_authtype(self):
# Create authenticated BGP peer without auth-type.
resource = 'bgp_peer'
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
peerip = '1.1.1.1'
remote_asnum = '2048'
password = 'abc'
args = [name,
'--peer-ip', peerip,
'--remote-as', remote_asnum,
'--password', password]
position_names = ['name', 'peer_ip', 'remote_as', 'password']
position_values = [name, peerip, remote_asnum, password]
exc = self.assertRaises(exceptions.CommandError,
self._test_create_resource,
resource, cmd, name, my_id, args,
position_names, position_values)
self.assertEqual('Must provide auth-type if password is specified.',
str(exc))
def test_create_authenticated_bgp_peer_without_password(self):
# Create authenticated BGP peer without password.
resource = 'bgp_peer'
cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
peerip = '1.1.1.1'
remote_asnum = '2048'
authType = 'md5'
args = [name,
'--peer-ip', peerip,
'--remote-as', remote_asnum,
'--auth-type', authType]
position_names = ['name', 'peer_ip', 'remote_as', 'auth-type']
position_values = [name, peerip, remote_asnum, authType]
exc = self.assertRaises(exceptions.CommandError,
self._test_create_resource,
resource, cmd, name, my_id, args,
position_names, position_values)
self.assertEqual('Must provide password if auth-type is specified.',
str(exc))
def test_update_bgp_peer(self):
# Update BGP peer:
# myid --advertise-tenant-networks True
# --advertise-floating-ip-host-routes False
resource = 'bgp_peer'
cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout),
None)
self._test_update_resource(resource, cmd, 'myid',
['myid', '--name', 'new-name',
'--password', 'abc'],
{'name': 'new-name', 'password': 'abc'})
def test_update_bgp_peer_exception(self):
# Update BGP peer: myid.
resource = 'bgp_peer'
cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout),
None)
self.assertRaises(exceptions.CommandError,
self._test_update_resource,
resource, cmd, 'myid', ['myid'], {})
def test_list_bgp_peer(self):
# List all BGP peers.
resources = "bgp_peers"
cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd, True)
# TODO(Vikram): Add test_list_bgp_peer_pagination
def test_list_bgp_peer_sort(self):
# sorted list: bgp-peer-list --sort-key name --sort-key id
# --sort-key asc --sort-key desc
resources = "bgp_peers"
cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_bgp_peer_limit(self):
# size (1000) limited list: bgp-peer-list -P.
resources = "bgp_peers"
cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_show_bgp_peer(self):
# Show BGP peer: --fields id --fields name myid.
resource = 'bgp_peer'
cmd = bgp_peer.ShowPeer(test_cli20.MyApp(sys.stdout),
None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args,
['id', 'name'])
def test_delete_bgp_peer(self):
# Delete BGP peer: bgp_peer_id.
resource = 'bgp_peer'
cmd = bgp_peer.DeletePeer(test_cli20.MyApp(sys.stdout),
None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)

View File

@ -0,0 +1,267 @@
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
# 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 sys
from mox3 import mox
from neutronclient.common import exceptions
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
from neutronclient.tests.unit import test_cli20
class CLITestV20BGPSpeakerJSON(test_cli20.CLITestV20Base):
non_admin_status_resources = ['bgp_speaker']
def test_create_bgp_speaker_with_minimal_options(self):
# Create BGP Speaker with mandatory params.
resource = 'bgp_speaker'
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
local_asnum = '1'
args = [name, '--local-as', local_asnum, ]
position_names = ['name', 'local_as', 'ip_version']
position_values = [name, local_asnum, 4]
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values)
def test_create_ipv4_bgp_speaker_with_all_params(self):
# Create BGP Speaker with all params.
resource = 'bgp_speaker'
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
local_asnum = '1'
args = [name,
'--local-as', local_asnum,
'--ip-version', '4',
'--advertise-floating-ip-host-routes', 'True',
'--advertise-tenant-networks', 'True']
position_names = ['name', 'local_as', 'ip_version',
'advertise_floating_ip_host_routes',
'advertise_tenant_networks']
position_values = [name, local_asnum, 4, 'True', 'True']
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values)
def test_create_ipv6_bgp_speaker_with_all_params(self):
# Create BGP Speaker with all params.
resource = 'bgp_speaker'
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
local_asnum = '65535'
args = [name,
'--local-as', local_asnum,
'--ip-version', '6',
'--advertise-floating-ip-host-routes', 'True',
'--advertise-tenant-networks', 'True']
position_names = ['name', 'local_as', 'ip_version',
'advertise_floating_ip_host_routes',
'advertise_tenant_networks']
position_values = [name, local_asnum, 6, 'True', 'True']
self._test_create_resource(resource, cmd, name, my_id, args,
position_names, position_values)
def test_create_bgp_speaker_with_invalid_min_local_asnum(self):
# Create BGP Speaker with invalid minimum local-asnum.
resource = 'bgp_speaker'
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
local_asnum = '0'
args = [name,
'--local-as', local_asnum]
position_names = ['name', 'local_as']
position_values = [name, local_asnum]
exc = self.assertRaises(exceptions.CommandError,
self._test_create_resource,
resource, cmd, name, my_id, args,
position_names, position_values)
self.assertEqual('local-as "0" should be an integer [%s:%s].' %
(bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM),
str(exc))
def test_create_bgp_speaker_with_invalid_max_local_asnum(self):
# Create BGP Speaker with invalid maximum local-asnum.
resource = 'bgp_speaker'
cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout),
None)
name = 'my-name'
my_id = 'my-id'
local_asnum = '65536'
args = [name,
'--local-as', local_asnum]
position_names = ['name', 'local_as', ]
position_values = [name, local_asnum, ]
exc = self.assertRaises(exceptions.CommandError,
self._test_create_resource,
resource, cmd, name, my_id, args,
position_names, position_values)
self.assertEqual('local-as "65536" should be an integer [%s:%s].' %
(bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM),
str(exc))
def test_update_bgp_speaker(self):
# Update BGP Speaker:
# myid --advertise-tenant-networks True
# --advertise-floating-ip-host-routes False
resource = 'bgp_speaker'
cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout),
None)
self._test_update_resource(resource, cmd, 'myid',
['myid',
'--name', 'new-name',
'--advertise-tenant-networks', 'True',
'--advertise-floating-ip-host-routes',
'False'],
{'name': 'new-name',
'advertise_tenant_networks': 'True',
'advertise_floating_ip_host_routes':
'False'})
def test_update_bgp_speaker_exception(self):
# Update BGP Speaker: myid.
resource = 'bgp_speaker'
cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout),
None)
self.assertRaises(exceptions.CommandError,
self._test_update_resource,
resource, cmd, 'myid', ['myid'], {})
def test_list_bgp_speaker(self):
# List all BGP Speakers.
resources = "bgp_speakers"
cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd, True)
def test_list_bgp_speaker_pagination(self):
# List all BGP Speakers with pagination support.
cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout),
None)
self.mox.StubOutWithMock(bgp_speaker.ListSpeakers,
"extend_list")
bgp_speaker.ListSpeakers.extend_list(mox.IsA(list),
mox.IgnoreArg())
self._test_list_resources_with_pagination("bgp_speakers",
cmd)
self.mox.VerifyAll()
self.mox.UnsetStubs()
def test_list_bgp_speaker_sort(self):
# sorted list: bgp-speaker-list --sort-key name --sort-key id
# --sort-key asc --sort-key desc
resources = "bgp_speakers"
cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd,
sort_key=["name", "id"],
sort_dir=["asc", "desc"])
def test_list_bgp_speaker_limit(self):
# size (1000) limited list: bgp-speaker-list -P.
resources = "bgp_speakers"
cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout),
None)
self._test_list_resources(resources, cmd, page_size=1000)
def test_show_bgp_speaker(self):
# Show BGP Speaker: --fields id --fields name myid.
resource = 'bgp_speaker'
cmd = bgp_speaker.ShowSpeaker(test_cli20.MyApp(sys.stdout),
None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(resource, cmd, self.test_id, args,
['id', 'name'])
def test_delete_bgp_speaker(self):
# Delete BGP Speaker: bgp_speaker_id.
resource = 'bgp_speaker'
cmd = bgp_speaker.DeleteSpeaker(test_cli20.MyApp(sys.stdout),
None)
myid = 'myid'
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
def _test_add_remove_peer(self, action, cmd, args):
"""Add or Remove BGP Peer to/from a BGP Speaker."""
resource = 'bgp_speaker'
subcmd = '%s_bgp_peer' % action
body = {'bgp_peer_id': 'peerid'}
if action == 'add':
retval = {'bgp_peer': 'peerid'}
else:
retval = None
self._test_update_resource_action(resource, cmd, 'myid',
subcmd, args, body, retval)
def test_add_peer_to_bgp_speaker(self):
# Add peer to BGP speaker: myid peer_id=peerid
cmd = bgp_speaker.AddPeerToSpeaker(test_cli20.MyApp(sys.stdout),
None)
args = ['myid', 'peerid']
self._test_add_remove_peer('add', cmd, args)
def test_remove_peer_from_bgp_speaker(self):
# Remove peer from BGP speaker: myid peer_id=peerid
cmd = bgp_speaker.RemovePeerFromSpeaker(test_cli20.MyApp(sys.stdout),
None)
args = ['myid', 'peerid']
self._test_add_remove_peer('remove', cmd, args)
def _test_add_remove_network(self, action, cmd, args):
# Add or Remove network to/from a BGP Speaker.
resource = 'bgp_speaker'
subcmd = '%s_gateway_network' % action
body = {'network_id': 'netid'}
if action == 'add':
retval = {'network': 'netid'}
else:
retval = None
self._test_update_resource_action(resource, cmd, 'myid',
subcmd, args, body, retval)
def test_add_network_to_bgp_speaker(self):
# Add peer to BGP speaker: myid network_id=netid
cmd = bgp_speaker.AddNetworkToSpeaker(test_cli20.MyApp(sys.stdout),
None)
args = ['myid', 'netid']
self._test_add_remove_network('add', cmd, args)
def test_remove_network_from_bgp_speaker(self):
# Remove network from BGP speaker: myid network_id=netid
cmd = bgp_speaker.RemoveNetworkFromSpeaker(
test_cli20.MyApp(sys.stdout), None)
args = ['myid', 'netid']
self._test_add_remove_network('remove', cmd, args)
def test_list_routes_advertised_by_a_bgp_speaker(self):
# Retrieve advertised route list
resources = 'advertised_routes'
cmd = bgp_speaker.ListRoutesAdvertisedBySpeaker(
test_cli20.MyApp(sys.stdout), None)
bs_id = 'bgp_speaker_id1'
path = ((self.client.bgp_speaker_path + '/get_advertised_routes') %
bs_id)
self._test_list_resources(resources, cmd, base_args=[bs_id],
path=path)

View File

@ -404,6 +404,14 @@ class Client(ClientBase):
flavor_profile_binding_path = flavor_path + service_profile_path
availability_zones_path = "/availability_zones"
auto_allocated_topology_path = "/auto-allocated-topology/%s"
BGP_DRINSTANCES = "/bgp-drinstances"
BGP_DRINSTANCE = "/bgp-drinstance/%s"
BGP_DRAGENTS = "/bgp-dragents"
BGP_DRAGENT = "/bgp-dragents/%s"
bgp_speakers_path = "/bgp-speakers"
bgp_speaker_path = "/bgp-speakers/%s"
bgp_peers_path = "/bgp-peers"
bgp_peer_path = "/bgp-peers/%s"
# API has no way to report plurals, so we have to hard code them
EXTED_PLURALS = {'routers': 'router',
@ -441,6 +449,8 @@ class Client(ClientBase):
'bandwidth_limit_rules': 'bandwidth_limit_rule',
'rule_types': 'rule_type',
'flavors': 'flavor',
'bgp_speakers': 'bgp_speaker',
'bgp_peers': 'bgp_peer',
}
@APIParamsCall
@ -1340,6 +1350,30 @@ class Client(ClientBase):
return self.post((self.agent_path + self.L3_ROUTERS) % l3_agent,
body=body)
@APIParamsCall
def list_dragents_hosting_bgp_speaker(self, bgp_speaker, **_params):
"""Fetches a list of Dynamic Routing agents hosting a BGP speaker."""
return self.get((self.bgp_speaker_path + self.BGP_DRAGENTS)
% bgp_speaker, params=_params)
@APIParamsCall
def add_bgp_speaker_to_dragent(self, bgp_dragent, body):
"""Adds a BGP speaker to Dynamic Routing agent."""
return self.post((self.agent_path + self.BGP_DRINSTANCES)
% bgp_dragent, body=body)
@APIParamsCall
def remove_bgp_speaker_from_dragent(self, bgp_dragent, bgpspeaker_id):
"""Removes a BGP speaker from Dynamic Routing agent."""
return self.delete((self.agent_path + self.BGP_DRINSTANCES + "/%s")
% (bgp_dragent, bgpspeaker_id))
@APIParamsCall
def list_bgp_speaker_on_dragent(self, bgp_dragent, **_params):
"""Fetches a list of BGP speakers hosted by Dynamic Routing agent."""
return self.get((self.agent_path + self.BGP_DRINSTANCES)
% bgp_dragent, params=_params)
@APIParamsCall
def list_firewall_rules(self, retrieve_all=True, **_params):
"""Fetches a list of all firewall rules for a tenant."""
@ -1695,6 +1729,89 @@ class Client(ClientBase):
self.auto_allocated_topology_path % tenant_id,
params=_params)
@APIParamsCall
def list_bgp_speakers(self, retrieve_all=True, **_params):
"""Fetches a list of all BGP speakers for a tenant."""
return self.list('bgp_speakers', self.bgp_speakers_path, retrieve_all,
**_params)
@APIParamsCall
def show_bgp_speaker(self, bgp_speaker_id, **_params):
"""Fetches information of a certain BGP speaker."""
return self.get(self.bgp_speaker_path % (bgp_speaker_id),
params=_params)
@APIParamsCall
def create_bgp_speaker(self, body=None):
"""Creates a new BGP speaker."""
return self.post(self.bgp_speakers_path, body=body)
@APIParamsCall
def update_bgp_speaker(self, bgp_speaker_id, body=None):
"""Update a BGP speaker."""
return self.put(self.bgp_speaker_path % bgp_speaker_id, body=body)
@APIParamsCall
def delete_bgp_speaker(self, speaker_id):
"""Deletes the specified BGP speaker."""
return self.delete(self.bgp_speaker_path % (speaker_id))
@APIParamsCall
def add_peer_to_bgp_speaker(self, speaker_id, body=None):
"""Adds a peer to BGP speaker."""
return self.put((self.bgp_speaker_path % speaker_id) +
"/add_bgp_peer", body=body)
@APIParamsCall
def remove_peer_from_bgp_speaker(self, speaker_id, body=None):
"""Removes a peer from BGP speaker."""
return self.put((self.bgp_speaker_path % speaker_id) +
"/remove_bgp_peer", body=body)
@APIParamsCall
def add_network_to_bgp_speaker(self, speaker_id, body=None):
"""Adds a network to BGP speaker."""
return self.put((self.bgp_speaker_path % speaker_id) +
"/add_gateway_network", body=body)
@APIParamsCall
def remove_network_from_bgp_speaker(self, speaker_id, body=None):
"""Removes a network from BGP speaker."""
return self.put((self.bgp_speaker_path % speaker_id) +
"/remove_gateway_network", body=body)
@APIParamsCall
def list_route_advertised_from_bgp_speaker(self, speaker_id, **_params):
"""Fetches a list of all routes advertised by BGP speaker."""
return self.get((self.bgp_speaker_path % speaker_id) +
"/get_advertised_routes", params=_params)
@APIParamsCall
def list_bgp_peers(self, **_params):
"""Fetches a list of all BGP peers."""
return self.get(self.bgp_peers_path, params=_params)
@APIParamsCall
def show_bgp_peer(self, peer_id, **_params):
"""Fetches information of a certain BGP peer."""
return self.get(self.bgp_peer_path % peer_id,
params=_params)
@APIParamsCall
def create_bgp_peer(self, body=None):
"""Create a new BGP peer."""
return self.post(self.bgp_peers_path, body=body)
@APIParamsCall
def update_bgp_peer(self, bgp_peer_id, body=None):
"""Update a BGP peer."""
return self.put(self.bgp_peer_path % bgp_peer_id, body=body)
@APIParamsCall
def delete_bgp_peer(self, peer_id):
"""Deletes the specified BGP peer."""
return self.delete(self.bgp_peer_path % peer_id)
def __init__(self, **kwargs):
"""Initialize a new client for the Neutron v2.0 API."""
super(Client, self).__init__(**kwargs)

View File

@ -0,0 +1,5 @@
---
features:
- |
CLI support for the BGP dynamic routing functionality will help
advertising neutron fixed-ips and dvr host routes via BGP.