Repository: vmware/govmomi (go)

Issue:
Add categoryID option when creating a category, tagID when creating tag.
**Is your feature request related to a problem? Please describe.**

I would like to replicate tagging across multiple vCenter Server instances.
If I am unable to create a category with the same ID as the source, then the linkage of the tags to categories needs to be manually adjusted.

**Describe the solution you'd like**

https://developer.broadcom.com/xapis/vsphere-automation-api/latest/cis/api/cis/tagging/category/post/
This mentions an optional category_id parameter.
I would like to be able to provide that to the CreateCategory request.
I believe currently it excludes that value in the create struct.

**Describe alternatives you've considered**

Alternatively I just manually make the REST calls.


Pull request:
api: Add optional CategoryID,TagID fields for category and tag creation
- govc: Add '-id' option for tags.category and tags create commands
- vcsim: Add vapi support for CategoryID and TagID fields

Fixes #3706


Patch:
diff --git a/cli/tags/category/create.go b/cli/tags/category/create.go
old mode 100755
new mode 100644
index 00ca7514..7ab19ebc
--- a/cli/tags/category/create.go
+++ b/cli/tags/category/create.go
@@ -1,18 +1,6 @@
-/*
-Copyright (c) 2018 VMware, Inc. 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.
-*/
+// © Broadcom. All Rights Reserved.
+// The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+// SPDX-License-Identifier: Apache-2.0
 
 package category
 
@@ -39,6 +27,7 @@ func init() {
 func (cmd *create) Register(ctx context.Context, f *flag.FlagSet) {
 	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
 	cmd.ClientFlag.Register(ctx, f)
+	f.StringVar(&cmd.cat.CategoryID, "id", "", "Category ID")
 	f.StringVar(&cmd.cat.Description, "d", "", "Description")
 	f.Var((*kinds)(&cmd.cat.AssociableTypes), "t", "Object types")
 	f.BoolVar(&cmd.multi, "m", false, "Allow multiple tags per object")
diff --git a/cli/tags/create.go b/cli/tags/create.go
index 713f6460..b21f5649 100644
--- a/cli/tags/create.go
+++ b/cli/tags/create.go
@@ -1,18 +1,6 @@
-/*
-Copyright (c) 2018 VMware, Inc. 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.
-*/
+// © Broadcom. All Rights Reserved.
+// The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+// SPDX-License-Identifier: Apache-2.0
 
 package tags
 
@@ -38,6 +26,7 @@ func init() {
 func (cmd *create) Register(ctx context.Context, f *flag.FlagSet) {
 	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
 	cmd.ClientFlag.Register(ctx, f)
+	f.StringVar(&cmd.tag.TagID, "id", "", "Tag ID")
 	f.StringVar(&cmd.tag.CategoryID, "c", "", "Category name")
 	f.StringVar(&cmd.tag.Description, "d", "", "Description of tag")
 }
diff --git a/govc/USAGE.md b/govc/USAGE.md
index d752ac2a..7d73a65a 100644
--- a/govc/USAGE.md
+++ b/govc/USAGE.md
@@ -6117,6 +6117,7 @@ Examples:
 
 Options:
   -d=                    Description
+  -id=                   Category ID
   -m=false               Allow multiple tags per object
   -t=[]                  Object types
 ```
@@ -6205,6 +6206,7 @@ Examples:
 Options:
   -c=                    Category name
   -d=                    Description of tag
+  -id=                   Tag ID
 ```
 
 ## tags.detach
diff --git a/vapi/simulator/simulator.go b/vapi/simulator/simulator.go
index 391d801a..608b23d0 100644
--- a/vapi/simulator/simulator.go
+++ b/vapi/simulator/simulator.go
@@ -595,7 +595,13 @@ func (s *handler) category(w http.ResponseWriter, r *http.Request) {
 					return
 				}
 			}
-			id := newID("Category")
+			id := spec.Category.CategoryID
+			if id == "" {
+				id = newID("Category")
+			} else if !strings.HasPrefix(id, "urn:vmomi:InventoryServiceCategory:") {
+				BadRequest(w, "com.vmware.vapi.std.errors.invalid_argument")
+				return
+			}
 			spec.Category.ID = id
 			s.Category[id] = &spec.Category
 			OK(w, id)
@@ -668,7 +674,13 @@ func (s *handler) tag(w http.ResponseWriter, r *http.Request) {
 					return
 				}
 			}
-			id := newID("Tag")
+			id := spec.Tag.TagID
+			if id == "" {
+				id = newID("Tag")
+			} else if !strings.HasPrefix(id, "urn:vmomi:InventoryServiceTag:") {
+				BadRequest(w, "com.vmware.vapi.std.errors.invalid_argument")
+				return
+			}
 			spec.Tag.ID = id
 			s.Tag[id] = &spec.Tag
 			s.Association[id] = make(map[internal.AssociatedObject]bool)
diff --git a/vapi/tags/categories.go b/vapi/tags/categories.go
index cdaa8390..27b96650 100644
--- a/vapi/tags/categories.go
+++ b/vapi/tags/categories.go
@@ -1,18 +1,6 @@
-/*
-Copyright (c) 2018 VMware, Inc. 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.
-*/
+// © Broadcom. All Rights Reserved.
+// The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+// SPDX-License-Identifier: Apache-2.0
 
 package tags
 
@@ -34,6 +22,7 @@ type Category struct {
 	Cardinality     string   `json:"cardinality,omitempty"`
 	AssociableTypes []string `json:"associable_types,omitempty"`
 	UsedBy          []string `json:"used_by,omitempty"`
+	CategoryID      string   `json:"category_id,omitempty"`
 }
 
 func (c *Category) hasType(kind string) bool {
@@ -74,6 +63,7 @@ func (c *Manager) CreateCategory(ctx context.Context, category *Category) (strin
 		Description     string   `json:"description"`
 		Cardinality     string   `json:"cardinality"`
 		AssociableTypes []string `json:"associable_types"`
+		CategoryID      string   `json:"category_id,omitempty"`
 	}
 	spec := struct {
 		Category create `json:"create_spec"`
@@ -83,6 +73,7 @@ func (c *Manager) CreateCategory(ctx context.Context, category *Category) (strin
 			Description:     category.Description,
 			Cardinality:     category.Cardinality,
 			AssociableTypes: category.AssociableTypes,
+			CategoryID:      category.CategoryID,
 		},
 	}
 	if spec.Category.AssociableTypes == nil {
diff --git a/vapi/tags/tags.go b/vapi/tags/tags.go
index 878464c5..9f49b797 100644
--- a/vapi/tags/tags.go
+++ b/vapi/tags/tags.go
@@ -1,18 +1,6 @@
-/*
-Copyright (c) 2018 VMware, Inc. 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.
-*/
+// © Broadcom. All Rights Reserved.
+// The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+// SPDX-License-Identifier: Apache-2.0
 
 package tags
 
@@ -50,6 +38,7 @@ type Tag struct {
 	Name        string   `json:"name,omitempty"`
 	CategoryID  string   `json:"category_id,omitempty"`
 	UsedBy      []string `json:"used_by,omitempty"`
+	TagID       string   `json:"tag_id,omitempty"`
 }
 
 // Patch merges updates from the given src.
@@ -73,6 +62,7 @@ func (c *Manager) CreateTag(ctx context.Context, tag *Tag) (string, error) {
 		Name        string `json:"name"`
 		Description string `json:"description"`
 		CategoryID  string `json:"category_id"`
+		TagID       string `json:"tag_id,omitempty"`
 	}
 	spec := struct {
 		Tag create `json:"create_spec"`
@@ -81,6 +71,7 @@ func (c *Manager) CreateTag(ctx context.Context, tag *Tag) (string, error) {
 			Name:        tag.Name,
 			Description: tag.Description,
 			CategoryID:  tag.CategoryID,
+			TagID:       tag.TagID,
 		},
 	}
 	if isName(tag.CategoryID) {


Test patch:
diff --git a/govc/test/tags.bats b/govc/test/tags.bats
index 8148dff3..2c1925ab 100755
--- a/govc/test/tags.bats
+++ b/govc/test/tags.bats
@@ -51,6 +51,16 @@ load test_helper
 
   run govc tags.category.rm "$update_name"
   assert_success
+
+  run govc tags.category.create -id "invalid" custom-id-cat
+  assert_failure
+
+  id="urn:vmomi:InventoryServiceCategory:$(new_id):GLOBAL"
+  run govc tags.category.create -id "$id" custom-id-cat
+  assert_success
+
+  run govc tags.category.info "$id"
+  assert_success
 }
 
 @test "tags" {
@@ -117,6 +127,16 @@ load test_helper
 
   run govc tags.info enoent
   assert_failure # does not exist
+
+  run govc tags.category.create -c "$category_name" -id "invalid" custom-id-tag
+  assert_failure
+
+  id="urn:vmomi:InventoryServiceTag:$(new_id):GLOBAL"
+  run govc tags.create -c "$category_name" -id "$id" custom-id-tag
+  assert_success
+
+  run govc tags.info "$id"
+  assert_success
 }
 
 @test "tags.association" {