diff --git a/releasenotes/notes/clustering-service-cli-e15cc6627de293fa.yaml b/releasenotes/notes/clustering-service-cli-e15cc6627de293fa.yaml new file mode 100644 index 00000000..9830157b --- /dev/null +++ b/releasenotes/notes/clustering-service-cli-e15cc6627de293fa.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Enalble CLI to support clustering service in Tacker Server diff --git a/tackerclient/shell.py b/tackerclient/shell.py index 76903b33..d238de61 100644 --- a/tackerclient/shell.py +++ b/tackerclient/shell.py @@ -50,6 +50,7 @@ from tackerclient.tacker.v1_0 import extension from tackerclient.tacker.v1_0.nfvo import ns from tackerclient.tacker.v1_0.nfvo import nsd from tackerclient.tacker.v1_0.nfvo import vim +from tackerclient.tacker.v1_0.nfvo import vnfcluster from tackerclient.tacker.v1_0.nfvo import vnffg from tackerclient.tacker.v1_0.nfvo import vnffgd from tackerclient.tacker.v1_0.vnfm import vnf @@ -168,6 +169,16 @@ COMMAND_V1 = { 'ns-list': ns.ListNS, 'ns-delete': ns.DeleteNS, 'ns-show': ns.ShowNS, + + 'cluster-create': vnfcluster.CreateCluster, + 'cluster-delete': vnfcluster.DeleteCluster, + 'cluster-list': vnfcluster.ListCluster, + 'cluster-show': vnfcluster.ShowCluster, + + 'cluster-member-add': vnfcluster.AddClusterMember, + 'cluster-member-show': vnfcluster.ShowClusterMember, + 'cluster-member-list': vnfcluster.ListClusterMember, + 'cluster-member-delete': vnfcluster.DeleteClusterMember, } COMMANDS = {'1.0': COMMAND_V1} diff --git a/tackerclient/tacker/v1_0/nfvo/vnfcluster.py b/tackerclient/tacker/v1_0/nfvo/vnfcluster.py new file mode 100644 index 00000000..cfb9185a --- /dev/null +++ b/tackerclient/tacker/v1_0/nfvo/vnfcluster.py @@ -0,0 +1,191 @@ +# 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 tackerclient.tacker import v1_0 as tackerV10 +import yaml + +_CLUSTER = 'cluster' +_CLUSTER_MEMBER = 'clustermember' + + +class ListCluster(tackerV10.ListCommand): + """List Clusters that belong to a given tenant.""" + + resource = _CLUSTER + list_columns = ['id', 'name', 'vnfd_id', 'status', 'vip_endpoint'] + + +class ShowCluster(tackerV10.ShowCommand): + """Show information of a given Cluster.""" + + resource = _CLUSTER + + +class DeleteCluster(tackerV10.DeleteCommand): + """Delete a given Cluster.""" + + resource = _CLUSTER + + +class CreateCluster(tackerV10.CreateCommand): + """Create a Cluster.""" + + resource = _CLUSTER + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', metavar='NAME', + help='Set a name for the VNF cluster') + vnfd_group = parser.add_mutually_exclusive_group(required=True) + vnfd_group.add_argument( + '--vnfd-id', + help='VNFD ID to use as template to create member VNF') + vnfd_group.add_argument( + '--vnfd-name', + help='VNFD name to use as template to create member VNF') + parser.add_argument('--policy-file', + help='Specify policy file for cluster', + required=True) + parser.add_argument( + '--description', + help='Set a description for the created VNF cluster') + + def args2body(self, parsed_args): + body = {self.resource: {}} + tacker_client = self.get_client() + tacker_client.format = parsed_args.request_format + + if parsed_args.vnfd_name: + _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, + 'vnfd', + parsed_args. + vnfd_name) + parsed_args.vnfd_id = _id + policy_info = None + with open(parsed_args.policy_file) as f: + policy_info = yaml.safe_load(f.read()) + tackerV10.update_dict(parsed_args, body[self.resource], + ['tenant_id', 'name', 'vnfd_id', 'description']) + if policy_info: + body[self.resource]['policy_info'] = policy_info + return body + + +class AddClusterMember(tackerV10.CreateCommand): + """Add a new Cluster Member to given Cluster.""" + + resource = _CLUSTER_MEMBER + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', metavar='NAME', + help='Set a name for the VNF cluster member') + cluster_group = parser.add_mutually_exclusive_group() + cluster_group.add_argument( + '--cluster-id', + help='VNFD ID to use as template to create member VNF') + cluster_group.add_argument( + '--cluster-name', + help='VNFD name to use as template to create member VNF') + vnfd_group = parser.add_mutually_exclusive_group() + vnfd_group.add_argument( + '--vnfd-id', + help='Set a id for the VNFD') + vnfd_group.add_argument( + '--vnfd-name', + help='Set a name for the VNFD') + parser.add_argument( + '--role', + help='Set a [Active/Standby] role to cluster member', + required=True) + vim_group = parser.add_mutually_exclusive_group() + vim_group.add_argument( + '--vim-id', + help='Set a VIM ID to deploy cluster member') + vim_group.add_argument( + '--vim-name', + help='Set a VIM name to deploy cluster member') + + def args2body(self, parsed_args): + body = {self.resource: {}} + + tacker_client = self.get_client() + tacker_client.format = parsed_args.request_format + if parsed_args.cluster_name: + _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, + 'cluster', + parsed_args. + cluster_name) + parsed_args.cluster_id = _id + if parsed_args.vnfd_name: + _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, + 'vnfd', + parsed_args. + vnfd_name) + parsed_args.vnfd_id = _id + parsed_args.role = parsed_args.role.upper() + if parsed_args.vim_name: + _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, + 'vim', + parsed_args. + vim_name) + parsed_args.vim_id = _id + tackerV10.update_dict(parsed_args, body[self.resource], + ['tenant_id', 'name', 'cluster_id', 'vnfd_id', + 'role', 'vim_id']) + return body + + +class ListClusterMember(tackerV10.ListCommand): + """List Cluster Members that belong to a given tenant.""" + + resource = _CLUSTER_MEMBER + + def add_known_arguments(self, parser): + cluster_group = parser.add_mutually_exclusive_group(required=True) + cluster_group.add_argument( + '--cluster-id', + help='Set a ID for the queried cluster') + cluster_group.add_argument( + '--cluster-name', + help='Set a name for the queried cluster') + + def args2body(self, parsed_args): + body = {self.resource: {}} + tacker_client = self.get_client() + tacker_client.format = parsed_args.request_format + + if parsed_args.cluster_name: + _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, + 'cluster', + parsed_args. + cluster_name) + parsed_args.cluster_id = _id + + tackerV10.update_dict(parsed_args, body[self.resource], + ['tenant_id', 'cluster_id']) + return body + + list_columns = ['id', 'name', 'cluster_id', 'role', 'vnf_id', + 'vim_id', 'mgmt_url', 'lb_member_id'] + + +class DeleteClusterMember(tackerV10.DeleteCommand): + """Delete a given Cluster Member.""" + + resource = _CLUSTER_MEMBER + + +class ShowClusterMember(tackerV10.ShowCommand): + """Show information of a given Cluster Member.""" + + resource = _CLUSTER_MEMBER diff --git a/tackerclient/v1_0/client.py b/tackerclient/v1_0/client.py index abdb03f4..d9b5dc35 100644 --- a/tackerclient/v1_0/client.py +++ b/tackerclient/v1_0/client.py @@ -368,6 +368,11 @@ class Client(ClientBase): nss_path = '/nss' ns_path = '/nss/%s' + clusters_path = '/clusters' + cluster_path = '/clusters/%s' + cluster_members_path = '/clustermembers' + cluster_member_path = '/clustermembers/%s' + # API has no way to report plurals, so we have to hard code them # EXTED_PLURALS = {} @@ -662,3 +667,44 @@ class Client(ClientBase): @APIParamsCall def delete_ns(self, ns): return self.delete(self.ns_path % ns) + + @APIParamsCall + def create_cluster(self, body=None): + return self.post(self.clusters_path, body) + + @APIParamsCall + def list_clusters(self, retrieve_all=True, **_params): + clusters = self.list('clusters', self.clusters_path, + retrieve_all, **_params) + return clusters + + @APIParamsCall + def show_cluster(self, cluster, **_params): + member = self.get(self.cluster_path % cluster, + params=_params) + return member + + @APIParamsCall + def delete_cluster(self, cluster): + return self.delete(self.cluster_path % cluster) + + @APIParamsCall + def create_clustermember(self, body=None): + return self.post(self.cluster_members_path, body) + + @APIParamsCall + def list_clustermembers(self, retrieve_all=True, **_params): + cluster_members = self.list('clustermembers', + self.cluster_members_path, + retrieve_all, **_params) + return cluster_members + + @APIParamsCall + def show_clustermember(self, clustermember, **_params): + member = self.get(self.cluster_member_path % clustermember, + params=_params) + return member + + @APIParamsCall + def delete_clustermember(self, clustermember): + return self.delete(self.cluster_member_path % clustermember)