testing
Last active
November 12, 2020 15:41
-
-
Save jcpowermac/8df504f79559fa5b1cd4ec2052f96a7f to your computer and use it in GitHub Desktop.
vsphere permissions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "context" | |
| "fmt" | |
| "net/url" | |
| "strings" | |
| "time" | |
| "github.com/davecgh/go-spew/spew" | |
| "github.com/vmware/govmomi" | |
| "github.com/vmware/govmomi/find" | |
| "github.com/vmware/govmomi/ssoadmin" | |
| ssotypes "github.com/vmware/govmomi/ssoadmin/types" | |
| "github.com/vmware/govmomi/sts" | |
| //"github.com/vmware/govmomi/nfc" | |
| "github.com/vmware/govmomi/object" | |
| _ "github.com/vmware/govmomi/object" | |
| _ "github.com/vmware/govmomi/ovf" | |
| "github.com/vmware/govmomi/vapi/rest" | |
| "github.com/vmware/govmomi/vim25" | |
| "github.com/vmware/govmomi/vim25/mo" | |
| "github.com/vmware/govmomi/vim25/soap" | |
| "github.com/vmware/govmomi/vim25/types" | |
| ) | |
| type Platform struct { | |
| // VCenter is the domain name or IP address of the vCenter. | |
| VCenter string `json:"vCenter"` | |
| // Username is the name of the user to use to connect to the vCenter. | |
| Username string `json:"username"` | |
| // Password is the password for the user to use to connect to the vCenter. | |
| Password string `json:"password"` | |
| // Datacenter is the name of the datacenter to use in the vCenter. | |
| Datacenter string `json:"datacenter"` | |
| // DefaultDatastore is the default datastore to use for provisioning volumes. | |
| DefaultDatastore string `json:"defaultDatastore"` | |
| // Folder is the name of the folder that will be used and/or created for | |
| // virtual machines. | |
| Folder string `json:"folder,omitempty"` | |
| // Cluster is the name of the cluster virtual machines will be cloned into. | |
| Cluster string `json:"cluster,omitempty"` | |
| // ClusterOSImage overrides the url provided in rhcos.json to download the RHCOS OVA | |
| ClusterOSImage string `json:"clusterOSImage,omitempty"` | |
| // APIVIP is the virtual IP address for the api endpoint | |
| APIVIP string `json:"apiVIP,omitempty"` | |
| // IngressVIP is the virtual IP address for ingress | |
| IngressVIP string `json:"ingressVIP,omitempty"` | |
| // DNSVIP is the virtual IP address for DNS | |
| DNSVIP string `json:"dnsVIP,omitempty"` | |
| // Network specifies the name of the network to be used by the cluster. | |
| Network string `json:"network,omitempty"` //TODO: determine if this should be omitempty or required | |
| } | |
| // ImportOvaParams contains the vCenter objects required to import a OVA into vSphere. | |
| type ImportOvaParams struct { | |
| ResourcePool *object.ResourcePool | |
| Datacenter *object.Datacenter | |
| Datastore *object.Datastore | |
| Network *object.Network | |
| Host *object.HostSystem | |
| Folder *object.Folder | |
| } | |
| func findImportOvaParams(client *vim25.Client, datacenter, cluster, datastore, network string) (*ImportOvaParams, error) { | |
| var ccrMo mo.ClusterComputeResource | |
| ctx := context.TODO() | |
| importOvaParams := &ImportOvaParams{} | |
| finder := find.NewFinder(client) | |
| dcObj, err := finder.Datacenter(ctx, datacenter) | |
| if err != nil { | |
| return nil, err | |
| } | |
| importOvaParams.Datacenter = dcObj | |
| folders, err := importOvaParams.Datacenter.Folders(ctx) | |
| if err != nil { | |
| return nil, err | |
| } | |
| importOvaParams.Folder = folders.VmFolder | |
| clusterPath := fmt.Sprintf("/%s/host/%s", datacenter, cluster) | |
| clusterComputeResource, err := finder.ClusterComputeResource(ctx, clusterPath) | |
| if err != nil { | |
| return nil, err | |
| } | |
| err = clusterComputeResource.Properties(context.TODO(), clusterComputeResource.Reference(), []string{"network"}, &ccrMo) | |
| if err != nil { | |
| return nil, err | |
| } | |
| for _, networkMoRef := range ccrMo.Network { | |
| networkObj := object.NewNetwork(client, networkMoRef) | |
| networkObjectName, err := networkObj.ObjectName(ctx) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if network == networkObjectName { | |
| importOvaParams.Network = networkObj | |
| break | |
| } | |
| } | |
| datastores, err := clusterComputeResource.Datastores(ctx) | |
| if err != nil { | |
| return nil, err | |
| } | |
| for _, datastoreObj := range datastores { | |
| datastoreObjName, err := datastoreObj.ObjectName(ctx) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if datastore == datastoreObjName { | |
| importOvaParams.Datastore = datastoreObj | |
| break | |
| } | |
| } | |
| hosts, err := clusterComputeResource.Hosts(ctx) | |
| if err != nil { | |
| return nil, err | |
| } | |
| foundDatastore := false | |
| foundNetwork := false | |
| var hostSystemManagedObject mo.HostSystem | |
| for _, hostObj := range hosts { | |
| hostObj.Properties(ctx, hostObj.Reference(), []string{"network", "datastore"}, &hostSystemManagedObject) | |
| if err != nil { | |
| return nil, err | |
| } | |
| for _, dsMoRef := range hostSystemManagedObject.Datastore { | |
| if importOvaParams.Datastore.Reference().Value == dsMoRef.Value { | |
| foundDatastore = true | |
| break | |
| } | |
| } | |
| for _, nMoRef := range hostSystemManagedObject.Network { | |
| if importOvaParams.Network.Reference().Value == nMoRef.Value { | |
| foundNetwork = true | |
| break | |
| } | |
| } | |
| if foundDatastore && foundNetwork { | |
| importOvaParams.Host = hostObj | |
| resourcePool, err := hostObj.ResourcePool(ctx) | |
| if err != nil { | |
| return nil, err | |
| } | |
| importOvaParams.ResourcePool = resourcePool | |
| } | |
| } | |
| return importOvaParams, nil | |
| } | |
| func CreateVSphereClients(ctx context.Context, vcenter, username, password string) (*vim25.Client, *rest.Client, error) { | |
| ctx, cancel := context.WithTimeout(ctx, 60*time.Second) | |
| defer cancel() | |
| u, err := soap.ParseURL(vcenter) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| u.User = url.UserPassword(username, password) | |
| c, err := govmomi.NewClient(ctx, u, true) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| restClient := rest.NewClient(c.Client) | |
| err = restClient.Login(ctx, u.User) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| return c.Client, restClient, nil | |
| } | |
| type Session struct { | |
| Vim25Client *vim25.Client | |
| RestClient *rest.Client | |
| SsoClient *ssoadmin.Client | |
| } | |
| type VCenterUser struct { | |
| //*ssoadmin.AdminPersonUser | |
| AdminPersonUser *ssotypes.AdminPersonUser | |
| AdminGroups *[]ssotypes.PrincipalId | |
| Roles []int32 | |
| } | |
| // GetSession - creates the Session struct | |
| func GetSession(ctx context.Context, p *Platform) (*Session, *VCenterUser, error) { | |
| //spew.Dump(p) | |
| //ctx, cancel := context.WithTimeout(ctx, 60*time.Second) | |
| //defer cancel() | |
| person := &VCenterUser{} | |
| session := &Session{} | |
| header := soap.Header{ | |
| Security: &sts.Signer{}, | |
| } | |
| u, err := soap.ParseURL(p.VCenter) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| u.User = url.UserPassword(p.Username, p.Password) | |
| if u.User == nil { | |
| spew.Dump("user nil") | |
| } | |
| // false in this method disables insecure | |
| // We do not allow insecure connections | |
| govmomiClient, err := govmomi.NewClient(ctx, u, true) | |
| //spew.Dump(c.Client) | |
| vimClient, err := vim25.NewClient(ctx, soap.NewClient(u, true)) | |
| stsClient, cerr := sts.NewClient(ctx, vimClient) | |
| if cerr != nil { | |
| return nil, nil, cerr | |
| } | |
| ssoClient, err := ssoadmin.NewClient(ctx, vimClient) | |
| req := sts.TokenRequest{ | |
| Certificate: vimClient.Certificate(), | |
| Userinfo: u.User, | |
| } | |
| header.Security, cerr = stsClient.Issue(ctx, req) | |
| if cerr != nil { | |
| return nil, nil, cerr | |
| } | |
| //spew.Dump(header.Security) | |
| if err = ssoClient.Login(ssoClient.WithHeader(ctx, header)); err != nil { | |
| return nil, nil, err | |
| } | |
| // **TODO** | |
| // **TODO** | |
| // **TODO** | |
| // ******** | |
| // Do not forget to check for nil | |
| // this is esp. the case when | |
| // vCenter is unhappy | |
| // ******** | |
| session.SsoClient = ssoClient | |
| personUser, err := ssoClient.FindPersonUser(ctx, "jcallen@vsphere.local") | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| //spew.Dump(personUser) | |
| person.AdminPersonUser = personUser | |
| /* | |
| adminGroups, err := ssoClient.FindGroups(ctx, "") | |
| if err != nil { | |
| return nil, err | |
| } | |
| spew.Dump(adminGroups) | |
| */ | |
| groups, err := ssoClient.FindParentGroups(ctx, personUser.Id) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| person.AdminGroups = &groups | |
| //spew.Dump(groups) | |
| //spew.Dump(ssoClient) | |
| /* | |
| defer func() { | |
| if err := ssoClient.Logout(ctx); err != nil { | |
| log.Printf("user logout error: %v", err) | |
| } | |
| }() | |
| */ | |
| session.Vim25Client = govmomiClient.Client | |
| //spew.Dump(session) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| restClient := rest.NewClient(vimClient) | |
| err = restClient.Login(ctx, u.User) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| session.RestClient = restClient | |
| //spew.Dump(session) | |
| return session, person, nil | |
| } | |
| //type PermissionManagedObjectType string | |
| //use ManagedObjectReference type string | |
| //Each ManagedObjectReference | |
| const ( | |
| PrivilegesDatacenter string = "Datacenter" | |
| PrivilegesResourcePool string = "ResourcePool" | |
| PrivilegesFolder string = "Folder" | |
| PrivilegesDatastore string = "Datastore" | |
| PrivilegesClusterComputeResource string = "ClusterComputeResource" | |
| // there is no VirtualMachine object to check privilege | |
| ) | |
| /* | |
| StorageProfile.View (Profile-driven storage view) vCenter No | |
| */ | |
| var managedObjectRequiredPrivileges = map[string]map[string]bool{ | |
| PrivilegesResourcePool: { | |
| "VApp.Import": false, | |
| }, | |
| PrivilegesDatacenter: { | |
| "VirtualMachine.Interact.PowerOff": false, | |
| "VirtualMachine.Interact.PowerOn": false, | |
| }, | |
| PrivilegesDatastore: { | |
| "Datastore.AllocateSpace": false, | |
| "Datastore.FileManagement": false, | |
| }, | |
| PrivilegesFolder: { | |
| "Resource.AssignVMToPool": false, | |
| "VirtualMachine.Config.AddExistingDisk": false, | |
| "VirtualMachine.Config.AddNewDisk": false, | |
| "VirtualMachine.Config.AddRemoveDevice": false, | |
| "VirtualMachine.Config.RemoveDisk": false, | |
| "VirtualMachine.Inventory.Create": false, | |
| "VirtualMachine.Inventory.Delete": false, | |
| "VirtualMachine.Config.Settings": false, | |
| }, | |
| PrivilegesClusterComputeResource: { | |
| "Resource.AssignVMToPool": false, | |
| "VirtualMachine.Config.AddExistingDisk": false, | |
| "VirtualMachine.Config.AddNewDisk": false, | |
| "VirtualMachine.Config.AddRemoveDevice": false, | |
| "VirtualMachine.Config.RemoveDisk": false, | |
| "VirtualMachine.Inventory.Create": false, | |
| "VirtualMachine.Inventory.Delete": false, | |
| "VirtualMachine.Config.Settings": false, | |
| }, | |
| } | |
| // logon could be a group too | |
| // just needs to be in the form domain/username | |
| func RoleIdsByLogon(authManager *object.AuthorizationManager, ref types.ManagedObjectReference, domain, name string) ([]int32, error) { | |
| ctx := context.TODO() | |
| var roleIds []int32 | |
| logon := fmt.Sprintf("%s/%s", strings.ToLower(domain), strings.ToLower(name)) | |
| permissions, err := authManager.RetrieveEntityPermissions(ctx, ref, true) | |
| if err != nil { | |
| return nil, err | |
| } | |
| // p.Principal is in the form of domain/username | |
| for _, p := range permissions { | |
| if strings.ToLower(p.Principal) == logon { | |
| roleIds = append(roleIds, p.RoleId) | |
| } | |
| } | |
| return roleIds, nil | |
| } | |
| // TempFunctionName - stop bugging me.... | |
| func TempFunctionName(moRef types.ManagedObjectReference, roleList *object.AuthorizationRoleList, roleIds []int32) { | |
| // For all the role ids the domain/name (username) | |
| // is apart of. Get the authorizationRole by id | |
| // for all Privileges contained within the role | |
| // | |
| for _, roleid := range roleIds { | |
| authorizationRole := roleList.ById(roleid) | |
| for _, p := range authorizationRole.Privilege { | |
| // How will I know if I have all my privileges? | |
| if _, ok := managedObjectRequiredPrivileges[moRef.Type][p]; ok { | |
| managedObjectRequiredPrivileges[moRef.Type][p] = true | |
| } | |
| } | |
| } | |
| for rpkey := range managedObjectRequiredPrivileges[moRef.Type] { | |
| if value, ok := managedObjectRequiredPrivileges[moRef.Type][rpkey]; ok { | |
| if !value { | |
| spew.Sprintf("missing privilege: %s", rpkey) | |
| } | |
| } | |
| } | |
| } | |
| func ValidateCreds(ssn *Session, p *Platform, person *VCenterUser) error { | |
| // What is my user account? | |
| // What group(s) is my user account in? | |
| // For the datacenter/host/cluster | |
| // user - role association | |
| // group - role association | |
| // what permissions do the roles contain? | |
| ctx := context.TODO() | |
| authManager := object.NewAuthorizationManager(ssn.Vim25Client) | |
| roleList, err := authManager.RoleList(ctx) | |
| if err != nil { | |
| return err | |
| } | |
| finder := find.NewFinder(ssn.Vim25Client) | |
| datacenter, err := finder.Datacenter(ctx, p.Datacenter) | |
| if err != nil { | |
| return err | |
| } | |
| //permissions, err := authManager.RetrieveEntityPermissions(ctx, datacenter.Reference(), true) | |
| roleIds, err := RoleIdsByLogon(authManager, datacenter.Reference(), "VCENTER.LOCAL", "PowerUsers") | |
| if err != nil { | |
| return err | |
| } | |
| // For all the role ids the domain/name (username) | |
| // is apart of. Get the authorizationRole by id | |
| // for all Privileges contained within the role | |
| // | |
| for _, roleid := range roleIds { | |
| authorizationRole := roleList.ById(roleid) | |
| for _, p := range authorizationRole.Privilege { | |
| // How will I know if I have all my privileges? | |
| if _, ok := managedObjectRequiredPrivileges[datacenter.Reference().Type][p]; ok { | |
| managedObjectRequiredPrivileges[datacenter.Reference().Type][p] = true | |
| } | |
| } | |
| } | |
| for rpkey := range managedObjectRequiredPrivileges[datacenter.Reference().Type] { | |
| if value, ok := managedObjectRequiredPrivileges[datacenter.Reference().Type][rpkey]; ok { | |
| if !value { | |
| spew.Sprintf("missing privilege: %s", rpkey) | |
| } | |
| } | |
| } | |
| /* | |
| if err != nil { | |
| return err | |
| } | |
| spew.Dump(roleList) | |
| */ | |
| /* | |
| personUser, err := ssn.SsoClient.FindPersonUser(ctx, p.Username) | |
| if err != nil { | |
| return err | |
| } | |
| dynamicData := personUser.GetDynamicData() | |
| spew.Dump(dynamicData) | |
| */ | |
| /* | |
| for _, p := range permissions { | |
| spew.Printf("prin: %s roleid: %d group: %t\n", p.Principal, p.RoleId, p.Group) | |
| } | |
| */ | |
| return nil | |
| } | |
| func main() { | |
| //datacenter := "dc1" | |
| //defaultDatastore := "NVMe" | |
| //ctx := context.TODO() | |
| //networkName := "VM Network" | |
| platform := &Platform{ | |
| VCenter: "", | |
| Password: "", | |
| Username: "", | |
| Datacenter: "dc1", | |
| Cluster: "cluster1", | |
| } | |
| /* | |
| client, _, err := CreateVSphereClients( | |
| context.TODO(), | |
| "vcenter.virtomation.com", "", "") | |
| //"vcenter.virtomation.com", "", "") | |
| spew.Dump(client) | |
| */ | |
| spew.Dump("Before GetSession") | |
| ssn, person, err := GetSession(context.TODO(), platform) | |
| if err != nil { | |
| spew.Dump(err) | |
| return | |
| } | |
| spew.Dump(person) | |
| spew.Dump("After GetSession, Before ValidateCreds") | |
| //spew.Dump(ssn) | |
| err = ValidateCreds(ssn, platform, person) | |
| if err != nil { | |
| spew.Dump(err) | |
| } | |
| spew.Dump("After ValidateCreds") | |
| //i, err := findImportOvaParams(client, "dc1", "cluster1", "NVMe", "VM Network") | |
| //spew.Dump(i) | |
| //spew.Dump(err) | |
| /* | |
| cachedImage := "rhcos-latest.ova" | |
| ovaTapeArchive := &TapeArchive{Path: cachedImage} | |
| ovaTapeArchive.Client = client | |
| archiveFlag := &ArchiveFlag{} | |
| archiveFlag.Archive = ovaTapeArchive | |
| ovfDescriptor, err := archiveFlag.ReadOvf("desc.ovf") | |
| if err != nil { | |
| spew.Dump(err) | |
| } | |
| ovfEnvelope, err := archiveFlag.ReadEnvelope(ovfDescriptor) | |
| if err != nil { | |
| spew.Dump(errors.Errorf("failed to parse ovf: %s", err)) | |
| } | |
| if err != nil { | |
| spew.Dump(err) | |
| return | |
| } | |
| name := "rhcos-test-image" | |
| */ | |
| /* | |
| defaultNetwork, err := finder.Network(context.TODO(), "VM Network") | |
| spew.Dump(defaultNetwork) | |
| if err != nil { | |
| spew.Dump(defaultNetwork) | |
| spew.Dump("default network") | |
| spew.Dump(err) | |
| return | |
| } | |
| datastore, err := finder.Datastore(ctx, defaultDatastore) | |
| if err != nil { | |
| spew.Dump(err) | |
| return | |
| } | |
| host, err := finder.DefaultHostSystem(ctx) | |
| if err != nil { | |
| spew.Dump(err) | |
| return | |
| } | |
| resourcePool, err := host.ResourcePool(ctx) | |
| if err != nil { | |
| spew.Dump(err) | |
| return | |
| } | |
| networkMappings := []types.OvfNetworkMapping{{ | |
| Name: ovfEnvelope.Network.Networks[0].Name, | |
| Network: defaultNetwork.Reference(), | |
| }} | |
| cisp := types.OvfCreateImportSpecParams{ | |
| EntityName: name, | |
| NetworkMapping: networkMappings, | |
| } | |
| spew.Dump(cisp) | |
| m := ovf.NewManager(client) | |
| spec, err := m.CreateImportSpec(ctx, string(ovfDescriptor), | |
| resourcePool.Reference(), | |
| datastore.Reference(), cisp) | |
| if err != nil { | |
| spew.Dump("CreateImportSpec") | |
| spew.Dump(err) | |
| return | |
| } | |
| if spec.Error != nil { | |
| spew.Dump(errors.New(spec.Error[0].LocalizedMessage)) | |
| return | |
| } | |
| if spec.Warning != nil { | |
| for _, w := range spec.Warning { | |
| logrus.Warn(w.LocalizedMessage) | |
| } | |
| } | |
| folder, err := finder.DefaultFolder(context.TODO()) | |
| if err != nil { | |
| spew.Dump("folder") | |
| spew.Dump(err) | |
| } | |
| lease, err := resourcePool.ImportVApp(ctx, spec.ImportSpec, folder, host) | |
| if err != nil { | |
| spew.Dump("ImportVApp") | |
| spew.Dump(err) | |
| return | |
| } | |
| spew.Dump(lease) | |
| spew.Dump(spec.FileItem) | |
| info, err := lease.Wait(ctx, spec.FileItem) | |
| if err != nil { | |
| spew.Dump("wait") | |
| spew.Dump(err) | |
| return | |
| } | |
| //return errors.Errorf("folder: %s", folder) | |
| u := lease.StartUpdater(ctx, info) | |
| defer u.Done() | |
| for _, i := range info.Items { | |
| err = Upload(ctx, archiveFlag, lease, i) | |
| if err != nil { | |
| spew.Dump("upload") | |
| spew.Dump(err) | |
| return | |
| } | |
| } | |
| err = lease.Complete(ctx) | |
| if err != nil { | |
| spew.Dump("lease complete") | |
| spew.Dump(err) | |
| return | |
| } | |
| vm := object.NewVirtualMachine(client, info.Entity) | |
| spew.Dump(vm.Name) | |
| */ | |
| return | |
| } | |
| /* | |
| func Upload(ctx context.Context, archive *ArchiveFlag, lease *nfc.Lease, item nfc.FileItem) error { | |
| file := item.Path | |
| f, size, err := archive.Open(file) | |
| if err != nil { | |
| return err | |
| } | |
| defer f.Close() | |
| // logger := cmd.ProgressLogger(fmt.Sprintf("Uploading %s... ", path.Base(file))) | |
| // defer logger.Wait() | |
| opts := soap.Upload{ | |
| ContentLength: size, | |
| // Progress: logger, | |
| } | |
| return lease.Upload(ctx, item, f, opts) | |
| } | |
| */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment