#!/usr/bin/perl

#
# Perl Core Modules
#
use Exception::Class;
use Getopt::Long;
use strict;

#
# VMware Lookup and SSO libraries
#
use VMware::SSOConnection;
use VMware::LookupService;
use VMware::VIRuntime;

#
# vApi runtime libraries
#
use Com::Vmware::Vapi::Protocol::HttpConfiguration;
use Com::Vmware::Vapi::Protocol::ProtocolConnectionFactory;
use Com::Vmware::Vapi::Bindings::StubConfiguration;
use Com::Vmware::Vapi::Bindings::StubFactory;
use Com::Vmware::Vapi::Dsig::SecurityContextFactory;
use Com::Vmware::Vapi::Util::Logger qw(log_info log_dumper log_framework log_warning set_verbosity);

#
# Generated SDK's
#
use Com::Vmware::Vapi::Std::DynamicID;
use Com::Vmware::Cis::Session;
use Com::Vmware::Cis::Tagging::Category;
use Com::Vmware::Cis::Tagging::Tag;
use Com::Vmware::Cis::Tagging::TagAssociation;
use Com::Vmware::Cis::Tagging::CategoryModel;

#
# This sample demonstrates tagging Cluster by using vSphere Automation SDK for Perl and vSphere Perl SDK
# Step 1:  Connect to the Lookup service and get the SSO URL.
# Step 2:  Connect to SSO service and retrieve the SAML token.
# Step 3:  Connect to the vapi service endpoint.
# Step 4:  Use the SAML token to login to vAPI service endpoint.
# Step 5:  Create a vAPI session.
# Step 6:  Get the cluster Moref by using vSphere Perl SDK.
# Step 7:  Create a new Tag Category.
# Step 8:  Create a new Tag.
# Step 9:  Attach the Tag.
# Step 10: Cleanup - Delete the Tag and Tag Category.
#

# Call main
&main();

sub main() {

   #
   # User inputs
   #
   my %params;
   GetOptions(
               \%params,     "server=s",     "lsurl=s",      "username=s",
               "password=s", "privatekey:s", "servercert:s", "cert:s",
               "tagname:s", "clustername:s", "cleanup:s",      "help:s"
     )
     or die
"\nValid options are --server <server> --username <user> --password <password> --lsurl <lookup service url>
                         --privatekey <private key> --servercert <server cert> --cert <cert> --tagname <tag name> --clustername <cluster name> --cleanup <value should be true or false> or --help\n";

   if ( defined( $params{'help'} ) ) {
      print "\nCommand to execute sample:\n";
      print
"tagging_workflow.pl --server <server> --username <user> --password <password> --lsurl <lookup service url> \n";
      print
"               --privatekey <private key> --servercert <server cert> --cert <cert> --tagname <tag name> --clustername <cluster name> --cleanup <value should be true or false> \n";
      exit;
   }

   if (
         !defined( $params{'username'} )
      || !defined( $params{'password'} )
      || !defined( $params{'lsurl'} )
      || !defined( $params{'server'} )
      || !defined( $params{'cleanup'} )

     )
   {
      print
"\nMissing mandatory parameter(s) i.e. --server <server> --lsurl <lookup service url> --username <user> --password <password> --cleanup <value should be true or false> \n";
      exit;
   }
   our $tag_name= undef;
   my $ip               = $params{'server'};
   my $ls_url           = $params{'lsurl'};
   my $sso_url          = undef;
   my $username         = $params{'username'};
   my $password         = $params{'password'};
   my $private_key      = $params{'privatekey'};
   my $cert             = $params{'cert'};
   my $server_cert      = $params{'servercert'};
   $tag_name            = $params{'tagname'};
   my $cluster_name     = $params{'clustername'};
   our $cleanup         = $params{'cleanup'};
   my $tag_description  = "This tag is created by vAPI Perl sample tagging_workflow.pl";
   our $category_service = undef;
   our $tagging_service  = undef;
   our $category_id      = undef;
   our $tag_id           = undef;

   #
   # If tag name is not supplied use the default
   #
   if ( !defined($tag_name)) {
      $tag_name = "Sample-Tag";
   }

   our $category_name = $tag_name . "-Category";

   set_verbosity( 'level' => 3 );

   #
   # Get the SSO url
   #
   log_info( MSG => "Getting the SSO URL via Lookup Service..." );
   my $lookup_service = new LookupService( 'lookup_url' => $ls_url );
   $sso_url = $lookup_service->find_sso_url();

   #
   # Get the SSO token
   #
   log_info( MSG => $sso_url );

   log_info( MSG => "Getting the SSO token..." );
   my $ssoConnection = new SSOConnection( 'sso_url' => $sso_url );
   $ssoConnection->login(
                          'user_name'   => $username,
                          'password'    => $password,
                          'public_key'  => $cert,
                          'private_key' => $private_key
   );
   my $token = $ssoConnection->get_token();

   #
   # Create a VAPI transport connection
   #
   my $protocolConn = new Com::Vmware::Vapi::Protocol::ProtocolConnectionFactory();
   my $config = $protocolConn->get_connection( 'protocol_type' => 'https',
                                               'uri'           => "$ip/api" );

   #
   # Create a VAPI security context
   #
   my $scFactory = new Com::Vmware::Vapi::Dsig::SecurityContextFactory();
   my $samlTokenSecurityContext = $scFactory->create_saml_security_context(
                                                             'token'       => $token,
                                                             'private_key' => $private_key
   );
   my $stubConfig = new Com::Vmware::Vapi::Bindings::StubConfiguration();
   $stubConfig->set_security_context( security_context => $samlTokenSecurityContext );

   #
   # Get the instance of stub factory
   #
   my $stubFactory =
     new Com::Vmware::Vapi::Bindings::StubFactory(
                                          'api_provider' => $config->get_api_provider() );

   #
   # Get the Session Service
   #
   my $session = $stubFactory->create_stub( 'service_name' => 'Com::Vmware::Cis::Session',
                                            'stub_config'  => $stubConfig );

   log_info( MSG => "Login in to VAPI server using token..." );
   my $sessionId = $session->create();
   log_info( MSG => "Got the Session Id" );

   #
   # Update the VAPI security context with session id
   #
   my $ssContext =
     $scFactory->create_session_security_context( 'session_id' => $sessionId );

   $stubConfig->set_security_context( security_context => $ssContext );

   #
   # Retrieve the Cluster's MoRef by using vSphere Perl SDK
   #
   $ls_url =~ m/http(s?):\/\/(.*)\/lookupservice\/sdk.*/g;
   my $psc_url = $2;
   Opts::set_option( 'psc' => $psc_url );
   Util::connect( $ip . '/sdk', $username, $password );
   my $cluster_view = undef;
   if ( defined $cluster_name ) {
      $cluster_view = Vim::find_entity_view( view_type => 'ClusterComputeResource',
                                             filter    => { name => $cluster_name } );
      if ( !defined($cluster_view) || !defined($cluster_view->{'name'}) ) {
         die "\nCould not find Cluster '".$cluster_name."' ..";
      }
   }
   else {
      $cluster_view = Vim::find_entity_view( view_type => 'ClusterComputeResource' );
      if ( !defined($cluster_view) || !defined($cluster_view->{'name'}) ) {
         die "\nCould not find any valid cluster ..";
      }
      $cluster_name = $cluster_view->{'name'};
      log_info(MSG => "Cluster name is not supplied, hence automatically choosing cluster '".$cluster_name."'" );
   }

   #
   # Convert the MoRef to vAPI DynamicID
   #
   my $moref = $cluster_view->{'mo_ref'};
   my $cluster_tag_id = new Com::Vmware::Vapi::Std::DynamicID( 'type' => $moref->{'type'},
                                                               'id'   => $moref->{'value'}
   );

   #
   # Get the Tag Category Service
   #
   log_info( MSG => "Creating Tag Category Service..." );
   $category_service = $stubFactory->create_stub(
                                  'service_name' => 'Com::Vmware::Cis::Tagging::Category',
                                  'stub_config'  => $stubConfig );

   #
   # Get the Tagging Service
   #
   log_info( MSG => "Creating Tagging Service..." );
   $tagging_service = $stubFactory->create_stub(
                                       'service_name' => 'Com::Vmware::Cis::Tagging::Tag',
                                       'stub_config'  => $stubConfig );

   #
   # Create a new tag category
   #
   log_info( MSG => "Creating a new Tag category '$category_name' ..." );
   my $spec = new Com::Vmware::Cis::Tagging::Category::CreateSpec();
   $spec->set_name( 'name' => $category_name );
   $spec->set_description( 'description' => $tag_description );
   $spec->set_cardinality(
       'cardinality' => Com::Vmware::Cis::Tagging::CategoryModel::Cardinality::MULTIPLE );

   # Set empty types
   $spec->set_associable_types( 'associable_types' => [] );
   $category_id = $category_service->create( 'create_spec' => $spec );
   log_info( MSG => "Tag category create result: $category_id" );

   #
   # Creating a tag
   #
   log_info( MSG => "Creating a new tag '$tag_name' ..." );
   $spec = new Com::Vmware::Cis::Tagging::Tag::CreateSpec();
   $spec->set_name( 'name' => $tag_name );
   $spec->set_description( 'description' => $tag_description );
   $spec->set_category_id( 'category_id' => $category_id );
   $tag_id = $tagging_service->create( 'create_spec' => $spec );
   log_info( MSG => "Tag create result: $tag_id" );

   #
   # Get the Tag Category Service
   #
   log_info( MSG => "Creating Tag Association Service..." );
   my $tag_assoc_service = $stubFactory->create_stub(
                            'service_name' => 'Com::Vmware::Cis::Tagging::TagAssociation',
                            'stub_config'  => $stubConfig );

   log_info( MSG => "Attaching tag to the cluster '" . $cluster_name. "'" );

   #
   # Attach the tag
   #
   $tag_assoc_service->attach( 'tag_id' => $tag_id, 'object_id' => $cluster_tag_id );
   my $attached_tags =
     $tag_assoc_service->list_attached_tags( 'object_id' => $cluster_tag_id );
   log_info( MSG => "Listing all attached tags for cluster '".$cluster_name."'");
   log_warning( MSG => $attached_tags );

   #
   # Detach the tag
   #
   log_info( MSG => "Detaching the tag '$tag_name' ..." );
   $tag_assoc_service->detach( 'tag_id' => $tag_id, 'object_id' => $cluster_tag_id );

   #
   # Cleanup any data created by the sample run, if cleanup=true
   #
   sub cleanup() {
      log_info( MSG => "Deleting the tag '$tag_name' ..." );
      $tagging_service->delete( 'tag_id' => $tag_id );

      log_info( MSG => "Deleting the tag category '$category_name' ..." );
      $category_service->delete( 'category_id' => $category_id );
   }

   if ( $cleanup eq 'true' ) {
      &cleanup();
   }
   log_info( 'MSG' => "Done!" );
}

# END
