// 2020 Rackspace, Inc. // // 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. package common import ( "context" "fmt" "net/url" "time" dcmTools "github.rackspace.com/DCM/tools" "github.rackspace.com/gorax/clients/dcxAutohost" "github.rackspace.com/gorax/passkey" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) const ( // DBVlanTranslation is an exportable constant that can be used to reference the // vlan translations collection DBVlanTranslation = "vlanTranslations" // DBPortMap is an exportable constant used to reference the port-maps collection DBPortMap = "portMaps" ) // DB is an inherited mongo.Client we can use to attach additional functionality // onto the existing mongo client. type DB struct { *mongo.Client } // IContains function will perform a case-insensitive partial search for a substring // within a string. func NewDB(key *passkey.Client) (*DB, error) { dbKey := key.Keys.Mongo.NSIMongoUser.Credential mgoURI := fmt.Sprintf("mongodb://%s:%s@%s/migration?ssl=true", dbKey.Username, url.QueryEscape(dbKey.Password), dbKey.HostName) mgo, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(mgoURI)) if err != nil { return nil, err } return &DB{mgo}, nil } // GetTranslations will fetch any existing translations for an environment number. If translations exist, we will map // them to the vlans we already discovered. func (c DB) GetTranslations(env string) ([]dcxAutohost.EnvironmentMigrationVlan, error) { ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) col := c.Database(dcmTools.MigrationDB).Collection(DBVlanTranslation) defer cancel() cursor := col.FindOne(ctx, bson.M{"_id": env}) // Get translations from the migrationDB. type req struct { ID string `json:"_id" bson:"_id"` Xlates []dcxAutohost.EnvironmentMigrationVlan `json:"vlan_translations" bson:"vlan_translations"` } var payload req if err := cursor.Decode(&payload); err != nil { if err != mongo.ErrNoDocuments { return nil, err } } return payload.Xlates, nil } // GetPublicTranslation will fetch any existing translations for a migration zone. If translations exist, we will map // them to the vlans we already discovered. func (c DB) GetPublicTranslation(migrationZone string) (*dcxAutohost.EnvironmentMigrationVlan, error) { ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) col := c.Database(dcmTools.MigrationDB).Collection(DBVlanTranslation) defer cancel() cursor, err := col.Find(ctx, bson.M{"vlan_translations.migration_vlan.zone_name": migrationZone}) if err != nil { return nil, err } // Get translations from the migrationDB. type req struct { ID string `json:"_id" bson:"_id"` Xlates []dcxAutohost.EnvironmentMigrationVlan `json:"vlan_translations" bson:"vlan_translations"` } var payload []req if err := cursor.All(ctx, &payload); err != nil { if err == mongo.ErrNoDocuments { return nil, nil } return nil, err } for _, item := range payload { for _, x := range item.Xlates { if x.MigrationVlan.ZoneName == migrationZone { return &x, nil } } } return nil, nil } // WriteTranslations will write or overwrite any vlan translations that we have built as part of the // environment manager update process. Once we create vlans in EM, we store the result as translated vlans // and we can write them to the DB here. func (c *DB) WriteTranslations(env string, vlans []Translation) error { ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) col := c.Database(dcmTools.MigrationDB).Collection(DBVlanTranslation) opts := options.Update().SetUpsert(true) defer cancel() if _, err := col.UpdateOne(ctx, bson.M{"_id": env}, bson.M{"$set": bson.M{"vlan_translations": vlans}}, opts); err != nil { return err } return nil } // GetPortMaps will fetch any existing translations for an environment number. If translations exist, we will map // them to the vlans we already discovered. func (c DB) GetPortMaps(device int) (dcxAutohost.DevicePortMap, error) { ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) col := c.Database(dcmTools.MigrationDB).Collection(DBPortMap) defer cancel() cursor := col.FindOne(ctx, bson.M{"_id": device}) // Get translations from the migrationDB. type req struct { ID int `json:"_id" bson:"_id"` PortMaps dcxAutohost.DevicePortMap `json:"port_maps" bson:"port_maps"` } var payload req if err := cursor.Decode(&payload); err != nil { if err != mongo.ErrNoDocuments { return payload.PortMaps, err } } return payload.PortMaps, nil } // WritePortMaps will write or overwrite any device port-mappings that we have built as part of the // discovery process. After we have joined a device to the environment, we can write the port-maps here. func (c *DB) WritePortMaps(device int, portMap dcxAutohost.DevicePortMap) error { ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) col := c.Database(dcmTools.MigrationDB).Collection(DBPortMap) opts := options.Update().SetUpsert(true) defer cancel() if _, err := col.UpdateOne(ctx, bson.M{"_id": device}, bson.M{"$set": bson.M{"port_maps": portMap}}, opts); err != nil { return err } return nil }