diff --git a/client/contivModel.js b/client/contivModel.js index 2ea3b6d..761c9d2 100644 --- a/client/contivModel.js +++ b/client/contivModel.js @@ -676,6 +676,71 @@ var ServiceInstanceModalView = React.createClass({ module.exports.ServiceInstanceSummaryView = ServiceInstanceSummaryView module.exports.ServiceInstanceModalView = ServiceInstanceModalView +var ServiceLBSummaryView = React.createClass({ + render: function() { + var self = this + + // Walk thru all objects + var serviceLBListView = self.props.serviceLBs.map(function(serviceLB){ + return ( + }> + + + + + + ); + }); + + return ( +
+ + + + + + + + + { serviceLBListView } + +
+
+ ); + } +}); + +var ServiceLBModalView = React.createClass({ + render() { + var obj = this.props.serviceLB + return ( + +
+ + + + + + + + + + + + + + +
+
+ +
+
+ ); + } +}); + +module.exports.ServiceLBSummaryView = ServiceLBSummaryView +module.exports.ServiceLBModalView = ServiceLBModalView var TenantSummaryView = React.createClass({ render: function() { var self = this diff --git a/client/contivModelClient.go b/client/contivModelClient.go index 73cf0a3..82ebcbf 100644 --- a/client/contivModelClient.go +++ b/client/contivModelClient.go @@ -238,6 +238,7 @@ type Network struct { type NetworkLinkSets struct { AppProfiles map[string]Link `json:"AppProfiles,omitempty"` EndpointGroups map[string]Link `json:"EndpointGroups,omitempty"` + Servicelbs map[string]Link `json:"Servicelbs,omitempty"` Services map[string]Link `json:"Services,omitempty"` } @@ -349,6 +350,25 @@ type ServiceInstanceLinks struct { Service Link `json:"Service,omitempty"` } +type ServiceLB struct { + // every object has a key + Key string `json:"key,omitempty"` + + IpAddress string `json:"ipAddress,omitempty"` // Service ip + Network string `json:"network,omitempty"` // Service network name + Ports []string `json:"ports,omitempty"` + Selectors []string `json:"selectors,omitempty"` + ServiceName string `json:"serviceName,omitempty"` // service name + TenantName string `json:"tenantName,omitempty"` // Tenant Name + + Links ServiceLBLinks `json:"links,omitempty"` +} + +type ServiceLBLinks struct { + Network Link `json:"Network,omitempty"` + Tenant Link `json:"Tenant,omitempty"` +} + type Tenant struct { // every object has a key Key string `json:"key,omitempty"` @@ -365,6 +385,7 @@ type TenantLinkSets struct { EndpointGroups map[string]Link `json:"EndpointGroups,omitempty"` Networks map[string]Link `json:"Networks,omitempty"` Policies map[string]Link `json:"Policies,omitempty"` + Servicelbs map[string]Link `json:"Servicelbs,omitempty"` VolumeProfiles map[string]Link `json:"VolumeProfiles,omitempty"` Volumes map[string]Link `json:"Volumes,omitempty"` } @@ -1002,6 +1023,71 @@ func (c *ContivClient) ServiceInstanceDelete(tenantName string, appName string, return nil } +// ServiceLBPost posts the serviceLB object +func (c *ContivClient) ServiceLBPost(obj *ServiceLB) error { + // build key and URL + keyStr := obj.ServiceName + ":" + obj.TenantName + url := c.baseURL + "/api/serviceLBs/" + keyStr + "/" + + // http post the object + err := httpPost(url, obj) + if err != nil { + log.Debugf("Error creating serviceLB %+v. Err: %v", obj, err) + return err + } + + return nil +} + +// ServiceLBList lists all serviceLB objects +func (c *ContivClient) ServiceLBList() (*[]*ServiceLB, error) { + // build key and URL + url := c.baseURL + "/api/serviceLBs/" + + // http get the object + var objList []*ServiceLB + err := httpGet(url, &objList) + if err != nil { + log.Debugf("Error getting serviceLBs. Err: %v", err) + return nil, err + } + + return &objList, nil +} + +// ServiceLBGet gets the serviceLB object +func (c *ContivClient) ServiceLBGet(serviceName string, tenantName string) (*ServiceLB, error) { + // build key and URL + keyStr := serviceName + ":" + tenantName + url := c.baseURL + "/api/serviceLBs/" + keyStr + "/" + + // http get the object + var obj ServiceLB + err := httpGet(url, &obj) + if err != nil { + log.Debugf("Error getting serviceLB %+v. Err: %v", keyStr, err) + return nil, err + } + + return &obj, nil +} + +// ServiceLBDelete deletes the serviceLB object +func (c *ContivClient) ServiceLBDelete(serviceName string, tenantName string) error { + // build key and URL + keyStr := serviceName + ":" + tenantName + url := c.baseURL + "/api/serviceLBs/" + keyStr + "/" + + // http get the object + err := httpDelete(url) + if err != nil { + log.Debugf("Error deleting serviceLB %s. Err: %v", keyStr, err) + return err + } + + return nil +} + // TenantPost posts the tenant object func (c *ContivClient) TenantPost(obj *Tenant) error { // build key and URL diff --git a/client/contivModelClient.py b/client/contivModelClient.py index 04d999d..bcd9238 100644 --- a/client/contivModelClient.py +++ b/client/contivModelClient.py @@ -218,6 +218,7 @@ def createNetwork(self, obj): "encap": obj.encap, "gateway": obj.gateway, "networkName": obj.networkName, + "nwType": obj.nwType, "pktTag": obj.pktTag, "subnet": obj.subnet, "tenantName": obj.tenantName, @@ -398,6 +399,42 @@ def listServiceInstance(self): if retData == "Error": errorExit("list ServiceInstance failed") + return json.loads(retData) + # Create serviceLB + def createServiceLB(self, obj): + postUrl = self.baseUrl + '/api/serviceLBs/' + obj.serviceName + ":" + obj.tenantName + '/' + + jdata = json.dumps({ + "ipAddress": obj.ipAddress, + "network": obj.network, + "ports": obj.ports, + "selectors": obj.selectors, + "serviceName": obj.serviceName, + "tenantName": obj.tenantName, + }) + + # Post the data + response = httpPost(postUrl, jdata) + + if response == "Error": + errorExit("ServiceLB create failure") + + # Delete serviceLB + def deleteServiceLB(self, serviceName, tenantName): + # Delete ServiceLB + deleteUrl = self.baseUrl + '/api/serviceLBs/' + serviceName + ":" + tenantName + '/' + response = httpDelete(deleteUrl) + + if response == "Error": + errorExit("ServiceLB create failure") + + # List all serviceLB objects + def listServiceLB(self): + # Get a list of serviceLB objects + retDate = urllib2.urlopen(self.baseUrl + '/api/serviceLBs/') + if retData == "Error": + errorExit("list ServiceLB failed") + return json.loads(retData) # Create tenant def createTenant(self, obj): diff --git a/contivModel.go b/contivModel.go index a9c4308..6cf21b3 100644 --- a/contivModel.go +++ b/contivModel.go @@ -107,6 +107,7 @@ type Network struct { type NetworkLinkSets struct { AppProfiles map[string]modeldb.Link `json:"AppProfiles,omitempty"` EndpointGroups map[string]modeldb.Link `json:"EndpointGroups,omitempty"` + Servicelbs map[string]modeldb.Link `json:"Servicelbs,omitempty"` Services map[string]modeldb.Link `json:"Services,omitempty"` } @@ -218,6 +219,25 @@ type ServiceInstanceLinks struct { Service modeldb.Link `json:"Service,omitempty"` } +type ServiceLB struct { + // every object has a key + Key string `json:"key,omitempty"` + + IpAddress string `json:"ipAddress,omitempty"` // Service ip + Network string `json:"network,omitempty"` // Service network name + Ports []string `json:"ports,omitempty"` + Selectors []string `json:"selectors,omitempty"` + ServiceName string `json:"serviceName,omitempty"` // service name + TenantName string `json:"tenantName,omitempty"` // Tenant Name + + Links ServiceLBLinks `json:"links,omitempty"` +} + +type ServiceLBLinks struct { + Network modeldb.Link `json:"Network,omitempty"` + Tenant modeldb.Link `json:"Tenant,omitempty"` +} + type Tenant struct { // every object has a key Key string `json:"key,omitempty"` @@ -234,6 +254,7 @@ type TenantLinkSets struct { EndpointGroups map[string]modeldb.Link `json:"EndpointGroups,omitempty"` Networks map[string]modeldb.Link `json:"Networks,omitempty"` Policies map[string]modeldb.Link `json:"Policies,omitempty"` + Servicelbs map[string]modeldb.Link `json:"Servicelbs,omitempty"` VolumeProfiles map[string]modeldb.Link `json:"VolumeProfiles,omitempty"` Volumes map[string]modeldb.Link `json:"Volumes,omitempty"` } @@ -296,6 +317,7 @@ type Collections struct { rules map[string]*Rule services map[string]*Service serviceInstances map[string]*ServiceInstance + serviceLBs map[string]*ServiceLB tenants map[string]*Tenant volumes map[string]*Volume volumeProfiles map[string]*VolumeProfile @@ -357,6 +379,12 @@ type ServiceInstanceCallbacks interface { ServiceInstanceDelete(serviceInstance *ServiceInstance) error } +type ServiceLBCallbacks interface { + ServiceLBCreate(serviceLB *ServiceLB) error + ServiceLBUpdate(serviceLB, params *ServiceLB) error + ServiceLBDelete(serviceLB *ServiceLB) error +} + type TenantCallbacks interface { TenantCreate(tenant *Tenant) error TenantUpdate(tenant, params *Tenant) error @@ -385,6 +413,7 @@ type CallbackHandlers struct { RuleCb RuleCallbacks ServiceCb ServiceCallbacks ServiceInstanceCb ServiceInstanceCallbacks + ServiceLBCb ServiceLBCallbacks TenantCb TenantCallbacks VolumeCb VolumeCallbacks VolumeProfileCb VolumeProfileCallbacks @@ -402,6 +431,7 @@ func Init() { collections.rules = make(map[string]*Rule) collections.services = make(map[string]*Service) collections.serviceInstances = make(map[string]*ServiceInstance) + collections.serviceLBs = make(map[string]*ServiceLB) collections.tenants = make(map[string]*Tenant) collections.volumes = make(map[string]*Volume) collections.volumeProfiles = make(map[string]*VolumeProfile) @@ -415,6 +445,7 @@ func Init() { restoreRule() restoreService() restoreServiceInstance() + restoreServiceLB() restoreTenant() restoreVolume() restoreVolumeProfile() @@ -457,6 +488,10 @@ func RegisterServiceInstanceCallbacks(handler ServiceInstanceCallbacks) { objCallbackHandler.ServiceInstanceCb = handler } +func RegisterServiceLBCallbacks(handler ServiceLBCallbacks) { + objCallbackHandler.ServiceLBCb = handler +} + func RegisterTenantCallbacks(handler TenantCallbacks) { objCallbackHandler.TenantCb = handler } @@ -598,6 +633,16 @@ func AddRoutes(router *mux.Router) { router.Path(route).Methods("PUT").HandlerFunc(makeHttpHandler(httpCreateServiceInstance)) router.Path(route).Methods("DELETE").HandlerFunc(makeHttpHandler(httpDeleteServiceInstance)) + // Register serviceLB + route = "/api/serviceLBs/{key}/" + listRoute = "/api/serviceLBs/" + log.Infof("Registering %s", route) + router.Path(listRoute).Methods("GET").HandlerFunc(makeHttpHandler(httpListServiceLBs)) + router.Path(route).Methods("GET").HandlerFunc(makeHttpHandler(httpGetServiceLB)) + router.Path(route).Methods("POST").HandlerFunc(makeHttpHandler(httpCreateServiceLB)) + router.Path(route).Methods("PUT").HandlerFunc(makeHttpHandler(httpCreateServiceLB)) + router.Path(route).Methods("DELETE").HandlerFunc(makeHttpHandler(httpDeleteServiceLB)) + // Register tenant route = "/api/tenants/{key}/" listRoute = "/api/tenants/" @@ -2910,6 +2955,261 @@ func ValidateServiceInstance(obj *ServiceInstance) error { return nil } +// LIST REST call +func httpListServiceLBs(w http.ResponseWriter, r *http.Request, vars map[string]string) (interface{}, error) { + log.Debugf("Received httpListServiceLBs: %+v", vars) + + list := make([]*ServiceLB, 0) + for _, obj := range collections.serviceLBs { + list = append(list, obj) + } + + // Return the list + return list, nil +} + +// GET REST call +func httpGetServiceLB(w http.ResponseWriter, r *http.Request, vars map[string]string) (interface{}, error) { + log.Debugf("Received httpGetServiceLB: %+v", vars) + + key := vars["key"] + + obj := collections.serviceLBs[key] + if obj == nil { + log.Errorf("serviceLB %s not found", key) + return nil, errors.New("serviceLB not found") + } + + // Return the obj + return obj, nil +} + +// CREATE REST call +func httpCreateServiceLB(w http.ResponseWriter, r *http.Request, vars map[string]string) (interface{}, error) { + log.Debugf("Received httpGetServiceLB: %+v", vars) + + var obj ServiceLB + key := vars["key"] + + // Get object from the request + err := json.NewDecoder(r.Body).Decode(&obj) + if err != nil { + log.Errorf("Error decoding serviceLB create request. Err %v", err) + return nil, err + } + + // set the key + obj.Key = key + + // Create the object + err = CreateServiceLB(&obj) + if err != nil { + log.Errorf("CreateServiceLB error for: %+v. Err: %v", obj, err) + return nil, err + } + + // Return the obj + return obj, nil +} + +// DELETE rest call +func httpDeleteServiceLB(w http.ResponseWriter, r *http.Request, vars map[string]string) (interface{}, error) { + log.Debugf("Received httpDeleteServiceLB: %+v", vars) + + key := vars["key"] + + // Delete the object + err := DeleteServiceLB(key) + if err != nil { + log.Errorf("DeleteServiceLB error for: %s. Err: %v", key, err) + return nil, err + } + + // Return the obj + return key, nil +} + +// Create a serviceLB object +func CreateServiceLB(obj *ServiceLB) error { + // Validate parameters + err := ValidateServiceLB(obj) + if err != nil { + log.Errorf("ValidateServiceLB retruned error for: %+v. Err: %v", obj, err) + return err + } + + // Check if we handle this object + if objCallbackHandler.ServiceLBCb == nil { + log.Errorf("No callback registered for serviceLB object") + return errors.New("Invalid object type") + } + + saveObj := obj + + // Check if object already exists + if collections.serviceLBs[obj.Key] != nil { + // Perform Update callback + err = objCallbackHandler.ServiceLBCb.ServiceLBUpdate(collections.serviceLBs[obj.Key], obj) + if err != nil { + log.Errorf("ServiceLBUpdate retruned error for: %+v. Err: %v", obj, err) + return err + } + + // save the original object after update + saveObj = collections.serviceLBs[obj.Key] + } else { + // save it in cache + collections.serviceLBs[obj.Key] = obj + + // Perform Create callback + err = objCallbackHandler.ServiceLBCb.ServiceLBCreate(obj) + if err != nil { + log.Errorf("ServiceLBCreate retruned error for: %+v. Err: %v", obj, err) + delete(collections.serviceLBs, obj.Key) + return err + } + } + + // Write it to modeldb + err = saveObj.Write() + if err != nil { + log.Errorf("Error saving serviceLB %s to db. Err: %v", saveObj.Key, err) + return err + } + + return nil +} + +// Return a pointer to serviceLB from collection +func FindServiceLB(key string) *ServiceLB { + obj := collections.serviceLBs[key] + if obj == nil { + return nil + } + + return obj +} + +// Delete a serviceLB object +func DeleteServiceLB(key string) error { + obj := collections.serviceLBs[key] + if obj == nil { + log.Errorf("serviceLB %s not found", key) + return errors.New("serviceLB not found") + } + + // Check if we handle this object + if objCallbackHandler.ServiceLBCb == nil { + log.Errorf("No callback registered for serviceLB object") + return errors.New("Invalid object type") + } + + // Perform callback + err := objCallbackHandler.ServiceLBCb.ServiceLBDelete(obj) + if err != nil { + log.Errorf("ServiceLBDelete retruned error for: %+v. Err: %v", obj, err) + return err + } + + // delete it from modeldb + err = obj.Delete() + if err != nil { + log.Errorf("Error deleting serviceLB %s. Err: %v", obj.Key, err) + } + + // delete it from cache + delete(collections.serviceLBs, key) + + return nil +} + +func (self *ServiceLB) GetType() string { + return "serviceLB" +} + +func (self *ServiceLB) GetKey() string { + return self.Key +} + +func (self *ServiceLB) Read() error { + if self.Key == "" { + log.Errorf("Empty key while trying to read serviceLB object") + return errors.New("Empty key") + } + + return modeldb.ReadObj("serviceLB", self.Key, self) +} + +func (self *ServiceLB) Write() error { + if self.Key == "" { + log.Errorf("Empty key while trying to Write serviceLB object") + return errors.New("Empty key") + } + + return modeldb.WriteObj("serviceLB", self.Key, self) +} + +func (self *ServiceLB) Delete() error { + if self.Key == "" { + log.Errorf("Empty key while trying to Delete serviceLB object") + return errors.New("Empty key") + } + + return modeldb.DeleteObj("serviceLB", self.Key) +} + +func restoreServiceLB() error { + strList, err := modeldb.ReadAllObj("serviceLB") + if err != nil { + log.Errorf("Error reading serviceLB list. Err: %v", err) + } + + for _, objStr := range strList { + // Parse the json model + var serviceLB ServiceLB + err = json.Unmarshal([]byte(objStr), &serviceLB) + if err != nil { + log.Errorf("Error parsing object %s, Err %v", objStr, err) + return err + } + + // add it to the collection + collections.serviceLBs[serviceLB.Key] = &serviceLB + } + + return nil +} + +// Validate a serviceLB object +func ValidateServiceLB(obj *ServiceLB) error { + // Validate key is correct + keyStr := obj.ServiceName + ":" + obj.TenantName + if obj.Key != keyStr { + log.Errorf("Expecting ServiceLB Key: %s. Got: %s", keyStr, obj.Key) + return errors.New("Invalid Key") + } + + // Validate each field + + if len(obj.IpAddress) > 15 { + return errors.New("ipAddress string too long") + } + + if len(obj.Network) > 64 { + return errors.New("network string too long") + } + + if len(obj.ServiceName) > 256 { + return errors.New("serviceName string too long") + } + + if len(obj.TenantName) > 64 { + return errors.New("tenantName string too long") + } + + return nil +} + // LIST REST call func httpListTenants(w http.ResponseWriter, r *http.Request, vars map[string]string) (interface{}, error) { log.Debugf("Received httpListTenants: %+v", vars) diff --git a/network.json b/network.json index 2a062e2..e400f45 100644 --- a/network.json +++ b/network.json @@ -57,7 +57,10 @@ }, "endpointGroups": { "ref": "endpointGroup" - } + }, + "servicelbs":{ + "ref": "servicelb" + } }, "links": { "tenant": { diff --git a/servicelb.json b/servicelb.json new file mode 100755 index 0000000..29fc874 --- /dev/null +++ b/servicelb.json @@ -0,0 +1,52 @@ +{ + "name": "contivModel", + "objects": [ + { + "name": "serviceLB", + "type": "object", + "key": ["serviceName","tenantName"], + "properties": { + "serviceName": { + "type": "string", + "title": "service name", + "length": 256 + }, + "tenantName": { + "type": "string", + "title": "Tenant Name", + "length": 64 + }, + + "network": { + "type": "string", + "title": "Service network name", + "length": 64 + }, + "ipAddress":{ + "type":"string", + "title":"Service ip", + "length": 15 + }, + "selectors": { + "type": "array", + "title": "labels key value pair", + "length": 512, + "items" :"string" + }, + "ports":{ + "type":"array", + "title":"service provider port", + "length": 32, + "items" : "string" + } + }, + "links": { + "tenant": { + "ref": "tenant" + }, + "network": { + "ref": "network" + } + } + }] +} diff --git a/tenant.json b/tenant.json index bdc21fe..4aaae38 100644 --- a/tenant.json +++ b/tenant.json @@ -35,7 +35,10 @@ }, "volumeProfiles": { "ref": "volumeProfile" - } + }, + "servicelbs":{ + "ref": "servicelb" + } } } ]