Browse Source

First commit

Rufus Deponian 3 years ago
commit
689e7ebe19
3 changed files with 294 additions and 0 deletions
  1. 95 0
      main.go
  2. 12 0
      project_and_roles_template.conf
  3. 187 0
      utils.go

+ 95 - 0
main.go

@@ -0,0 +1,95 @@
+package main
+
+import (
+	"fmt"
+
+	"github.com/alyu/configparser"
+	"github.com/gophercloud/gophercloud/openstack/identity/v3/projects"
+)
+
+func main() {
+	domainID := "614b64b5d4ce4e989ad0590ec56b021f" // mephi domain
+
+	var osclient OpenstackIdentityClient
+	osclient.client = initIdentityClient()
+
+	config, err := configparser.Read("./project_and_roles.conf")
+	checkErr(err)
+	osclient.config = config
+
+	// allProjects := osclient.getProjectsMap(domainID)
+	// for _, project := range allProjects {
+	// 	fmt.Println(project)
+	// }
+	// allUnits := getCleanUnitsMap(osclient.config)
+	// for _, unit := range allUnits {
+	// 	fmt.Println(*unit.BriefName)
+	// }
+
+	fmt.Println("Syncing projects...")
+	osclient.syncProjects(domainID)
+	// fmt.Println("Deleting projects...")
+	// osclient.deleteAllProjects(domainID)
+	//fmt.Println("Syncing roles...")
+	//syncRoles(domainID)
+}
+
+func (osclient OpenstackIdentityClient) syncProjects(domainID string) {
+	allUnits := getCleanUnitsMap(osclient.config)
+	allProjects := osclient.getProjectsMap(domainID)
+
+	for persNumber, unit := range allUnits {
+		if _, ok := allProjects[persNumber]; ok {
+			updateOpts := makeUpdateOpts(unit, domainID)
+			projectID := allProjects[persNumber].ID
+			updateResult := projects.Update(osclient.client, projectID, updateOpts)
+			checkErr(updateResult.Err)
+			fmt.Printf("Project \"%s\" updated\n", *unit.BriefName)
+		} else {
+			createOpts := makeCreateOpts(unit, domainID)
+			createResult := projects.Create(osclient.client, createOpts)
+			checkErr(createResult.Err)
+			fmt.Printf("Project \"%s\" created\n", *unit.BriefName)
+		}
+	}
+}
+
+func (osclient OpenstackIdentityClient) createOrUpdateParentIDs(domainID string) {
+	allProjects := osclient.getProjectsMap(domainID)
+
+	for _, project := range allProjects {
+		if project.Extra["ParentPersNumber"] == nil {
+			continue
+		}
+		parentPersNumber := int(project.Extra["ParentPersNumber"].(float64))
+		parent, ok := allProjects[parentPersNumber]
+		if !ok {
+			panic(fmt.Sprintf("Cannot find parent for %s", project.Name))
+		}
+		if project.ParentID != parent.ID {
+			updateOpts := projects.UpdateOpts{
+				ParentID: parent.ID,
+			}
+			updateResult := projects.Update(osclient.client, project.ID, updateOpts)
+			checkErr(updateResult.Err)
+			fmt.Printf("Parent ID for %s set", project.Name)
+		}
+	}
+}
+
+func (osclient OpenstackIdentityClient) deleteAllProjects(domainID string) {
+	allProjects := osclient.getProjectsMap(domainID)
+
+	index := 1
+	amount := len(allProjects)
+	for _, project := range allProjects {
+		deleteResult := projects.Delete(osclient.client, project.ID)
+		checkErr(deleteResult.ExtractErr())
+		fmt.Printf("[%v/%v] Project \"%s\" deleted\n", index, amount, project.Name)
+		index++
+	}
+}
+
+func syncRoles() {
+
+}

+ 12 - 0
project_and_roles_template.conf

@@ -0,0 +1,12 @@
+[sd.mephi.ru]
+api_key = Your-API-Key-Here
+
+[openstack]
+identity_endpoint = http://controller-IP-Here:5000/v3
+domain_name = mephi
+project_name = UserProjectName
+
+# You have to use admin level user here
+username = GeraltFromRivia
+password = Plotva
+

+ 187 - 0
utils.go

@@ -0,0 +1,187 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+	"unicode"
+
+	"devel.mephi.ru/dyokunev/go-sdapi/sdApi1"
+	"github.com/alyu/configparser"
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/identity/v3/projects"
+
+	models "devel.mephi.ru/dyokunev/go-asu-models"
+)
+
+// H E L P E R S
+
+func checkErr(err error) {
+	if err != nil {
+		panic(err)
+	}
+}
+
+func pointerFromBool(b bool) *bool {
+	return &b
+}
+
+func capitalize(str string) string {
+	runes := []rune(str)
+	runes[0] = unicode.ToUpper(runes[0])
+	return string(runes)
+}
+
+// O P E N S T A C K   C L I E N T
+
+type OpenstackIdentityClient struct {
+	client *gophercloud.ServiceClient
+	config *configparser.Configuration
+}
+
+func initIdentityClient() *gophercloud.ServiceClient {
+	config, err := configparser.Read("./project_and_roles.conf")
+	checkErr(err)
+
+	section, err := config.Section("openstack")
+	checkErr(err)
+
+	opts := gophercloud.AuthOptions{
+		IdentityEndpoint: section.ValueOf("identity_endpoint"),
+		DomainName:       section.ValueOf("domain_name"),
+		TenantName:       section.ValueOf("project_name"),
+		Username:         section.ValueOf("username"),
+		Password:         section.ValueOf("password"),
+	}
+
+	provider, err := openstack.AuthenticatedClient(opts)
+	checkErr(err)
+
+	client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{
+		Region: "Moscow",
+	})
+	checkErr(err)
+
+	return client
+}
+
+func projectsToMap(allProjects []projects.Project) map[int]projects.Project {
+	projectsMap := map[int]projects.Project{}
+
+	for _, project := range allProjects {
+		if project.Extra["PersNumber"] == nil {
+			panic(fmt.Sprintf("Project %s has no persNumber", project.Name))
+		}
+		persNumber := int(project.Extra["PersNumber"].(float64))
+		projectsMap[persNumber] = project
+	}
+
+	return projectsMap
+}
+
+func (osclient OpenstackIdentityClient) getProjectsMap(domainID string) map[int]projects.Project {
+	listOpts := projects.ListOpts{
+		DomainID: domainID,
+		Enabled:  pointerFromBool(true),
+		IsDomain: pointerFromBool(false),
+	}
+
+	allPages, err := projects.List(osclient.client, listOpts).AllPages()
+	checkErr(err)
+
+	allProjects, err := projects.ExtractProjects(allPages)
+	checkErr(err)
+
+	return projectsToMap(allProjects)
+}
+
+func makeUpdateOpts(unit models.Unit, domainID string) projects.UpdateOpts {
+	updateOpts := projects.UpdateOpts{
+		DomainID:    domainID,
+		Enabled:     pointerFromBool(true),
+		IsDomain:    pointerFromBool(false),
+		Name:        fmt.Sprintf("%s [%v]", *unit.BriefName, *unit.PersNumber),
+		Description: *unit.Name,
+		Extra: map[string]interface{}{
+			"ParentPersNumber": unit.ParentId},
+	}
+
+	return updateOpts
+}
+
+func makeCreateOpts(unit models.Unit, domainID string) projects.CreateOpts {
+	createOpts := projects.CreateOpts{
+		DomainID:    domainID,
+		Enabled:     pointerFromBool(true),
+		IsDomain:    pointerFromBool(false),
+		Name:        fmt.Sprintf("%s [%v]", *unit.BriefName, *unit.PersNumber),
+		Description: *unit.Name,
+		Extra: map[string]interface{}{
+			"PersNumber":       unit.PersNumber,
+			"ParentPersNumber": unit.ParentId},
+	}
+
+	return createOpts
+}
+
+// U N I T S
+
+func getAllUnits(config *configparser.Configuration) models.Units {
+	section, err := config.Section("sd.mephi.ru")
+	checkErr(err)
+
+	sdApi1.SetApiKey(section.ValueOf("api_key"))
+
+	allUnits, err := sdApi1.GetUnits()
+	checkErr(err)
+
+	return allUnits
+}
+
+func getCleanUnitsMap(config *configparser.Configuration) map[int]models.Unit {
+	allUnits := getAllUnits(config)
+	idToPersNum := map[int]int{}
+	cleanUnits := map[int]models.Unit{}
+
+	// Get all not nil units without duplicates
+	for _, unit := range allUnits {
+		if unit.PersNumber == nil || unit.IsHidden == 1 || unit.Name == nil || unit.BriefName == nil {
+			continue
+		}
+
+		idToPersNum[unit.Id] = *unit.PersNumber
+		existingUnit, ok := cleanUnits[*unit.PersNumber]
+		if !ok {
+			cleanUnits[*unit.PersNumber] = unit
+		} else {
+			cleanUnits[*unit.PersNumber] = newestUnit(existingUnit, unit)
+		}
+	}
+
+	for persNumber, unit := range cleanUnits {
+		// Change parent ID to parent persNumber
+		if unit.ParentId != nil {
+			parentPersNumber := idToPersNum[*unit.ParentId]
+			unit.ParentId = &parentPersNumber
+		}
+		// Clean names
+		*unit.Name = strings.Join(strings.Fields(*unit.Name), " ")
+		*unit.Name = capitalize(*unit.Name)
+		*unit.BriefName = strings.Join(strings.Fields(*unit.BriefName), " ")
+		*unit.BriefName = strings.ToUpper(*unit.BriefName)
+		cleanUnits[persNumber] = unit
+	}
+
+	return cleanUnits
+}
+
+func newestUnit(unit1 models.Unit, unit2 models.Unit) models.Unit {
+	if unit1.CreateOrderDate != nil && unit2.CreateOrderDate != nil {
+		if unit1.CreateOrderDate.Unix() > unit2.CreateOrderDate.Unix() {
+			return unit1
+		}
+		return unit2
+	}
+	return unit1
+
+}