mirror of https://github.com/portainer/portainer
refactor(storidge): remove Storidge support from backend [EE-2450] (#6511)
* refactor(storidge): remove Storidge support from backend * refactor(storidge): remove Storidge support from backend * refactor(storidge): remove Storidge support from frontendpull/6545/head
parent
e96f63023e
commit
318844226c
|
@ -398,7 +398,6 @@ func createTLSSecuredEndpoint(flags *portainer.CLIFlags, dataStore dataservices.
|
|||
TLSConfig: tlsConfiguration,
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
Extensions: []portainer.EndpointExtension{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
|
@ -460,7 +459,6 @@ func createUnsecuredEndpoint(endpointURL string, dataStore dataservices.DataStor
|
|||
TLSConfig: portainer.TLSConfiguration{},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
Extensions: []portainer.EndpointExtension{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
|
|
|
@ -96,7 +96,6 @@ func newEndpoint(endpointType portainer.EndpointType, id portainer.EndpointID, n
|
|||
},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
Extensions: []portainer.EndpointExtension{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
|
|
|
@ -34,7 +34,5 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
|
|||
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToDockerAPI)))
|
||||
h.PathPrefix("/{id}/agent/kubernetes").Handler(
|
||||
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToKubernetesAPI)))
|
||||
h.PathPrefix("/{id}/storidge").Handler(
|
||||
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToStoridgeAPI)))
|
||||
return h
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
package endpointproxy
|
||||
|
||||
// TODO: legacy extension management
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
"github.com/portainer/libhttp/request"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (handler *Handler) proxyRequestsToStoridgeAPI(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusBadRequest, "Invalid environment identifier route variable", err}
|
||||
}
|
||||
|
||||
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
|
||||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an environment with the specified identifier inside the database", err}
|
||||
} else if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an environment with the specified identifier inside the database", err}
|
||||
}
|
||||
|
||||
err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access environment", err}
|
||||
}
|
||||
|
||||
var storidgeExtension *portainer.EndpointExtension
|
||||
for _, extension := range endpoint.Extensions {
|
||||
if extension.Type == portainer.StoridgeEndpointExtension {
|
||||
storidgeExtension = &extension
|
||||
}
|
||||
}
|
||||
|
||||
if storidgeExtension == nil {
|
||||
return &httperror.HandlerError{http.StatusServiceUnavailable, "Storidge extension not supported on this environment", errors.New("This extension is not supported")}
|
||||
}
|
||||
|
||||
proxyExtensionKey := strconv.Itoa(endpointID) + "_" + strconv.Itoa(int(portainer.StoridgeEndpointExtension)) + "_" + storidgeExtension.URL
|
||||
|
||||
var proxy http.Handler
|
||||
proxy = handler.ProxyManager.GetLegacyExtensionProxy(proxyExtensionKey)
|
||||
if proxy == nil {
|
||||
proxy, err = handler.ProxyManager.CreateLegacyExtensionProxy(proxyExtensionKey, storidgeExtension.URL)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create extension proxy", err}
|
||||
}
|
||||
}
|
||||
|
||||
id := strconv.Itoa(endpointID)
|
||||
http.StripPrefix("/"+id+"/storidge", proxy).ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
|
@ -284,7 +284,6 @@ func (handler *Handler) createAzureEndpoint(payload *endpointCreatePayload) (*po
|
|||
PublicURL: payload.PublicURL,
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
Extensions: []portainer.EndpointExtension{},
|
||||
AzureCredentials: credentials,
|
||||
TagIDs: payload.TagIDs,
|
||||
Status: portainer.EndpointStatusUp,
|
||||
|
@ -330,7 +329,6 @@ func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload)
|
|||
},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
Extensions: []portainer.EndpointExtension{},
|
||||
TagIDs: payload.TagIDs,
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
|
@ -385,7 +383,6 @@ func (handler *Handler) createUnsecuredEndpoint(payload *endpointCreatePayload)
|
|||
},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
Extensions: []portainer.EndpointExtension{},
|
||||
TagIDs: payload.TagIDs,
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
|
@ -421,7 +418,6 @@ func (handler *Handler) createKubernetesEndpoint(payload *endpointCreatePayload)
|
|||
},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
Extensions: []portainer.EndpointExtension{},
|
||||
TagIDs: payload.TagIDs,
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
|
@ -451,7 +447,6 @@ func (handler *Handler) createTLSSecuredEndpoint(payload *endpointCreatePayload,
|
|||
},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
Extensions: []portainer.EndpointExtension{},
|
||||
TagIDs: payload.TagIDs,
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
package endpoints
|
||||
|
||||
// TODO: legacy extension management
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
"github.com/portainer/libhttp/request"
|
||||
"github.com/portainer/libhttp/response"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
type endpointExtensionAddPayload struct {
|
||||
Type int
|
||||
URL string
|
||||
}
|
||||
|
||||
func (payload *endpointExtensionAddPayload) Validate(r *http.Request) error {
|
||||
if payload.Type != 1 {
|
||||
return errors.New("Invalid type value. Value must be one of: 1 (Storidge)")
|
||||
}
|
||||
if payload.Type == 1 && govalidator.IsNull(payload.URL) {
|
||||
return errors.New("Invalid extension URL")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// @id endpointExtensionAdd
|
||||
// @tags endpoints
|
||||
// @deprecated
|
||||
// @param id path int true "Environment(Endpoint) identifier"
|
||||
// @success 204 "Success"
|
||||
// @router /endpoints/{id}/extensions [post]
|
||||
func (handler *Handler) endpointExtensionAdd(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusBadRequest, "Invalid environment identifier route variable", err}
|
||||
}
|
||||
|
||||
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
|
||||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an environment with the specified identifier inside the database", err}
|
||||
} else if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an environment with the specified identifier inside the database", err}
|
||||
}
|
||||
|
||||
var payload endpointExtensionAddPayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
|
||||
}
|
||||
|
||||
extensionType := portainer.EndpointExtensionType(payload.Type)
|
||||
|
||||
var extension *portainer.EndpointExtension
|
||||
for idx := range endpoint.Extensions {
|
||||
if endpoint.Extensions[idx].Type == extensionType {
|
||||
extension = &endpoint.Extensions[idx]
|
||||
}
|
||||
}
|
||||
|
||||
if extension != nil {
|
||||
extension.URL = payload.URL
|
||||
} else {
|
||||
extension = &portainer.EndpointExtension{
|
||||
Type: extensionType,
|
||||
URL: payload.URL,
|
||||
}
|
||||
endpoint.Extensions = append(endpoint.Extensions, *extension)
|
||||
}
|
||||
|
||||
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist environment changes inside the database", err}
|
||||
}
|
||||
|
||||
return response.JSON(w, extension)
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package endpoints
|
||||
|
||||
// TODO: legacy extension management
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
"github.com/portainer/libhttp/request"
|
||||
"github.com/portainer/libhttp/response"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
// @id endpointExtensionRemove
|
||||
// @tags endpoints
|
||||
// @deprecated
|
||||
// @param id path int true "Environment(Endpoint) identifier"
|
||||
// @param extensionType path string true "Extension Type"
|
||||
// @success 204 "Success"
|
||||
// @router /endpoints/{id}/extensions/{extensionType} [delete]
|
||||
func (handler *Handler) endpointExtensionRemove(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusBadRequest, "Invalid environment identifier route variable", err}
|
||||
}
|
||||
|
||||
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
|
||||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an environment with the specified identifier inside the database", err}
|
||||
} else if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an environment with the specified identifier inside the database", err}
|
||||
}
|
||||
|
||||
extensionType, err := request.RetrieveNumericRouteVariableValue(r, "extensionType")
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusBadRequest, "Invalid extension type route variable", err}
|
||||
}
|
||||
|
||||
for idx, ext := range endpoint.Extensions {
|
||||
if ext.Type == portainer.EndpointExtensionType(extensionType) {
|
||||
endpoint.Extensions = append(endpoint.Extensions[:idx], endpoint.Extensions[idx+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist environment changes inside the database", err}
|
||||
}
|
||||
|
||||
return response.Empty(w)
|
||||
}
|
|
@ -62,10 +62,6 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
|
|||
bouncer.AdminAccess(httperror.LoggerHandler(h.endpointDelete))).Methods(http.MethodDelete)
|
||||
h.Handle("/endpoints/{id}/dockerhub/{registryId}",
|
||||
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.endpointDockerhubStatus))).Methods(http.MethodGet)
|
||||
h.Handle("/endpoints/{id}/extensions",
|
||||
bouncer.RestrictedAccess(httperror.LoggerHandler(h.endpointExtensionAdd))).Methods(http.MethodPost)
|
||||
h.Handle("/endpoints/{id}/extensions/{extensionType}",
|
||||
bouncer.RestrictedAccess(httperror.LoggerHandler(h.endpointExtensionRemove))).Methods(http.MethodDelete)
|
||||
h.Handle("/endpoints/{id}/snapshot",
|
||||
bouncer.AdminAccess(httperror.LoggerHandler(h.endpointSnapshot))).Methods(http.MethodPost)
|
||||
h.Handle("/endpoints/{id}/status",
|
||||
|
|
|
@ -190,8 +190,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
|
||||
case strings.Contains(r.URL.Path, "/kubernetes/"):
|
||||
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
|
||||
case strings.Contains(r.URL.Path, "/storidge/"):
|
||||
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
|
||||
case strings.Contains(r.URL.Path, "/azure/"):
|
||||
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
|
||||
case strings.Contains(r.URL.Path, "/agent/"):
|
||||
|
|
|
@ -2,8 +2,6 @@ package factory
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
@ -40,18 +38,6 @@ func NewProxyFactory(dataStore dataservices.DataStore, signatureService portaine
|
|||
}
|
||||
}
|
||||
|
||||
// NewLegacyExtensionProxy returns a new HTTP proxy to a legacy extension server (Storidge)
|
||||
func (factory *ProxyFactory) NewLegacyExtensionProxy(extensionAPIURL string) (http.Handler, error) {
|
||||
extensionURL, err := url.Parse(extensionAPIURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extensionURL.Scheme = "http"
|
||||
proxy := httputil.NewSingleHostReverseProxy(extensionURL)
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
// NewEndpointProxy returns a new reverse proxy (filesystem based or HTTP) to an environment(endpoint) API server
|
||||
func (factory *ProxyFactory) NewEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
|
||||
switch endpoint.Type {
|
||||
|
|
|
@ -15,25 +15,21 @@ import (
|
|||
"github.com/portainer/portainer/api/http/proxy/factory"
|
||||
)
|
||||
|
||||
// TODO: contain code related to legacy extension management
|
||||
|
||||
type (
|
||||
// Manager represents a service used to manage proxies to environments(endpoints) and extensions.
|
||||
// Manager represents a service used to manage proxies to environments (endpoints).
|
||||
Manager struct {
|
||||
proxyFactory *factory.ProxyFactory
|
||||
endpointProxies cmap.ConcurrentMap
|
||||
legacyExtensionProxies cmap.ConcurrentMap
|
||||
k8sClientFactory *cli.ClientFactory
|
||||
proxyFactory *factory.ProxyFactory
|
||||
endpointProxies cmap.ConcurrentMap
|
||||
k8sClientFactory *cli.ClientFactory
|
||||
}
|
||||
)
|
||||
|
||||
// NewManager initializes a new proxy Service
|
||||
func NewManager(dataStore dataservices.DataStore, signatureService portainer.DigitalSignatureService, tunnelService portainer.ReverseTunnelService, clientFactory *docker.ClientFactory, kubernetesClientFactory *cli.ClientFactory, kubernetesTokenCacheManager *kubernetes.TokenCacheManager) *Manager {
|
||||
return &Manager{
|
||||
endpointProxies: cmap.New(),
|
||||
legacyExtensionProxies: cmap.New(),
|
||||
k8sClientFactory: kubernetesClientFactory,
|
||||
proxyFactory: factory.NewProxyFactory(dataStore, signatureService, tunnelService, clientFactory, kubernetesClientFactory, kubernetesTokenCacheManager),
|
||||
endpointProxies: cmap.New(),
|
||||
k8sClientFactory: kubernetesClientFactory,
|
||||
proxyFactory: factory.NewProxyFactory(dataStore, signatureService, tunnelService, clientFactory, kubernetesClientFactory, kubernetesTokenCacheManager),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,26 +69,6 @@ func (manager *Manager) DeleteEndpointProxy(endpointID portainer.EndpointID) {
|
|||
manager.k8sClientFactory.RemoveKubeClient(endpointID)
|
||||
}
|
||||
|
||||
// CreateLegacyExtensionProxy creates a new HTTP reverse proxy for a legacy extension and adds it to the registered proxies
|
||||
func (manager *Manager) CreateLegacyExtensionProxy(key, extensionAPIURL string) (http.Handler, error) {
|
||||
proxy, err := manager.proxyFactory.NewLegacyExtensionProxy(extensionAPIURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manager.legacyExtensionProxies.Set(key, proxy)
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
// GetLegacyExtensionProxy returns a legacy extension proxy associated to a key
|
||||
func (manager *Manager) GetLegacyExtensionProxy(key string) http.Handler {
|
||||
proxy, ok := manager.legacyExtensionProxies.Get(key)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return proxy.(http.Handler)
|
||||
}
|
||||
|
||||
// CreateGitlabProxy creates a new HTTP reverse proxy that can be used to send requests to the Gitlab API
|
||||
func (manager *Manager) CreateGitlabProxy(url string) (http.Handler, error) {
|
||||
return manager.proxyFactory.NewGitlabProxy(url)
|
||||
|
|
|
@ -153,7 +153,6 @@ func DefaultEndpointAuthorizationsForEndpointAdministratorRole() portainer.Autho
|
|||
portainer.OperationPortainerWebhookList: true,
|
||||
portainer.OperationPortainerWebhookCreate: true,
|
||||
portainer.OperationPortainerWebhookDelete: true,
|
||||
portainer.OperationIntegrationStoridgeAdmin: true,
|
||||
portainer.EndpointResourcesAccess: true,
|
||||
}
|
||||
}
|
||||
|
@ -412,21 +411,19 @@ func DefaultEndpointAuthorizationsForReadOnlyUserRole(volumeBrowsingAuthorizatio
|
|||
// DefaultPortainerAuthorizations returns the default Portainer authorizations used by non-admin users.
|
||||
func DefaultPortainerAuthorizations() portainer.Authorizations {
|
||||
return map[portainer.Authorization]bool{
|
||||
portainer.OperationPortainerDockerHubInspect: true,
|
||||
portainer.OperationPortainerEndpointGroupList: true,
|
||||
portainer.OperationPortainerEndpointList: true,
|
||||
portainer.OperationPortainerEndpointInspect: true,
|
||||
portainer.OperationPortainerEndpointExtensionAdd: true,
|
||||
portainer.OperationPortainerEndpointExtensionRemove: true,
|
||||
portainer.OperationPortainerMOTD: true,
|
||||
portainer.OperationPortainerRegistryList: true,
|
||||
portainer.OperationPortainerRegistryInspect: true,
|
||||
portainer.OperationPortainerTeamList: true,
|
||||
portainer.OperationPortainerTemplateList: true,
|
||||
portainer.OperationPortainerTemplateInspect: true,
|
||||
portainer.OperationPortainerUserList: true,
|
||||
portainer.OperationPortainerUserInspect: true,
|
||||
portainer.OperationPortainerUserMemberships: true,
|
||||
portainer.OperationPortainerDockerHubInspect: true,
|
||||
portainer.OperationPortainerEndpointGroupList: true,
|
||||
portainer.OperationPortainerEndpointList: true,
|
||||
portainer.OperationPortainerEndpointInspect: true,
|
||||
portainer.OperationPortainerMOTD: true,
|
||||
portainer.OperationPortainerRegistryList: true,
|
||||
portainer.OperationPortainerRegistryInspect: true,
|
||||
portainer.OperationPortainerTeamList: true,
|
||||
portainer.OperationPortainerTemplateList: true,
|
||||
portainer.OperationPortainerTemplateInspect: true,
|
||||
portainer.OperationPortainerUserList: true,
|
||||
portainer.OperationPortainerUserInspect: true,
|
||||
portainer.OperationPortainerUserMemberships: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
203
api/portainer.go
203
api/portainer.go
|
@ -296,10 +296,9 @@ type (
|
|||
// Environment(Endpoint) group identifier
|
||||
GroupID EndpointGroupID `json:"GroupId" example:"1"`
|
||||
// URL or IP address where exposed containers will be reachable
|
||||
PublicURL string `json:"PublicURL" example:"docker.mydomain.tld:2375"`
|
||||
TLSConfig TLSConfiguration `json:"TLSConfig"`
|
||||
Extensions []EndpointExtension `json:"Extensions" example:""`
|
||||
AzureCredentials AzureCredentials `json:"AzureCredentials,omitempty" example:""`
|
||||
PublicURL string `json:"PublicURL" example:"docker.mydomain.tld:2375"`
|
||||
TLSConfig TLSConfiguration `json:"TLSConfig"`
|
||||
AzureCredentials AzureCredentials `json:"AzureCredentials,omitempty" example:""`
|
||||
// List of tag identifiers to which this environment(endpoint) is associated
|
||||
TagIDs []TagID `json:"TagIds"`
|
||||
// The status of the environment(endpoint) (1 - up, 2 - down)
|
||||
|
@ -349,17 +348,6 @@ type (
|
|||
// EndpointAuthorizations represents the authorizations associated to a set of environments(endpoints)
|
||||
EndpointAuthorizations map[EndpointID]Authorizations
|
||||
|
||||
// EndpointExtension represents a deprecated form of Portainer extension
|
||||
// TODO: legacy extension management
|
||||
EndpointExtension struct {
|
||||
Type EndpointExtensionType `json:"Type"`
|
||||
URL string `json:"URL"`
|
||||
}
|
||||
|
||||
// EndpointExtensionType represents the type of an environment(endpoint) extension. Only
|
||||
// one extension of each type can be associated to an environment(endpoint)
|
||||
EndpointExtensionType int
|
||||
|
||||
// EndpointGroup represents a group of environments(endpoints)
|
||||
EndpointGroup struct {
|
||||
// Environment(Endpoint) group Identifier
|
||||
|
@ -1450,12 +1438,6 @@ const (
|
|||
StatusAcknowledged
|
||||
)
|
||||
|
||||
const (
|
||||
_ EndpointExtensionType = iota
|
||||
// StoridgeEndpointExtension represents the Storidge extension
|
||||
StoridgeEndpointExtension
|
||||
)
|
||||
|
||||
const (
|
||||
_ EndpointStatus = iota
|
||||
// EndpointStatusUp is used to represent an available environment(endpoint)
|
||||
|
@ -1730,101 +1712,102 @@ const (
|
|||
OperationDockerAgentBrowsePut Authorization = "DockerAgentBrowsePut"
|
||||
OperationDockerAgentBrowseRename Authorization = "DockerAgentBrowseRename"
|
||||
|
||||
OperationPortainerDockerHubInspect Authorization = "PortainerDockerHubInspect"
|
||||
OperationPortainerDockerHubUpdate Authorization = "PortainerDockerHubUpdate"
|
||||
OperationPortainerEndpointGroupCreate Authorization = "PortainerEndpointGroupCreate"
|
||||
OperationPortainerEndpointGroupList Authorization = "PortainerEndpointGroupList"
|
||||
OperationPortainerEndpointGroupDelete Authorization = "PortainerEndpointGroupDelete"
|
||||
OperationPortainerEndpointGroupInspect Authorization = "PortainerEndpointGroupInspect"
|
||||
OperationPortainerEndpointGroupUpdate Authorization = "PortainerEndpointGroupEdit"
|
||||
OperationPortainerEndpointGroupAccess Authorization = "PortainerEndpointGroupAccess "
|
||||
OperationPortainerEndpointList Authorization = "PortainerEndpointList"
|
||||
OperationPortainerEndpointInspect Authorization = "PortainerEndpointInspect"
|
||||
OperationPortainerEndpointCreate Authorization = "PortainerEndpointCreate"
|
||||
OperationPortainerEndpointExtensionAdd Authorization = "PortainerEndpointExtensionAdd"
|
||||
OperationPortainerEndpointJob Authorization = "PortainerEndpointJob"
|
||||
OperationPortainerEndpointSnapshots Authorization = "PortainerEndpointSnapshots"
|
||||
OperationPortainerEndpointSnapshot Authorization = "PortainerEndpointSnapshot"
|
||||
OperationPortainerEndpointUpdate Authorization = "PortainerEndpointUpdate"
|
||||
OperationPortainerEndpointUpdateAccess Authorization = "PortainerEndpointUpdateAccess"
|
||||
OperationPortainerEndpointDelete Authorization = "PortainerEndpointDelete"
|
||||
OperationPortainerEndpointExtensionRemove Authorization = "PortainerEndpointExtensionRemove"
|
||||
OperationPortainerExtensionList Authorization = "PortainerExtensionList"
|
||||
OperationPortainerExtensionInspect Authorization = "PortainerExtensionInspect"
|
||||
OperationPortainerExtensionCreate Authorization = "PortainerExtensionCreate"
|
||||
OperationPortainerExtensionUpdate Authorization = "PortainerExtensionUpdate"
|
||||
OperationPortainerExtensionDelete Authorization = "PortainerExtensionDelete"
|
||||
OperationPortainerMOTD Authorization = "PortainerMOTD"
|
||||
OperationPortainerRegistryList Authorization = "PortainerRegistryList"
|
||||
OperationPortainerRegistryInspect Authorization = "PortainerRegistryInspect"
|
||||
OperationPortainerRegistryCreate Authorization = "PortainerRegistryCreate"
|
||||
OperationPortainerRegistryConfigure Authorization = "PortainerRegistryConfigure"
|
||||
OperationPortainerRegistryUpdate Authorization = "PortainerRegistryUpdate"
|
||||
OperationPortainerRegistryUpdateAccess Authorization = "PortainerRegistryUpdateAccess"
|
||||
OperationPortainerRegistryDelete Authorization = "PortainerRegistryDelete"
|
||||
OperationPortainerResourceControlCreate Authorization = "PortainerResourceControlCreate"
|
||||
OperationPortainerResourceControlUpdate Authorization = "PortainerResourceControlUpdate"
|
||||
OperationPortainerResourceControlDelete Authorization = "PortainerResourceControlDelete"
|
||||
OperationPortainerRoleList Authorization = "PortainerRoleList"
|
||||
OperationPortainerRoleInspect Authorization = "PortainerRoleInspect"
|
||||
OperationPortainerRoleCreate Authorization = "PortainerRoleCreate"
|
||||
OperationPortainerRoleUpdate Authorization = "PortainerRoleUpdate"
|
||||
OperationPortainerRoleDelete Authorization = "PortainerRoleDelete"
|
||||
OperationPortainerScheduleList Authorization = "PortainerScheduleList"
|
||||
OperationPortainerScheduleInspect Authorization = "PortainerScheduleInspect"
|
||||
OperationPortainerScheduleFile Authorization = "PortainerScheduleFile"
|
||||
OperationPortainerScheduleTasks Authorization = "PortainerScheduleTasks"
|
||||
OperationPortainerScheduleCreate Authorization = "PortainerScheduleCreate"
|
||||
OperationPortainerScheduleUpdate Authorization = "PortainerScheduleUpdate"
|
||||
OperationPortainerScheduleDelete Authorization = "PortainerScheduleDelete"
|
||||
OperationPortainerSettingsInspect Authorization = "PortainerSettingsInspect"
|
||||
OperationPortainerSettingsUpdate Authorization = "PortainerSettingsUpdate"
|
||||
OperationPortainerSettingsLDAPCheck Authorization = "PortainerSettingsLDAPCheck"
|
||||
OperationPortainerStackList Authorization = "PortainerStackList"
|
||||
OperationPortainerStackInspect Authorization = "PortainerStackInspect"
|
||||
OperationPortainerStackFile Authorization = "PortainerStackFile"
|
||||
OperationPortainerStackCreate Authorization = "PortainerStackCreate"
|
||||
OperationPortainerStackMigrate Authorization = "PortainerStackMigrate"
|
||||
OperationPortainerStackUpdate Authorization = "PortainerStackUpdate"
|
||||
OperationPortainerStackDelete Authorization = "PortainerStackDelete"
|
||||
OperationPortainerTagList Authorization = "PortainerTagList"
|
||||
OperationPortainerTagCreate Authorization = "PortainerTagCreate"
|
||||
OperationPortainerTagDelete Authorization = "PortainerTagDelete"
|
||||
OperationPortainerTeamMembershipList Authorization = "PortainerTeamMembershipList"
|
||||
OperationPortainerTeamMembershipCreate Authorization = "PortainerTeamMembershipCreate"
|
||||
OperationPortainerTeamMembershipUpdate Authorization = "PortainerTeamMembershipUpdate"
|
||||
OperationPortainerTeamMembershipDelete Authorization = "PortainerTeamMembershipDelete"
|
||||
OperationPortainerTeamList Authorization = "PortainerTeamList"
|
||||
OperationPortainerTeamInspect Authorization = "PortainerTeamInspect"
|
||||
OperationPortainerTeamMemberships Authorization = "PortainerTeamMemberships"
|
||||
OperationPortainerTeamCreate Authorization = "PortainerTeamCreate"
|
||||
OperationPortainerTeamUpdate Authorization = "PortainerTeamUpdate"
|
||||
OperationPortainerTeamDelete Authorization = "PortainerTeamDelete"
|
||||
OperationPortainerTemplateList Authorization = "PortainerTemplateList"
|
||||
OperationPortainerTemplateInspect Authorization = "PortainerTemplateInspect"
|
||||
OperationPortainerTemplateCreate Authorization = "PortainerTemplateCreate"
|
||||
OperationPortainerTemplateUpdate Authorization = "PortainerTemplateUpdate"
|
||||
OperationPortainerTemplateDelete Authorization = "PortainerTemplateDelete"
|
||||
OperationPortainerUploadTLS Authorization = "PortainerUploadTLS"
|
||||
OperationPortainerUserList Authorization = "PortainerUserList"
|
||||
OperationPortainerUserInspect Authorization = "PortainerUserInspect"
|
||||
OperationPortainerUserMemberships Authorization = "PortainerUserMemberships"
|
||||
OperationPortainerUserCreate Authorization = "PortainerUserCreate"
|
||||
OperationPortainerUserUpdate Authorization = "PortainerUserUpdate"
|
||||
OperationPortainerUserUpdatePassword Authorization = "PortainerUserUpdatePassword"
|
||||
OperationPortainerUserDelete Authorization = "PortainerUserDelete"
|
||||
OperationPortainerWebsocketExec Authorization = "PortainerWebsocketExec"
|
||||
OperationPortainerWebhookList Authorization = "PortainerWebhookList"
|
||||
OperationPortainerWebhookCreate Authorization = "PortainerWebhookCreate"
|
||||
OperationPortainerWebhookDelete Authorization = "PortainerWebhookDelete"
|
||||
|
||||
OperationIntegrationStoridgeAdmin Authorization = "IntegrationStoridgeAdmin"
|
||||
OperationPortainerDockerHubInspect Authorization = "PortainerDockerHubInspect"
|
||||
OperationPortainerDockerHubUpdate Authorization = "PortainerDockerHubUpdate"
|
||||
OperationPortainerEndpointGroupCreate Authorization = "PortainerEndpointGroupCreate"
|
||||
OperationPortainerEndpointGroupList Authorization = "PortainerEndpointGroupList"
|
||||
OperationPortainerEndpointGroupDelete Authorization = "PortainerEndpointGroupDelete"
|
||||
OperationPortainerEndpointGroupInspect Authorization = "PortainerEndpointGroupInspect"
|
||||
OperationPortainerEndpointGroupUpdate Authorization = "PortainerEndpointGroupEdit"
|
||||
OperationPortainerEndpointGroupAccess Authorization = "PortainerEndpointGroupAccess "
|
||||
OperationPortainerEndpointList Authorization = "PortainerEndpointList"
|
||||
OperationPortainerEndpointInspect Authorization = "PortainerEndpointInspect"
|
||||
OperationPortainerEndpointCreate Authorization = "PortainerEndpointCreate"
|
||||
OperationPortainerEndpointJob Authorization = "PortainerEndpointJob"
|
||||
OperationPortainerEndpointSnapshots Authorization = "PortainerEndpointSnapshots"
|
||||
OperationPortainerEndpointSnapshot Authorization = "PortainerEndpointSnapshot"
|
||||
OperationPortainerEndpointUpdate Authorization = "PortainerEndpointUpdate"
|
||||
OperationPortainerEndpointUpdateAccess Authorization = "PortainerEndpointUpdateAccess"
|
||||
OperationPortainerEndpointDelete Authorization = "PortainerEndpointDelete"
|
||||
OperationPortainerExtensionList Authorization = "PortainerExtensionList"
|
||||
OperationPortainerExtensionInspect Authorization = "PortainerExtensionInspect"
|
||||
OperationPortainerExtensionCreate Authorization = "PortainerExtensionCreate"
|
||||
OperationPortainerExtensionUpdate Authorization = "PortainerExtensionUpdate"
|
||||
OperationPortainerExtensionDelete Authorization = "PortainerExtensionDelete"
|
||||
OperationPortainerMOTD Authorization = "PortainerMOTD"
|
||||
OperationPortainerRegistryList Authorization = "PortainerRegistryList"
|
||||
OperationPortainerRegistryInspect Authorization = "PortainerRegistryInspect"
|
||||
OperationPortainerRegistryCreate Authorization = "PortainerRegistryCreate"
|
||||
OperationPortainerRegistryConfigure Authorization = "PortainerRegistryConfigure"
|
||||
OperationPortainerRegistryUpdate Authorization = "PortainerRegistryUpdate"
|
||||
OperationPortainerRegistryUpdateAccess Authorization = "PortainerRegistryUpdateAccess"
|
||||
OperationPortainerRegistryDelete Authorization = "PortainerRegistryDelete"
|
||||
OperationPortainerResourceControlCreate Authorization = "PortainerResourceControlCreate"
|
||||
OperationPortainerResourceControlUpdate Authorization = "PortainerResourceControlUpdate"
|
||||
OperationPortainerResourceControlDelete Authorization = "PortainerResourceControlDelete"
|
||||
OperationPortainerRoleList Authorization = "PortainerRoleList"
|
||||
OperationPortainerRoleInspect Authorization = "PortainerRoleInspect"
|
||||
OperationPortainerRoleCreate Authorization = "PortainerRoleCreate"
|
||||
OperationPortainerRoleUpdate Authorization = "PortainerRoleUpdate"
|
||||
OperationPortainerRoleDelete Authorization = "PortainerRoleDelete"
|
||||
OperationPortainerScheduleList Authorization = "PortainerScheduleList"
|
||||
OperationPortainerScheduleInspect Authorization = "PortainerScheduleInspect"
|
||||
OperationPortainerScheduleFile Authorization = "PortainerScheduleFile"
|
||||
OperationPortainerScheduleTasks Authorization = "PortainerScheduleTasks"
|
||||
OperationPortainerScheduleCreate Authorization = "PortainerScheduleCreate"
|
||||
OperationPortainerScheduleUpdate Authorization = "PortainerScheduleUpdate"
|
||||
OperationPortainerScheduleDelete Authorization = "PortainerScheduleDelete"
|
||||
OperationPortainerSettingsInspect Authorization = "PortainerSettingsInspect"
|
||||
OperationPortainerSettingsUpdate Authorization = "PortainerSettingsUpdate"
|
||||
OperationPortainerSettingsLDAPCheck Authorization = "PortainerSettingsLDAPCheck"
|
||||
OperationPortainerStackList Authorization = "PortainerStackList"
|
||||
OperationPortainerStackInspect Authorization = "PortainerStackInspect"
|
||||
OperationPortainerStackFile Authorization = "PortainerStackFile"
|
||||
OperationPortainerStackCreate Authorization = "PortainerStackCreate"
|
||||
OperationPortainerStackMigrate Authorization = "PortainerStackMigrate"
|
||||
OperationPortainerStackUpdate Authorization = "PortainerStackUpdate"
|
||||
OperationPortainerStackDelete Authorization = "PortainerStackDelete"
|
||||
OperationPortainerTagList Authorization = "PortainerTagList"
|
||||
OperationPortainerTagCreate Authorization = "PortainerTagCreate"
|
||||
OperationPortainerTagDelete Authorization = "PortainerTagDelete"
|
||||
OperationPortainerTeamMembershipList Authorization = "PortainerTeamMembershipList"
|
||||
OperationPortainerTeamMembershipCreate Authorization = "PortainerTeamMembershipCreate"
|
||||
OperationPortainerTeamMembershipUpdate Authorization = "PortainerTeamMembershipUpdate"
|
||||
OperationPortainerTeamMembershipDelete Authorization = "PortainerTeamMembershipDelete"
|
||||
OperationPortainerTeamList Authorization = "PortainerTeamList"
|
||||
OperationPortainerTeamInspect Authorization = "PortainerTeamInspect"
|
||||
OperationPortainerTeamMemberships Authorization = "PortainerTeamMemberships"
|
||||
OperationPortainerTeamCreate Authorization = "PortainerTeamCreate"
|
||||
OperationPortainerTeamUpdate Authorization = "PortainerTeamUpdate"
|
||||
OperationPortainerTeamDelete Authorization = "PortainerTeamDelete"
|
||||
OperationPortainerTemplateList Authorization = "PortainerTemplateList"
|
||||
OperationPortainerTemplateInspect Authorization = "PortainerTemplateInspect"
|
||||
OperationPortainerTemplateCreate Authorization = "PortainerTemplateCreate"
|
||||
OperationPortainerTemplateUpdate Authorization = "PortainerTemplateUpdate"
|
||||
OperationPortainerTemplateDelete Authorization = "PortainerTemplateDelete"
|
||||
OperationPortainerUploadTLS Authorization = "PortainerUploadTLS"
|
||||
OperationPortainerUserList Authorization = "PortainerUserList"
|
||||
OperationPortainerUserInspect Authorization = "PortainerUserInspect"
|
||||
OperationPortainerUserMemberships Authorization = "PortainerUserMemberships"
|
||||
OperationPortainerUserCreate Authorization = "PortainerUserCreate"
|
||||
OperationPortainerUserUpdate Authorization = "PortainerUserUpdate"
|
||||
OperationPortainerUserUpdatePassword Authorization = "PortainerUserUpdatePassword"
|
||||
OperationPortainerUserDelete Authorization = "PortainerUserDelete"
|
||||
OperationPortainerWebsocketExec Authorization = "PortainerWebsocketExec"
|
||||
OperationPortainerWebhookList Authorization = "PortainerWebhookList"
|
||||
OperationPortainerWebhookCreate Authorization = "PortainerWebhookCreate"
|
||||
OperationPortainerWebhookDelete Authorization = "PortainerWebhookDelete"
|
||||
|
||||
OperationDockerUndefined Authorization = "DockerUndefined"
|
||||
OperationDockerAgentUndefined Authorization = "DockerAgentUndefined"
|
||||
OperationPortainerUndefined Authorization = "PortainerUndefined"
|
||||
|
||||
EndpointResourcesAccess Authorization = "EndpointResourcesAccess"
|
||||
|
||||
// Deprecated operations
|
||||
OperationPortainerEndpointExtensionAdd Authorization = "PortainerEndpointExtensionAdd"
|
||||
OperationPortainerEndpointExtensionRemove Authorization = "PortainerEndpointExtensionRemove"
|
||||
OperationIntegrationStoridgeAdmin Authorization = "IntegrationStoridgeAdmin"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -849,10 +849,6 @@ definitions:
|
|||
EdgeKey:
|
||||
description: The key which is used to map the agent to Portainer
|
||||
type: string
|
||||
Extensions:
|
||||
items:
|
||||
$ref: '#/definitions/portainer.EndpointExtension'
|
||||
type: array
|
||||
GroupId:
|
||||
description: Endpoint group identifier
|
||||
example: 1
|
||||
|
@ -926,13 +922,6 @@ definitions:
|
|||
additionalProperties:
|
||||
$ref: '#/definitions/portainer.Authorizations'
|
||||
type: object
|
||||
portainer.EndpointExtension:
|
||||
properties:
|
||||
Type:
|
||||
type: integer
|
||||
URL:
|
||||
type: string
|
||||
type: object
|
||||
portainer.EndpointGroup:
|
||||
properties:
|
||||
AuthorizedTeams:
|
||||
|
|
|
@ -22,7 +22,7 @@ angular.module('portainer.azure', ['portainer.app', containerInstancesModule]).c
|
|||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
||||
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
|
||||
await StateManager.updateEndpointState(endpoint, []);
|
||||
await StateManager.updateEndpointState(endpoint);
|
||||
} catch (e) {
|
||||
Notifications.error('Failed loading environment', e);
|
||||
$state.go('portainer.home', {}, { reload: true });
|
||||
|
|
|
@ -12,7 +12,7 @@ angular.module('portainer.docker', ['portainer.app', containersModule]).config([
|
|||
parent: 'endpoint',
|
||||
url: '/docker',
|
||||
abstract: true,
|
||||
onEnter: /* @ngInject */ function onEnter(endpoint, $async, $state, EndpointService, EndpointProvider, LegacyExtensionManager, Notifications, StateManager, SystemService) {
|
||||
onEnter: /* @ngInject */ function onEnter(endpoint, $async, $state, EndpointService, EndpointProvider, Notifications, StateManager, SystemService) {
|
||||
return $async(async () => {
|
||||
if (![1, 2, 4].includes(endpoint.Type)) {
|
||||
$state.go('portainer.home');
|
||||
|
@ -39,8 +39,7 @@ angular.module('portainer.docker', ['portainer.app', containersModule]).config([
|
|||
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
||||
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
|
||||
|
||||
const extensions = await LegacyExtensionManager.initEndpointExtensions(endpoint);
|
||||
await StateManager.updateEndpointState(endpoint, extensions);
|
||||
await StateManager.updateEndpointState(endpoint);
|
||||
} catch (e) {
|
||||
Notifications.error('Failed loading environment', e);
|
||||
$state.go('portainer.home', {}, { reload: true });
|
||||
|
|
|
@ -86,11 +86,6 @@ angular.module('portainer.docker').controller('CreateVolumeController', [
|
|||
var name = $scope.formValues.Name;
|
||||
var driver = $scope.formValues.Driver;
|
||||
var driverOptions = $scope.formValues.DriverOptions;
|
||||
var storidgeProfile = $scope.formValues.StoridgeProfile;
|
||||
|
||||
if (driver === 'cio:latest' && storidgeProfile) {
|
||||
driverOptions.push({ name: 'profile', value: storidgeProfile.Name });
|
||||
}
|
||||
|
||||
if ($scope.formValues.NFSData.useNFS) {
|
||||
prepareNFSConfiguration(driverOptions);
|
||||
|
|
|
@ -83,12 +83,6 @@
|
|||
</div>
|
||||
<volumes-cifs-form data="formValues.CIFSData" ng-show="formValues.Driver === 'local'"></volumes-cifs-form>
|
||||
<!-- !cifs-management -->
|
||||
<!-- storidge -->
|
||||
<div ng-if="formValues.Driver === 'cio:latest'">
|
||||
<div class="col-sm-12 form-section-title"> Storidge </div>
|
||||
<storidge-profile-selector storidge-profile="formValues.StoridgeProfile"></storidge-profile-selector>
|
||||
</div>
|
||||
<!-- storidge -->
|
||||
<div ng-if="applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && formValues.Driver === 'local'">
|
||||
<div class="col-sm-12 form-section-title"> Deployment </div>
|
||||
<!-- node-selection -->
|
||||
|
|
|
@ -51,32 +51,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="isCioDriver">
|
||||
<div class="col-sm-12">
|
||||
<volume-storidge-info volume="storidgeVolume"> </volume-storidge-info>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="isCioDriver">
|
||||
<div class="col-sm-12">
|
||||
<storidge-snapshot-creation volume-id="storidgeVolume.Vdisk" ng-if="storidgeVolume.SnapshotEnabled"> </storidge-snapshot-creation>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="isCioDriver && storidgeVolume.SnapshotEnabled">
|
||||
<div class="col-sm-12">
|
||||
<storidge-snapshots-datatable
|
||||
title-text="Snapshots"
|
||||
title-icon="fa-camera"
|
||||
dataset="storidgeSnapshots"
|
||||
table-key="storidgeSnapshots"
|
||||
order-by="Id"
|
||||
remove-action="removeSnapshot"
|
||||
>
|
||||
</storidge-snapshots-datatable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- access-control-panel -->
|
||||
<por-access-control-panel ng-if="volume" resource-id="volume.ResourceId" resource-control="volume.ResourceControl" resource-type="'volume'"> </por-access-control-panel>
|
||||
<!-- !access-control-panel -->
|
||||
|
|
|
@ -8,48 +8,7 @@ angular.module('portainer.docker').controller('VolumeController', [
|
|||
'ContainerService',
|
||||
'Notifications',
|
||||
'HttpRequestHelper',
|
||||
'StoridgeVolumeService',
|
||||
'StoridgeSnapshotService',
|
||||
function ($scope, $state, $transition$, $q, ModalService, VolumeService, ContainerService, Notifications, HttpRequestHelper, StoridgeVolumeService, StoridgeSnapshotService) {
|
||||
$scope.storidgeSnapshots = [];
|
||||
$scope.storidgeVolume = {};
|
||||
|
||||
$scope.removeSnapshot = function (selectedItems) {
|
||||
ModalService.confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'Do you want really want to remove this snapshot?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
var actionCount = selectedItems.length;
|
||||
angular.forEach(selectedItems, function (item) {
|
||||
StoridgeSnapshotService.remove(item.Id)
|
||||
.then(function success() {
|
||||
Notifications.success('Snapshot successfully removed', item.Id);
|
||||
var index = $scope.storidgeSnapshots.indexOf(item);
|
||||
$scope.storidgeSnapshots.splice(index, 1);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to remove snapshot');
|
||||
})
|
||||
.finally(function final() {
|
||||
--actionCount;
|
||||
if (actionCount === 0) {
|
||||
$state.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function ($scope, $state, $transition$, $q, ModalService, VolumeService, ContainerService, Notifications, HttpRequestHelper) {
|
||||
$scope.removeVolume = function removeVolume() {
|
||||
ModalService.confirmDeletion('Do you want to remove this volume?', (confirmed) => {
|
||||
if (confirmed) {
|
||||
|
@ -80,15 +39,7 @@ angular.module('portainer.docker').controller('VolumeController', [
|
|||
$scope.volume = volume;
|
||||
var containerFilter = { volume: [volume.Id] };
|
||||
|
||||
$scope.isCioDriver = volume.Driver.includes('cio');
|
||||
if ($scope.isCioDriver) {
|
||||
return $q.all({
|
||||
containers: ContainerService.containers(1, containerFilter),
|
||||
storidgeVolume: StoridgeVolumeService.volume($transition$.params().id),
|
||||
});
|
||||
} else {
|
||||
return ContainerService.containers(1, containerFilter);
|
||||
}
|
||||
return ContainerService.containers(1, containerFilter);
|
||||
})
|
||||
.then(function success(data) {
|
||||
var dataContainers = $scope.isCioDriver ? data.containers : data;
|
||||
|
@ -98,16 +49,6 @@ angular.module('portainer.docker').controller('VolumeController', [
|
|||
return container;
|
||||
});
|
||||
$scope.containersUsingVolume = containers;
|
||||
|
||||
if ($scope.isCioDriver) {
|
||||
$scope.storidgeVolume = data.storidgeVolume;
|
||||
if ($scope.storidgeVolume.SnapshotEnabled) {
|
||||
return StoridgeSnapshotService.snapshots(data.storidgeVolume.Vdisk);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.storidgeSnapshots = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve volume details');
|
||||
|
|
|
@ -47,7 +47,6 @@ angular
|
|||
'portainer.docker',
|
||||
'portainer.kubernetes',
|
||||
'portainer.edge',
|
||||
'portainer.integrations',
|
||||
'rzModule',
|
||||
'moment-picker',
|
||||
'angulartics',
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
angular.module('portainer.integrations', ['portainer.integrations.storidge']);
|
|
@ -1,128 +0,0 @@
|
|||
// TODO: legacy extension management
|
||||
|
||||
angular.module('portainer.integrations.storidge', []).config([
|
||||
'$stateRegistryProvider',
|
||||
function ($stateRegistryProvider) {
|
||||
'use strict';
|
||||
|
||||
var storidge = {
|
||||
name: 'storidge',
|
||||
parent: 'root',
|
||||
abstract: true,
|
||||
url: '/storidge',
|
||||
};
|
||||
|
||||
var profiles = {
|
||||
name: 'storidge.profiles',
|
||||
url: '/profiles',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/profiles/profiles.html',
|
||||
controller: 'StoridgeProfilesController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var profile = {
|
||||
name: 'storidge.profiles.profile',
|
||||
url: '/:id',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/profiles/edit/profile.html',
|
||||
controller: 'StoridgeProfileController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var drives = {
|
||||
name: 'storidge.drives',
|
||||
url: '/drives',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/drives/drives.html',
|
||||
controller: 'StoridgeDrivesController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var drive = {
|
||||
name: 'storidge.drives.drive',
|
||||
url: '/:id',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/drives/inspect/drive.html',
|
||||
controller: 'StoridgeDriveController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var snapshot = {
|
||||
name: 'docker.volumes.volume.snapshot',
|
||||
url: '/:snapshotId',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/snapshots/inspect/snapshot.html',
|
||||
controller: 'StoridgeSnapshotController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var profileCreation = {
|
||||
name: 'storidge.profiles.new',
|
||||
url: '/new',
|
||||
params: {
|
||||
profileName: '',
|
||||
},
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/profiles/create/createprofile.html',
|
||||
controller: 'StoridgeCreateProfileController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var cluster = {
|
||||
name: 'storidge.cluster',
|
||||
url: '/cluster',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/cluster/cluster.html',
|
||||
controller: 'StoridgeClusterController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var node = {
|
||||
name: 'storidge.cluster.node',
|
||||
url: '/:name',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/nodes/inspect/node.html',
|
||||
controller: 'StoridgeNodeController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var monitor = {
|
||||
name: 'storidge.monitor',
|
||||
url: '/events',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/monitor/monitor.html',
|
||||
controller: 'StoridgeMonitorController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
$stateRegistryProvider.register(storidge);
|
||||
$stateRegistryProvider.register(drives);
|
||||
$stateRegistryProvider.register(drive);
|
||||
$stateRegistryProvider.register(snapshot);
|
||||
$stateRegistryProvider.register(profiles);
|
||||
$stateRegistryProvider.register(profile);
|
||||
$stateRegistryProvider.register(profileCreation);
|
||||
$stateRegistryProvider.register(cluster);
|
||||
$stateRegistryProvider.register(node);
|
||||
$stateRegistryProvider.register(monitor);
|
||||
},
|
||||
]);
|
|
@ -1,91 +0,0 @@
|
|||
<div class="datatable">
|
||||
<rd-widget>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="toolBar">
|
||||
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div>
|
||||
</div>
|
||||
<div class="searchBar">
|
||||
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
|
||||
<input
|
||||
type="text"
|
||||
class="searchInput"
|
||||
ng-model="$ctrl.state.textFilter"
|
||||
ng-change="$ctrl.onTextFilterChange()"
|
||||
placeholder="Search..."
|
||||
auto-focus
|
||||
ng-model-options="{ debounce: 300 }"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Time')">
|
||||
Date
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Time' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Time' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Category')">
|
||||
Category
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Category' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Category' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Module')">
|
||||
Module
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Module' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Module' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Content')">
|
||||
Content
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Content' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Content' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
>
|
||||
<td>{{ item.Time }}</td>
|
||||
<td>{{ item.Category }}</td>
|
||||
<td>{{ item.Module }}</td>
|
||||
<td>{{ item.Content }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!$ctrl.dataset">
|
||||
<td colspan="4" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
|
||||
<td colspan="4" class="text-center text-muted">No events available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer" ng-if="$ctrl.dataset">
|
||||
<div class="paginationControls">
|
||||
<form class="form-inline">
|
||||
<span class="limitSelector">
|
||||
<span style="margin-right: 5px"> Items per page </span>
|
||||
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</span>
|
||||
<dir-pagination-controls max-size="5"></dir-pagination-controls>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
|
@ -1,12 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').component('storidgeClusterEventsDatatable', {
|
||||
templateUrl: './storidgeClusterEventsDatatable.html',
|
||||
controller: 'GenericDatatableController',
|
||||
bindings: {
|
||||
titleText: '@',
|
||||
titleIcon: '@',
|
||||
dataset: '<',
|
||||
tableKey: '@',
|
||||
orderBy: '@',
|
||||
reverseOrder: '<',
|
||||
},
|
||||
});
|
|
@ -1,137 +0,0 @@
|
|||
<div class="datatable">
|
||||
<rd-widget>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="toolBar">
|
||||
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div>
|
||||
</div>
|
||||
<div class="actionBar">
|
||||
<button type="button" class="btn btn-sm btn-primary" ng-click="$ctrl.rescanAction()"> <i class="fa fa-sync space-right" aria-hidden="true"></i>Rescan drives </button>
|
||||
</div>
|
||||
<div class="searchBar">
|
||||
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
|
||||
<input
|
||||
type="text"
|
||||
class="searchInput"
|
||||
ng-model="$ctrl.state.textFilter"
|
||||
ng-change="$ctrl.onTextFilterChange()"
|
||||
placeholder="Search..."
|
||||
auto-focus
|
||||
ng-model-options="{ debounce: 300 }"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Id')">
|
||||
Id
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Node')">
|
||||
Node
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Node' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Node' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Device')">
|
||||
Device
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Device' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Device' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Size')">
|
||||
Size
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Size' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Size' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Use')">
|
||||
Use
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Use' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Use' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Type')">
|
||||
Type
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Type' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Type' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Status')">
|
||||
Status
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Status' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Status' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th> Actions </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
>
|
||||
<td>
|
||||
<a ui-sref="storidge.drives.drive({id: item.Id})"> {{ item.Id }}</a>
|
||||
</td>
|
||||
<td>{{ item.Node }}</td>
|
||||
<td>{{ item.Device }}</td>
|
||||
<td>{{ item.Size }}</td>
|
||||
<td>{{ item.Use }}</td>
|
||||
<td>{{ item.Type }}</td>
|
||||
<td>
|
||||
<span class="label label-{{ item.Status | drivestatusbadge }}">{{ item.Status | capitalize }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
ng-if="item.Status === 'available'"
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary btn-datatable"
|
||||
ng-click="$ctrl.addAction(item, $index)"
|
||||
button-spinner="$ctrl.additionInProgress[$index]"
|
||||
ng-disabled="$ctrl.actionInProgress"
|
||||
>
|
||||
<span ng-hide="$ctrl.additionInProgress[$index]"><i class="fa fa-plus space-right" aria-hidden="true"></i>Add to storage pool</span>
|
||||
<span ng-show="$ctrl.additionInProgress[$index]">Addition in progress...</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!$ctrl.dataset">
|
||||
<td colspan="7" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
|
||||
<td class="text-center text-muted">No drives available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer" ng-if="$ctrl.dataset">
|
||||
<div class="infoBar" ng-if="$ctrl.state.selectedItemCount !== 0"> {{ $ctrl.state.selectedItemCount }} item(s) selected </div>
|
||||
<div class="paginationControls">
|
||||
<form class="form-inline">
|
||||
<span class="limitSelector">
|
||||
<span style="margin-right: 5px"> Items per page </span>
|
||||
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</span>
|
||||
<dir-pagination-controls max-size="5"></dir-pagination-controls>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').component('storidgeDrivesDatatable', {
|
||||
templateUrl: './storidgeDrivesDatatable.html',
|
||||
controller: 'StoridgeDrivesDatatableController',
|
||||
bindings: {
|
||||
titleText: '@',
|
||||
titleIcon: '@',
|
||||
dataset: '<',
|
||||
tableKey: '@',
|
||||
orderBy: '@',
|
||||
reverseOrder: '<',
|
||||
removeAction: '<',
|
||||
addAction: '<',
|
||||
rescanAction: '<',
|
||||
actionInProgress: '<',
|
||||
additionInProgress: '<',
|
||||
},
|
||||
});
|
|
@ -1,45 +0,0 @@
|
|||
angular.module('portainer.docker').controller('StoridgeDrivesDatatableController', [
|
||||
'$scope',
|
||||
'$controller',
|
||||
'DatatableService',
|
||||
function ($scope, $controller, DatatableService) {
|
||||
angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
|
||||
|
||||
this.allowSelection = function (item) {
|
||||
return item.Status !== 'normal';
|
||||
};
|
||||
|
||||
this.$onInit = function () {
|
||||
this.setDefaults();
|
||||
this.prepareTableFromDataset();
|
||||
|
||||
this.state.orderBy = this.orderBy;
|
||||
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
|
||||
if (storedOrder !== null) {
|
||||
this.state.reverseOrder = storedOrder.reverse;
|
||||
this.state.orderBy = storedOrder.orderBy;
|
||||
}
|
||||
|
||||
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
|
||||
if (textFilter !== null) {
|
||||
this.state.textFilter = textFilter;
|
||||
this.onTextFilterChange();
|
||||
}
|
||||
|
||||
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
|
||||
if (storedFilters !== null) {
|
||||
this.filters = storedFilters;
|
||||
}
|
||||
if (this.filters && this.filters.state) {
|
||||
this.filters.state.open = false;
|
||||
}
|
||||
|
||||
var storedSettings = DatatableService.getDataTableSettings(this.tableKey);
|
||||
if (storedSettings !== null) {
|
||||
this.settings = storedSettings;
|
||||
this.settings.open = false;
|
||||
}
|
||||
this.onSettingsRepeaterChange();
|
||||
};
|
||||
},
|
||||
]);
|
|
@ -1,113 +0,0 @@
|
|||
<div class="datatable">
|
||||
<rd-widget>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="toolBar">
|
||||
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div>
|
||||
</div>
|
||||
<div class="actionBar">
|
||||
<div class="row">
|
||||
<button type="button" class="btn btn-sm btn-primary" ng-click="$ctrl.addNodeAction()"> <i class="fa fa-plus space-right" aria-hidden="true"></i>Add node </button>
|
||||
<div style="margin-bottom: 0px">
|
||||
<span ng-if="$ctrl.addInfo" class="text-muted small">
|
||||
To add a node to this cluster, run the following command on your new node
|
||||
<code>
|
||||
{{ $ctrl.addInfo }}
|
||||
</code>
|
||||
<span class="btn btn-primary btn-sm space-left" ng-click="$ctrl.copyAddNodeCommand()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy</span>
|
||||
<span>
|
||||
<i id="copyNotification" class="fa fa-check green-icon" aria-hidden="true" style="margin-left: 7px; display: none"></i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchBar">
|
||||
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
|
||||
<input
|
||||
type="text"
|
||||
class="searchInput"
|
||||
ng-model="$ctrl.state.textFilter"
|
||||
ng-change="$ctrl.onTextFilterChange()"
|
||||
placeholder="Search..."
|
||||
auto-focus
|
||||
ng-model-options="{ debounce: 300 }"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Name')">
|
||||
Name
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Name' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Name' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('IP')">
|
||||
IP Address
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IP' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IP' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Role')">
|
||||
Role
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Role' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Role' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Status')">
|
||||
Status
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Status' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Status' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
>
|
||||
<td>
|
||||
<a ui-sref="storidge.cluster.node({name: item.Name})"> {{ item.Name }}</a>
|
||||
</td>
|
||||
<td>{{ item.IP }}</td>
|
||||
<td>{{ item.Role }}</td>
|
||||
<td>
|
||||
<i class="fa fa-heartbeat space-right {{ item.Status | storidgeNodeStatusBadge }}"></i>
|
||||
{{ item.Status }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!$ctrl.dataset">
|
||||
<td colspan="4" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
|
||||
<td colspan="4" class="text-center text-muted">No nodes available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer" ng-if="$ctrl.dataset">
|
||||
<div class="paginationControls">
|
||||
<form class="form-inline">
|
||||
<span class="limitSelector">
|
||||
<span style="margin-right: 5px"> Items per page </span>
|
||||
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</span>
|
||||
<dir-pagination-controls max-size="5"></dir-pagination-controls>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
|
@ -1,12 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').component('storidgeNodesDatatable', {
|
||||
templateUrl: './storidgeNodesDatatable.html',
|
||||
controller: 'StoridgeNodesDatatableController',
|
||||
bindings: {
|
||||
titleText: '@',
|
||||
titleIcon: '@',
|
||||
dataset: '<',
|
||||
tableKey: '@',
|
||||
orderBy: '@',
|
||||
reverseOrder: '<',
|
||||
},
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').controller('StoridgeNodesDatatableController', [
|
||||
'$scope',
|
||||
'$controller',
|
||||
'clipboard',
|
||||
'Notifications',
|
||||
'StoridgeNodeService',
|
||||
'DatatableService',
|
||||
function ($scope, $controller, clipboard, Notifications, StoridgeNodeService, DatatableService) {
|
||||
angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
|
||||
|
||||
var ctrl = this;
|
||||
|
||||
this.addNodeAction = function () {
|
||||
StoridgeNodeService.add()
|
||||
.then(function sucess(data) {
|
||||
ctrl.addInfo = data.content;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve the "add node" command');
|
||||
});
|
||||
};
|
||||
|
||||
this.copyAddNodeCommand = function () {
|
||||
clipboard.copyText(ctrl.addInfo);
|
||||
$('#copyNotification').show();
|
||||
$('#copyNotification').fadeOut(2000);
|
||||
};
|
||||
|
||||
this.$onInit = function () {
|
||||
this.setDefaults();
|
||||
this.prepareTableFromDataset();
|
||||
|
||||
this.state.orderBy = this.orderBy;
|
||||
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
|
||||
if (storedOrder !== null) {
|
||||
this.state.reverseOrder = storedOrder.reverse;
|
||||
this.state.orderBy = storedOrder.orderBy;
|
||||
}
|
||||
|
||||
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
|
||||
if (textFilter !== null) {
|
||||
this.state.textFilter = textFilter;
|
||||
this.onTextFilterChange();
|
||||
}
|
||||
|
||||
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
|
||||
if (storedFilters !== null) {
|
||||
this.filters = storedFilters;
|
||||
}
|
||||
if (this.filters && this.filters.state) {
|
||||
this.filters.state.open = false;
|
||||
}
|
||||
|
||||
var storedSettings = DatatableService.getDataTableSettings(this.tableKey);
|
||||
if (storedSettings !== null) {
|
||||
this.settings = storedSettings;
|
||||
this.settings.open = false;
|
||||
}
|
||||
this.onSettingsRepeaterChange();
|
||||
};
|
||||
},
|
||||
]);
|
|
@ -1,8 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label for="storidge_profile" class="col-sm-2 col-lg-1 control-label text-left">Profile</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select id="storidge_profile" ng-model="$ctrl.storidgeProfile" ng-options="profile.Name for profile in $ctrl.profiles" class="form-control">
|
||||
<option selected disabled hidden value="">Select a profile</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
|
@ -1,7 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').component('storidgeProfileSelector', {
|
||||
templateUrl: './storidgeProfileSelector.html',
|
||||
controller: 'StoridgeProfileSelectorController',
|
||||
bindings: {
|
||||
storidgeProfile: '=',
|
||||
},
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').controller('StoridgeProfileSelectorController', [
|
||||
'StoridgeProfileService',
|
||||
'Notifications',
|
||||
function (StoridgeProfileService, Notifications) {
|
||||
var ctrl = this;
|
||||
|
||||
this.$onInit = $onInit;
|
||||
function $onInit() {
|
||||
StoridgeProfileService.profiles()
|
||||
.then(function success(data) {
|
||||
ctrl.profiles = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve Storidge profiles');
|
||||
});
|
||||
}
|
||||
},
|
||||
]);
|
|
@ -1,83 +0,0 @@
|
|||
<div class="datatable">
|
||||
<rd-widget>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="toolBar">
|
||||
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div>
|
||||
</div>
|
||||
<div class="actionBar">
|
||||
<button type="button" class="btn btn-sm btn-danger" ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeAction($ctrl.state.selectedItems)">
|
||||
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
|
||||
</button>
|
||||
</div>
|
||||
<div class="searchBar">
|
||||
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
|
||||
<input
|
||||
type="text"
|
||||
class="searchInput"
|
||||
ng-model="$ctrl.state.textFilter"
|
||||
ng-change="$ctrl.onTextFilterChange()"
|
||||
placeholder="Search..."
|
||||
auto-focus
|
||||
ng-model-options="{ debounce: 300 }"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<span class="md-checkbox">
|
||||
<input id="select_all" type="checkbox" ng-model="$ctrl.state.selectAll" ng-change="$ctrl.selectAll()" />
|
||||
<label for="select_all"></label>
|
||||
</span>
|
||||
<a ng-click="$ctrl.changeOrderBy('Name')">
|
||||
Name
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Name' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Name' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
>
|
||||
<td>
|
||||
<span class="md-checkbox">
|
||||
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" />
|
||||
<label for="select_{{ $index }}"></label>
|
||||
</span>
|
||||
<a ui-sref="storidge.profiles.profile({id: item.Name})">{{ item.Name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!$ctrl.dataset">
|
||||
<td class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
|
||||
<td class="text-center text-muted">No profile available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer" ng-if="$ctrl.dataset">
|
||||
<div class="infoBar" ng-if="$ctrl.state.selectedItemCount !== 0"> {{ $ctrl.state.selectedItemCount }} item(s) selected </div>
|
||||
<div class="paginationControls">
|
||||
<form class="form-inline">
|
||||
<span class="limitSelector">
|
||||
<span style="margin-right: 5px"> Items per page </span>
|
||||
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</span>
|
||||
<dir-pagination-controls max-size="5"></dir-pagination-controls>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
|
@ -1,13 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').component('storidgeProfilesDatatable', {
|
||||
templateUrl: './storidgeProfilesDatatable.html',
|
||||
controller: 'GenericDatatableController',
|
||||
bindings: {
|
||||
titleText: '@',
|
||||
titleIcon: '@',
|
||||
dataset: '<',
|
||||
tableKey: '@',
|
||||
orderBy: '@',
|
||||
reverseOrder: '<',
|
||||
removeAction: '<',
|
||||
},
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
<rd-widget>
|
||||
<rd-widget-header icon="fa-plus" title-text="Create snapshot"> </rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="description" class="col-sm-3 col-lg-2 control-label text-left">Description</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="description" ng-model="$ctrl.formValues.Description" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="$ctrl.state.actionInProgress"
|
||||
ng-click="$ctrl.createSnapshot()"
|
||||
button-spinner="$ctrl.state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="$ctrl.state.actionInProgress">Create snapshot</span>
|
||||
<span ng-show="$ctrl.state.actionInProgress">Creating snapshot...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
|
@ -1,7 +0,0 @@
|
|||
angular.module('portainer.docker').component('storidgeSnapshotCreation', {
|
||||
templateUrl: './storidgeSnapshotCreation.html',
|
||||
controller: 'StoridgeSnapshotCreationController',
|
||||
bindings: {
|
||||
volumeId: '<',
|
||||
},
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
angular.module('portainer.docker').controller('StoridgeSnapshotCreationController', [
|
||||
'StoridgeSnapshotService',
|
||||
'Notifications',
|
||||
'$state',
|
||||
function (StoridgeSnapshotService, Notifications, $state) {
|
||||
var ctrl = this;
|
||||
|
||||
this.formValues = {};
|
||||
this.state = {
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
this.createSnapshot = function () {
|
||||
ctrl.state.actionInProgress = true;
|
||||
StoridgeSnapshotService.create(ctrl.volumeId, ctrl.formValues.Description)
|
||||
.then(function success() {
|
||||
Notifications.success('Success', 'Snapshot successfully created');
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to create snapshot');
|
||||
})
|
||||
.finally(function final() {
|
||||
ctrl.state.actionInProgress = false;
|
||||
});
|
||||
};
|
||||
},
|
||||
]);
|
|
@ -1,98 +0,0 @@
|
|||
<div class="datatable">
|
||||
<rd-widget>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="toolBar">
|
||||
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div>
|
||||
</div>
|
||||
<div class="actionBar">
|
||||
<button type="button" class="btn btn-sm btn-danger" ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeAction($ctrl.state.selectedItems)">
|
||||
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
|
||||
</button>
|
||||
</div>
|
||||
<div class="searchBar">
|
||||
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
|
||||
<input
|
||||
type="text"
|
||||
class="searchInput"
|
||||
ng-model="$ctrl.state.textFilter"
|
||||
ng-change="$ctrl.onTextFilterChange()"
|
||||
placeholder="Search..."
|
||||
ng-model-options="{ debounce: 300 }"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<span class="md-checkbox">
|
||||
<input id="select_all" type="checkbox" ng-model="$ctrl.state.selectAll" ng-change="$ctrl.selectAll()" />
|
||||
<label for="select_all"></label>
|
||||
</span>
|
||||
<a ng-click="$ctrl.changeOrderBy('Id')">
|
||||
Id
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Date')">
|
||||
Date
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Date' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Date' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Description')">
|
||||
Description
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Description' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Description' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
>
|
||||
<td>
|
||||
<span class="md-checkbox">
|
||||
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" ng-disabled="item.Status === 'normal'" />
|
||||
<label for="select_{{ $index }}"></label>
|
||||
</span>
|
||||
<a ui-sref="docker.volumes.volume.snapshot({snapshotId: item.Id})"> {{ item.Id }}</a>
|
||||
</td>
|
||||
<td>{{ item.Date }}</td>
|
||||
<td>{{ item.Description }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!$ctrl.dataset">
|
||||
<td colspan="7" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
|
||||
<td colspan="3" class="text-center text-muted">No snapshots available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer" ng-if="$ctrl.dataset">
|
||||
<div class="infoBar" ng-if="$ctrl.state.selectedItemCount !== 0"> {{ $ctrl.state.selectedItemCount }} item(s) selected </div>
|
||||
<div class="paginationControls">
|
||||
<form class="form-inline">
|
||||
<span class="limitSelector">
|
||||
<span style="margin-right: 5px"> Items per page </span>
|
||||
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</span>
|
||||
<dir-pagination-controls max-size="5"></dir-pagination-controls>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
|
@ -1,13 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').component('storidgeSnapshotsDatatable', {
|
||||
templateUrl: './storidgeSnapshotsDatatable.html',
|
||||
controller: 'GenericDatatableController',
|
||||
bindings: {
|
||||
titleText: '@',
|
||||
titleIcon: '@',
|
||||
dataset: '<',
|
||||
tableKey: '@',
|
||||
orderBy: '@',
|
||||
reverseOrder: '<',
|
||||
removeAction: '<',
|
||||
},
|
||||
});
|
|
@ -1,198 +0,0 @@
|
|||
<rd-widget>
|
||||
<rd-widget-header icon="fa-cube" title-text="Storidge details">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-click="$ctrl.updateVolume()" ng-if="!$ctrl.state.updateInProgress">
|
||||
<span>Update the volume</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-click="$ctrl.confirmUpdate()" ng-if="$ctrl.state.updateInProgress" button-spinner="$ctrl.state.isUpdating">
|
||||
<span ng-hide="$ctrl.state.isUpdating">Confirm</span>
|
||||
<span ng-show="$ctrl.state.isUpdating">Updating the volume...</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="$ctrl.cancelUpdate()" ng-if="$ctrl.state.updateInProgress" ng-disabled="$ctrl.state.isUpdating">
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-body classes="no-padding" ng-if="!$ctrl.state.updateInProgress">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ $ctrl.volume.Name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UUID</td>
|
||||
<td>{{ $ctrl.volume.Uuid }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Node</td>
|
||||
<td>{{ $ctrl.volume.Node }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Node ID</td>
|
||||
<td>{{ $ctrl.volume.NodeID }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Directory</td>
|
||||
<td>{{ $ctrl.volume.Directory }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Capacity</td>
|
||||
<td>{{ $ctrl.volume.Capacity }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Allocated</td>
|
||||
<td>{{ $ctrl.volume.Allocated }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IOPS Min</td>
|
||||
<td>{{ $ctrl.volume.IOPSMin }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IOPS Max</td>
|
||||
<td>{{ $ctrl.volume.IOPSMax }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bandwidth Min</td>
|
||||
<td>{{ $ctrl.volume.BandwidthMin }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bandwidth Max</td>
|
||||
<td>{{ $ctrl.volume.BandwidthMax }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Local Drive Only</td>
|
||||
<td>{{ $ctrl.volume.LocalDriveOnly }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Provisioning</td>
|
||||
<td>{{ $ctrl.volume.Provisioning }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Redundancy</td>
|
||||
<td>{{ $ctrl.volume.Redundancy }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Vdisk</td>
|
||||
<td>{{ $ctrl.volume.Vdisk }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP</td>
|
||||
<td>{{ $ctrl.volume.IP }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Drive Type</td>
|
||||
<td>{{ $ctrl.volume.DriveType }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Encryption</td>
|
||||
<td>{{ $ctrl.volume.Encryption }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Snapshot Enabled</td>
|
||||
<td>{{ $ctrl.volume.SnapshotEnabled }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Snapshot Interval</td>
|
||||
<td>{{ $ctrl.volume.SnapshotInterval }} minute(s)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Max Snapshots</td>
|
||||
<td>{{ $ctrl.volume.SnapshotMax }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Filesystem</td>
|
||||
<td>{{ $ctrl.volume.Filesystem }}</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.volume.Labels">
|
||||
<td>Labels</td>
|
||||
<td>
|
||||
<table class="table table-bordered table-condensed">
|
||||
<tr ng-repeat="var in $ctrl.volume.Labels">
|
||||
<td>{{ var|key: '=' }}</td>
|
||||
<td>{{ var|value: '=' }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
<rd-widget-body ng-if="$ctrl.state.updateInProgress">
|
||||
<form class="form-horizontal" name="storidgeUpdateVolumeForm">
|
||||
<!-- Node -->
|
||||
<div class="form-group">
|
||||
<label for="volume_node" class="col-sm-2 col-lg-1 control-label text-left">Node</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="$ctrl.formValues.Node" name="volume_node" placeholder="2" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Node -->
|
||||
<!-- Capacity -->
|
||||
<div class="form-group">
|
||||
<label for="volume_capacity" class="col-sm-2 col-lg-1 control-label text-left">Capacity</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="$ctrl.formValues.Capacity" name="volume_capacity" placeholder="2" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Capacity -->
|
||||
<!-- IOPS -->
|
||||
<div class="form-group">
|
||||
<label for="min_iops" class="col-sm-2 col-lg-1 control-label text-left">Min IOPS</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="number" class="form-control" ng-model="$ctrl.formValues.IOPSMin" name="min_iops" placeholder="100" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="max_iops" class="col-sm-2 col-lg-1 control-label text-left">Max IOPS</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="number" class="form-control" ng-model="$ctrl.formValues.IOPSMax" name="max_iops" placeholder="2000" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !IOPS -->
|
||||
<!-- Bandwidth -->
|
||||
<div class="form-group">
|
||||
<label for="min_bandwidth" class="col-sm-2 col-lg-1 control-label text-left">Min Bandwidth</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="number" class="form-control" ng-model="$ctrl.formValues.BandwidthMin" name="min_bandwidth" placeholder="100" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="max_bandwidth" class="col-sm-2 col-lg-1 control-label text-left">Max Bandwidth</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="number" class="form-control" ng-model="$ctrl.formValues.BandwidthMax" name="max_bandwidth" placeholder="2000" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Bandwidth -->
|
||||
<!-- labels -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12" style="margin-top: 5px">
|
||||
<label class="control-label text-left">Labels</label>
|
||||
<span class="label label-default interactive" style="margin-left: 10px" ng-click="$ctrl.addLabel()">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i> add label
|
||||
</span>
|
||||
</div>
|
||||
<!-- labels-input-list -->
|
||||
<div class="col-sm-12 form-inline" style="margin-top: 10px">
|
||||
<div ng-repeat="label in $ctrl.formValues.Labels" style="margin-top: 2px">
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">name</span>
|
||||
<input type="text" class="form-control" ng-model="label.name" placeholder="e.g. com.example.foo" />
|
||||
</div>
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">value</span>
|
||||
<input type="text" class="form-control" ng-model="label.value" placeholder="e.g. bar" />
|
||||
</div>
|
||||
<button class="btn btn-sm btn-danger" type="button" ng-click="$ctrl.removeLabel($index)">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !labels-input-list -->
|
||||
</div>
|
||||
<!-- !labels -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
|
@ -1,7 +0,0 @@
|
|||
angular.module('portainer.docker').component('volumeStoridgeInfo', {
|
||||
templateUrl: './volumeStoridgeInfo.html',
|
||||
controller: 'VolumeStoridgeInfoController',
|
||||
bindings: {
|
||||
volume: '<',
|
||||
},
|
||||
});
|
|
@ -1,105 +0,0 @@
|
|||
angular.module('portainer.docker').controller('VolumeStoridgeInfoController', [
|
||||
'$state',
|
||||
'StoridgeVolumeService',
|
||||
'Notifications',
|
||||
function ($state, StoridgeVolumeService, Notifications) {
|
||||
var ctrl = this;
|
||||
|
||||
this.state = {
|
||||
updateInProgress: false,
|
||||
isUpdating: false,
|
||||
};
|
||||
|
||||
this.addLabel = function () {
|
||||
this.formValues.Labels.push({ name: '', value: '' });
|
||||
};
|
||||
|
||||
this.removeLabel = function (index) {
|
||||
this.formValues.Labels.splice(index, 1);
|
||||
};
|
||||
|
||||
this.initLabels = function () {
|
||||
var labels = this.volume.Labels;
|
||||
if (labels) {
|
||||
this.formValues.Labels = Object.keys(labels).map(function (key) {
|
||||
return { name: key, value: labels[key] };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.updateVolume = function () {
|
||||
this.state.updateInProgress = true;
|
||||
this.formValues = {
|
||||
IOPSMin: this.volume.IOPSMin,
|
||||
IOPSMax: this.volume.IOPSMax,
|
||||
Node: this.volume.Node,
|
||||
Capacity: this.volume.Capacity,
|
||||
BandwidthMin: this.volume.BandwidthMin,
|
||||
BandwidthMax: this.volume.BandwidthMax,
|
||||
Labels: [],
|
||||
};
|
||||
this.initLabels();
|
||||
};
|
||||
|
||||
this.cancelUpdate = function () {
|
||||
this.state.updateInProgress = false;
|
||||
this.formValues = {};
|
||||
};
|
||||
|
||||
this.prepareLabels = function (volume) {
|
||||
var labels = {};
|
||||
this.formValues.Labels.forEach(function (label) {
|
||||
if (label.name && label.value) {
|
||||
labels[label.name] = label.value;
|
||||
}
|
||||
});
|
||||
volume.Labels = labels;
|
||||
};
|
||||
|
||||
this.prepareVolume = function () {
|
||||
var volume = angular.copy(this.formValues);
|
||||
var data = this.volume;
|
||||
|
||||
if (volume.Node === data.Node || !volume.Node) {
|
||||
delete volume.Node;
|
||||
}
|
||||
if (volume.Capacity === data.Capacity || !volume.Capacity) {
|
||||
delete volume.Capacity;
|
||||
}
|
||||
if (volume.IOPSMin === data.IOPSMin || !volume.IOPSMin) {
|
||||
delete volume.IOPSMin;
|
||||
} else {
|
||||
volume.IOPSMin = volume.IOPSMin.toString();
|
||||
}
|
||||
if (volume.IOPSMax === data.IOPSMax || !volume.IOPSMax) {
|
||||
delete volume.IOPSMax;
|
||||
} else {
|
||||
volume.IOPSMax = volume.IOPSMax.toString();
|
||||
}
|
||||
if (volume.BandwidthMin === data.BandwidthMin || !volume.BandwidthMin) {
|
||||
delete volume.BandwidthMin;
|
||||
}
|
||||
if (volume.BandwidthMax === data.BandwidthMax || !volume.BandwidthMax) {
|
||||
delete volume.BandwidthMax;
|
||||
}
|
||||
this.prepareLabels(volume);
|
||||
return volume;
|
||||
};
|
||||
|
||||
this.confirmUpdate = function () {
|
||||
this.state.isUpdating = true;
|
||||
|
||||
var volume = this.prepareVolume();
|
||||
volume.Name = this.volume.Name;
|
||||
StoridgeVolumeService.update(volume)
|
||||
.then(function success() {
|
||||
Notifications.success('Volume successfully updated');
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update volume');
|
||||
ctrl.state.isUpdating = false;
|
||||
});
|
||||
};
|
||||
},
|
||||
]);
|
|
@ -1,53 +0,0 @@
|
|||
import _ from 'lodash-es';
|
||||
|
||||
angular
|
||||
.module('portainer.integrations.storidge')
|
||||
.filter('drivestatusbadge', function () {
|
||||
'use strict';
|
||||
return function (text) {
|
||||
var status = text ? _.toLower(text) : '';
|
||||
if (status === 'available') {
|
||||
return 'info';
|
||||
} else if (status === 'faulty') {
|
||||
return 'danger';
|
||||
}
|
||||
return 'success';
|
||||
};
|
||||
})
|
||||
.filter('storidgeNodeStatusBadge', function () {
|
||||
'use strict';
|
||||
return function (text) {
|
||||
var status = text ? _.toLower(text) : '';
|
||||
if (status === 'cordoned' || status === 'maintenance') {
|
||||
return 'orange-icon';
|
||||
} else if (status === 'leaving' || status === 'failed') {
|
||||
return 'red-icon';
|
||||
}
|
||||
return 'green-icon';
|
||||
};
|
||||
})
|
||||
.filter('storidgeClusterConditionBadge', function () {
|
||||
'use strict';
|
||||
return function (text) {
|
||||
var status = text ? _.toLower(text) : '';
|
||||
if (status === 'alert') {
|
||||
return 'red-icon';
|
||||
} else if (status === 'warning') {
|
||||
return 'orange-icon';
|
||||
}
|
||||
return 'green-icon';
|
||||
};
|
||||
})
|
||||
.filter('bytes', function () {
|
||||
return function (bytes, precision) {
|
||||
bytes = parseFloat(bytes);
|
||||
if (isNaN(bytes) || !isFinite(bytes)) return '-';
|
||||
if (!precision) precision = 1;
|
||||
var units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
|
||||
var number = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
if (bytes === 0) {
|
||||
return '0 B';
|
||||
}
|
||||
return (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision) + ' ' + units[number];
|
||||
};
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
export function StoridgeDriveModel(data) {
|
||||
this.Id = data.driveid;
|
||||
this.Node = data.node;
|
||||
this.Use = data.use;
|
||||
this.Status = data.drivestatus.toLowerCase();
|
||||
this.Size = data.size;
|
||||
this.Type = data.type;
|
||||
this.Device = data.device;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
export function StoridgeEventModel(data) {
|
||||
this.Time = data.time;
|
||||
this.Category = data.category;
|
||||
this.Module = data.module;
|
||||
this.Content = data.content;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
export function StoridgeInfoModel(data) {
|
||||
this.Domain = data.domain;
|
||||
this.Nodes = data.nodes;
|
||||
this.Condition = data.condition;
|
||||
this.ProvisionedBandwidth = data.provisionedBandwidth;
|
||||
this.UsedBandwidth = data.usedBandwidth;
|
||||
this.FreeBandwidth = data.freeBandwidth;
|
||||
this.TotalBandwidth = data.totalBandwidth;
|
||||
this.ProvisionedIOPS = data.provisionedIOPS;
|
||||
this.UsedIOPS = data.usedIOPS;
|
||||
this.FreeIOPS = data.freeIOPS;
|
||||
this.TotalIOPS = data.totalIOPS;
|
||||
this.ProvisionedCapacity = data.provisionedCapacity;
|
||||
this.UsedCapacity = data.usedCapacity;
|
||||
this.FreeCapacity = data.freeCapacity;
|
||||
this.TotalCapacity = data.totalCapacity;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
export function StoridgeNodeModel(name, data) {
|
||||
this.Name = name;
|
||||
this.IP = data.ip;
|
||||
this.Role = data.role;
|
||||
this.Status = data.status;
|
||||
}
|
||||
|
||||
export function StoridgeNodeDetailedModel(name, properties) {
|
||||
this.Name = name;
|
||||
this.Condition = properties.condition;
|
||||
this.Domain = properties.domain;
|
||||
this.DomainID = properties.domainID;
|
||||
this.FreeBandwidth = properties.freeBandwidth;
|
||||
this.FreeCapacity = properties.freeCapacity;
|
||||
this.FreeIOPS = properties.freeIOPS;
|
||||
this.Hdds = properties.hdds;
|
||||
this.MetadataVersion = properties.metadataVersion;
|
||||
this.Nodes = properties.nodes;
|
||||
this.ProvisionedBandwidth = properties.provisionedBandwidth;
|
||||
this.ProvisionedCapacity = properties.provisionedCapacity;
|
||||
this.ProvisionedIOPS = properties.provisionedIOPS;
|
||||
this.Ssds = properties.ssds;
|
||||
this.Status = properties.status;
|
||||
this.TotalBandwidth = properties.totalBandwidth;
|
||||
this.TotalCapacity = properties.totalCapacity;
|
||||
this.TotalIOPS = properties.totalIOPS;
|
||||
this.UsedBandwidth = properties.usedBandwidth;
|
||||
this.UsedCapacity = properties.usedCapacity;
|
||||
this.UsedIOPS = properties.usedIOPS;
|
||||
this.Vdisks = properties.vdisks;
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
export function StoridgeProfileDefaultModel() {
|
||||
this.Directory = '/cio/';
|
||||
this.Capacity = 20;
|
||||
this.Redundancy = 2;
|
||||
this.Provisioning = 'thin';
|
||||
this.Type = 'ssd';
|
||||
this.MinIOPS = 100;
|
||||
this.MaxIOPS = 2000;
|
||||
this.MinBandwidth = 1;
|
||||
this.MaxBandwidth = 100;
|
||||
this.Filesystem = 'btrfs';
|
||||
this.SnapshotEnabled = false;
|
||||
this.SnapshotInterval = 1440;
|
||||
this.SnapshotMax = 1;
|
||||
this.EncryptionEnabled = false;
|
||||
this.InterfaceType = '';
|
||||
this.InterfaceDriver = '';
|
||||
this.InterfaceNetwork = '';
|
||||
this.InterfaceConf = '';
|
||||
this.Labels = [];
|
||||
}
|
||||
|
||||
export function StoridgeProfileListModel(data) {
|
||||
this.Name = data;
|
||||
this.Checked = false;
|
||||
}
|
||||
|
||||
export function StoridgeProfileModel(name, data) {
|
||||
this.Name = name;
|
||||
this.Directory = data.directory;
|
||||
this.Capacity = data.capacity;
|
||||
this.Provisioning = data.provision;
|
||||
this.Type = data.type;
|
||||
this.Redundancy = data.level;
|
||||
|
||||
if (data.iops) {
|
||||
this.MinIOPS = data.iops.min;
|
||||
this.MaxIOPS = data.iops.max;
|
||||
}
|
||||
|
||||
if (data.bandwidth) {
|
||||
this.MinBandwidth = data.bandwidth.min;
|
||||
this.MaxBandwidth = data.bandwidth.max;
|
||||
}
|
||||
|
||||
if (data.filesystem) {
|
||||
this.Filesystem = data.filesystem.type;
|
||||
}
|
||||
// this.Filesystem = data.filesystem;
|
||||
|
||||
var service = data.service;
|
||||
|
||||
if (service.snapshot) {
|
||||
this.SnapshotEnabled = service.snapshot.enabled;
|
||||
this.SnapshotInterval = service.snapshot.interval;
|
||||
this.SnapshotMax = service.snapshot.max;
|
||||
} else {
|
||||
this.SnapshotEnabled = false;
|
||||
}
|
||||
|
||||
if (service.encryption) {
|
||||
this.EncryptionEnabled = service.encryption.enabled;
|
||||
} else {
|
||||
this.EncryptionEnabled = false;
|
||||
}
|
||||
|
||||
if (data.interface) {
|
||||
this.InterfaceType = data.interface.type;
|
||||
this.InterfaceDriver = data.interface.driver;
|
||||
this.InterfaceNetwork = data.interface.network;
|
||||
this.InterfaceConf = data.interface.conf;
|
||||
}
|
||||
|
||||
if (data.label) {
|
||||
this.Labels = data.label;
|
||||
} else {
|
||||
this.Labels = [];
|
||||
}
|
||||
}
|
||||
|
||||
export function StoridgeCreateProfileRequest(model) {
|
||||
this.name = model.Name;
|
||||
this.capacity = model.Capacity;
|
||||
this.directory = model.Directory;
|
||||
this.provision = model.Provisioning;
|
||||
this.type = model.Type;
|
||||
this.level = model.Redundancy;
|
||||
if (model.MinIOPS && model.MaxIOPS) {
|
||||
this.iops = {
|
||||
min: model.MinIOPS,
|
||||
max: model.MaxIOPS,
|
||||
};
|
||||
}
|
||||
|
||||
if (model.MinBandwidth && model.MaxBandwidth) {
|
||||
this.bandwidth = {
|
||||
min: model.MinBandwidth,
|
||||
max: model.MaxBandwidth,
|
||||
};
|
||||
}
|
||||
|
||||
this.filesystem = {
|
||||
type: model.Filesystem,
|
||||
};
|
||||
|
||||
var service = {};
|
||||
|
||||
service.snapshot = {
|
||||
enabled: model.SnapshotEnabled,
|
||||
};
|
||||
if (model.SnapshotEnabled) {
|
||||
service.snapshot.interval = model.SnapshotInterval;
|
||||
service.snapshot.max = model.SnapshotMax;
|
||||
}
|
||||
|
||||
service.encryption = {
|
||||
enabled: model.EncryptionEnabled,
|
||||
};
|
||||
|
||||
this.service = service;
|
||||
|
||||
this.interface = {
|
||||
driver: model.InterfaceDriver,
|
||||
network: model.InterfaceNetwork,
|
||||
conf: model.InterfaceConf,
|
||||
};
|
||||
|
||||
if (model.InterfaceType) {
|
||||
this.interface.type = model.InterfaceType;
|
||||
}
|
||||
|
||||
this.label = model.Labels;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
export function StoridgeSnapshotModel(data) {
|
||||
this.Id = data.identifier;
|
||||
this.Date = data.date;
|
||||
this.Description = data.description;
|
||||
this.SourceID = data.sourceid;
|
||||
this.Type = data.type;
|
||||
this.Directory = data.directory;
|
||||
this.Source = data.source;
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
export function StoridgeVolumeModel(data) {
|
||||
this.Allocated = data.allocated;
|
||||
this.Capacity = data.capacity;
|
||||
this.Directory = data.directory;
|
||||
this.IOPSMax = data.maximumIOPS;
|
||||
this.IOPSMin = data.minimumIOPS;
|
||||
this.BandwidthMin = data.minimumBandwidth;
|
||||
this.BandwidthMax = data.maximumBandwidth;
|
||||
this.LocalDriveOnly = data.localDriveOnly;
|
||||
this.Name = data.name;
|
||||
this.Node = data.node;
|
||||
this.NodeID = data.nodeid;
|
||||
this.Provisioning = data.provisioning;
|
||||
this.Redundancy = data.redundancy;
|
||||
this.Uuid = data.uuid;
|
||||
this.Vdisk = data.vdisk;
|
||||
this.Labels = data.labels;
|
||||
|
||||
this.IP = data.ipaddr;
|
||||
this.DriveType = data.driveType;
|
||||
this.Encryption = data.encryption;
|
||||
this.SnapshotEnabled = data.snapshot;
|
||||
this.SnapshotInterval = data.snapInterval;
|
||||
this.SnapshotMax = data.maximumSnapshots;
|
||||
this.Filesystem = data.filesystem;
|
||||
}
|
||||
|
||||
export function StoridgeVolumeUpdateModel(data) {
|
||||
this.name = data.Name;
|
||||
this.opts = {
|
||||
node: data.Node,
|
||||
nodeid: data.NodeID,
|
||||
capacity: data.Capacity,
|
||||
iopsmin: data.IOPSMin,
|
||||
iopsmax: data.IOPSMax,
|
||||
bandwidthmin: data.BandwidthMin,
|
||||
bandwidthmax: data.BandwidthMax,
|
||||
};
|
||||
this.labels = data.Labels;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').factory('Storidge', [
|
||||
'$resource',
|
||||
'API_ENDPOINT_ENDPOINTS',
|
||||
'EndpointProvider',
|
||||
function StoridgeFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
|
||||
'use strict';
|
||||
return $resource(
|
||||
API_ENDPOINT_ENDPOINTS + '/:endpointId/storidge/:resource/:id/:action',
|
||||
{
|
||||
endpointId: EndpointProvider.endpointID,
|
||||
},
|
||||
{
|
||||
rebootCluster: { method: 'POST', params: { resource: 'clusters', action: 'reboot' } },
|
||||
shutdownCluster: { method: 'POST', params: { resource: 'clusters', action: 'shutdown' } },
|
||||
queryEvents: { method: 'GET', params: { resource: 'clusters', action: 'events' }, ignoreLoadingBar: true, isArray: true },
|
||||
getVersion: { method: 'GET', params: { resource: 'clusters', action: 'version' } },
|
||||
getInfo: { method: 'GET', params: { resource: 'clusters', action: 'info' }, ignoreLoadingBar: true },
|
||||
|
||||
queryNodes: { method: 'GET', params: { resource: 'nodes' } },
|
||||
getNode: { method: 'GET', params: { resource: 'nodes', id: '@id' } },
|
||||
addNode: { method: 'POST', params: { resource: 'nodes' } },
|
||||
removeNode: { method: 'DELETE', params: { resource: 'nodes', id: '@id' } },
|
||||
cordonNode: { method: 'POST', params: { resource: 'nodes', action: 'cordon', id: '@id' } },
|
||||
uncordonNode: { method: 'POST', params: { resource: 'nodes', action: 'uncordon', id: '@id' } },
|
||||
|
||||
queryProfiles: { method: 'GET', params: { resource: 'profiles' } },
|
||||
getProfile: { method: 'GET', params: { resource: 'profiles' } },
|
||||
createProfile: { method: 'POST', params: { resource: 'profiles' } },
|
||||
updateProfile: { method: 'PUT', params: { resource: 'profiles', id: '@name' } },
|
||||
deleteProfile: { method: 'DELETE', params: { resource: 'profiles' } },
|
||||
|
||||
queryDrives: { method: 'GET', params: { resource: 'drives' } },
|
||||
getDrive: { method: 'GET', params: { resource: 'drives', id: '@id' } },
|
||||
addDrive: { method: 'POST', params: { resource: 'drives' } },
|
||||
removeDrive: { method: 'DELETE', params: { resource: 'drives', id: '@id' } },
|
||||
rescanDrives: { method: 'POST', params: { resource: 'drives', action: 'rescan' } },
|
||||
|
||||
queryVolumes: { method: 'GET', params: { resource: 'volumes' } },
|
||||
createVolume: { method: 'POST', params: { resource: 'volumes' } },
|
||||
getVolume: { method: 'GET', params: { resource: 'volumes', id: '@id' } },
|
||||
updateVolume: { method: 'POST', params: { resource: 'volumes', id: '@name' } },
|
||||
removeVolume: { method: 'DELETE', params: { resource: 'volumes', id: '@id' } },
|
||||
|
||||
querySnapshots: { method: 'GET', params: { resource: 'volumes', id: '@id', action: 'snapshots' } },
|
||||
createSnapshot: { method: 'POST', params: { resource: 'volumes', id: '@id', action: 'snapshot' } },
|
||||
getSnapshot: { method: 'GET', params: { resource: 'snapshots', id: '@id' } },
|
||||
removeSnapshot: { method: 'DELETE', params: { resource: 'snapshots', id: '@id' } },
|
||||
}
|
||||
);
|
||||
},
|
||||
]);
|
|
@ -1,190 +0,0 @@
|
|||
import Chart from 'chart.js';
|
||||
import filesize from 'filesize';
|
||||
|
||||
angular.module('portainer.integrations.storidge').factory('StoridgeChartService', [
|
||||
function StoridgeChartService() {
|
||||
'use strict';
|
||||
|
||||
// Max. number of items to display on a chart
|
||||
var CHART_LIMIT = 600;
|
||||
|
||||
var service = {};
|
||||
|
||||
service.CreateCapacityChart = function (context) {
|
||||
return new Chart(context, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
data: [],
|
||||
backgroundColor: ['rgba(171, 213, 255, 0.7)', 'rgba(229, 57, 53, 0.7)'],
|
||||
},
|
||||
],
|
||||
labels: [],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: function (tooltipItem, data) {
|
||||
var dataset = data.datasets[tooltipItem.datasetIndex];
|
||||
var label = data.labels[tooltipItem.index];
|
||||
var value = dataset.data[tooltipItem.index];
|
||||
return label + ': ' + filesize(value, { base: 10, round: 1 });
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
responsiveAnimationDuration: 0,
|
||||
responsive: true,
|
||||
hover: {
|
||||
animationDuration: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
service.CreateIOPSChart = function (context) {
|
||||
return new Chart(context, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: 'IOPS',
|
||||
data: [],
|
||||
fill: true,
|
||||
backgroundColor: 'rgba(151,187,205,0.4)',
|
||||
borderColor: 'rgba(151,187,205,0.6)',
|
||||
pointBackgroundColor: 'rgba(151,187,205,1)',
|
||||
pointBorderColor: 'rgba(151,187,205,1)',
|
||||
pointRadius: 2,
|
||||
borderWidth: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
responsiveAnimationDuration: 0,
|
||||
responsive: true,
|
||||
tooltips: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
position: 'nearest',
|
||||
},
|
||||
hover: {
|
||||
animationDuration: 0,
|
||||
},
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
service.CreateBandwidthChart = function (context) {
|
||||
return new Chart(context, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Bandwidth',
|
||||
data: [],
|
||||
fill: true,
|
||||
backgroundColor: 'rgba(151,187,205,0.4)',
|
||||
borderColor: 'rgba(151,187,205,0.6)',
|
||||
pointBackgroundColor: 'rgba(151,187,205,1)',
|
||||
pointBorderColor: 'rgba(151,187,205,1)',
|
||||
pointRadius: 2,
|
||||
borderWidth: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
responsiveAnimationDuration: 0,
|
||||
responsive: true,
|
||||
tooltips: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
position: 'nearest',
|
||||
callbacks: {
|
||||
label: function (tooltipItem, data) {
|
||||
var datasetLabel = data.datasets[tooltipItem.datasetIndex].label;
|
||||
return bytePerSecBasedTooltipLabel(datasetLabel, tooltipItem.yLabel);
|
||||
},
|
||||
},
|
||||
},
|
||||
hover: {
|
||||
animationDuration: 0,
|
||||
},
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: bytePerSecBasedAxisLabel,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
service.UpdateChart = function (label, value, chart) {
|
||||
chart.data.labels.push(label);
|
||||
chart.data.datasets[0].data.push(value);
|
||||
|
||||
if (chart.data.datasets[0].data.length > CHART_LIMIT) {
|
||||
chart.data.labels.pop();
|
||||
chart.data.datasets[0].data.pop();
|
||||
}
|
||||
|
||||
chart.update(0);
|
||||
};
|
||||
|
||||
service.UpdatePieChart = function (label, value, chart) {
|
||||
var idx = chart.data.labels.indexOf(label);
|
||||
if (idx > -1) {
|
||||
chart.data.datasets[0].data[idx] = value;
|
||||
} else {
|
||||
chart.data.labels.push(label);
|
||||
chart.data.datasets[0].data.push(value);
|
||||
}
|
||||
|
||||
chart.update(0);
|
||||
};
|
||||
|
||||
function bytePerSecBasedTooltipLabel(label, value) {
|
||||
var processedValue = 0;
|
||||
if (value > 5) {
|
||||
processedValue = filesize(value, { base: 10, round: 1 });
|
||||
} else {
|
||||
processedValue = value.toFixed(1) + 'B';
|
||||
}
|
||||
return label + ': ' + processedValue + '/s';
|
||||
}
|
||||
|
||||
function bytePerSecBasedAxisLabel(value) {
|
||||
if (value > 5) {
|
||||
return filesize(value, { base: 10, round: 1 });
|
||||
}
|
||||
return value.toFixed(1) + 'B/s';
|
||||
}
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -1,68 +0,0 @@
|
|||
import { StoridgeInfoModel } from '../models/info';
|
||||
import { StoridgeEventModel } from '../models/events';
|
||||
|
||||
angular.module('portainer.integrations.storidge').factory('StoridgeClusterService', [
|
||||
'$q',
|
||||
'Storidge',
|
||||
function StoridgeClusterServiceFactory($q, Storidge) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.reboot = function () {
|
||||
return Storidge.rebootCluster().$promise;
|
||||
};
|
||||
|
||||
service.shutdown = function () {
|
||||
return Storidge.shutdownCluster().$promise;
|
||||
};
|
||||
|
||||
service.info = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.getInfo()
|
||||
.$promise.then(function success(data) {
|
||||
var info = new StoridgeInfoModel(data);
|
||||
deferred.resolve(info);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge information', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.version = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.getVersion()
|
||||
.$promise.then(function success(data) {
|
||||
var version = data.version;
|
||||
deferred.resolve(version);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge version', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.events = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.queryEvents()
|
||||
.$promise.then(function success(data) {
|
||||
var events = data.map(function (item) {
|
||||
return new StoridgeEventModel(item);
|
||||
});
|
||||
deferred.resolve(events);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge events', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -1,81 +0,0 @@
|
|||
import { StoridgeDriveModel } from '../models/drive';
|
||||
|
||||
angular.module('portainer.integrations.storidge').factory('StoridgeDriveService', [
|
||||
'$q',
|
||||
'Storidge',
|
||||
function StoridgeDriveServiceFactory($q, Storidge) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.drives = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.queryDrives()
|
||||
.$promise.then(function success(data) {
|
||||
var driveData = data.drives;
|
||||
var drives = driveData.map(function (drive) {
|
||||
return new StoridgeDriveModel(drive);
|
||||
});
|
||||
|
||||
deferred.resolve(drives);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge drives', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.drive = function (id) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.getDrive({ id: id })
|
||||
.$promise.then(function success(data) {
|
||||
var drive = new StoridgeDriveModel(data);
|
||||
Storidge.getNode({ id: data.nodeid }).$promise.then(function (data) {
|
||||
drive.Node = data.name;
|
||||
deferred.resolve(drive);
|
||||
});
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge drive', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.add = function (device, node) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.addDrive({ device: device, node: node })
|
||||
.$promise.then(function success() {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to add Storidge drive', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.remove = function (id) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.removeDrive({ id: id })
|
||||
.$promise.then(function success() {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to remove Storidge drive', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.rescan = function () {
|
||||
return Storidge.rescanDrives().$promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -1,66 +0,0 @@
|
|||
import { StoridgeNodeModel, StoridgeNodeDetailedModel } from '../models/node';
|
||||
|
||||
angular.module('portainer.integrations.storidge').factory('StoridgeNodeService', [
|
||||
'$q',
|
||||
'Storidge',
|
||||
function StoridgeNodeServiceFactory($q, Storidge) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.nodes = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.queryNodes()
|
||||
.$promise.then(function success(data) {
|
||||
var nodeData = data.nodes;
|
||||
var nodes = [];
|
||||
|
||||
for (var key in nodeData) {
|
||||
if (Object.prototype.hasOwnProperty.call(nodeData, key)) {
|
||||
nodes.push(new StoridgeNodeModel(key, nodeData[key]));
|
||||
}
|
||||
}
|
||||
|
||||
deferred.resolve(nodes);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge nodes', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.node = function (id) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.getNode({ id: id })
|
||||
.$promise.then(function success(data) {
|
||||
var node = new StoridgeNodeDetailedModel(data.name, data.properties);
|
||||
deferred.resolve(node);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge node', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.add = function () {
|
||||
return Storidge.addNode().$promise;
|
||||
};
|
||||
|
||||
service.cordon = function (id) {
|
||||
return Storidge.cordonNode({ id: id }).$promise;
|
||||
};
|
||||
|
||||
service.uncordon = function (id) {
|
||||
return Storidge.uncordonNode({ id: id }).$promise;
|
||||
};
|
||||
|
||||
service.remove = function (id) {
|
||||
return Storidge.removeNode({ id: id }).$promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -1,58 +0,0 @@
|
|||
import { StoridgeCreateProfileRequest, StoridgeProfileListModel, StoridgeProfileModel } from '../models/profile';
|
||||
|
||||
angular.module('portainer.integrations.storidge').factory('StoridgeProfileService', [
|
||||
'$q',
|
||||
'Storidge',
|
||||
function StoridgeProfileServiceFactory($q, Storidge) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.create = function (model) {
|
||||
var payload = new StoridgeCreateProfileRequest(model);
|
||||
return Storidge.createProfile(payload).$promise;
|
||||
};
|
||||
|
||||
service.update = function (model) {
|
||||
var payload = new StoridgeCreateProfileRequest(model);
|
||||
return Storidge.updateProfile(payload).$promise;
|
||||
};
|
||||
|
||||
service.delete = function (profileName) {
|
||||
return Storidge.deleteProfile({ id: profileName }).$promise;
|
||||
};
|
||||
|
||||
service.profile = function (profileName) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.getProfile({ id: profileName })
|
||||
.$promise.then(function success(data) {
|
||||
var profile = new StoridgeProfileModel(profileName, data);
|
||||
deferred.resolve(profile);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge profile details', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.profiles = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.queryProfiles()
|
||||
.$promise.then(function success(data) {
|
||||
var profiles = data.profiles.map(function (item) {
|
||||
return new StoridgeProfileListModel(item);
|
||||
});
|
||||
deferred.resolve(profiles);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge profiles', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -1,83 +0,0 @@
|
|||
import { StoridgeSnapshotModel } from '../models/snapshot';
|
||||
|
||||
angular.module('portainer.integrations.storidge').factory('StoridgeSnapshotService', [
|
||||
'$q',
|
||||
'Storidge',
|
||||
function StoridgeSnapshotServiceFactory($q, Storidge) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.snapshots = snapshots;
|
||||
service.snapshot = snapshot;
|
||||
service.create = create;
|
||||
service.remove = remove;
|
||||
|
||||
function snapshots(volumeId) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.querySnapshots({ id: volumeId })
|
||||
.$promise.then(function success(data) {
|
||||
var snapshotsData = data.snapshots;
|
||||
let snapshotsArray = [];
|
||||
for (const key in snapshotsData) {
|
||||
if (Object.prototype.hasOwnProperty.call(snapshotsData, key)) {
|
||||
snapshotsArray.push(snapshotsData[key]);
|
||||
}
|
||||
}
|
||||
var snapshots = snapshotsArray.map(function (snapshot) {
|
||||
return new StoridgeSnapshotModel(snapshot);
|
||||
});
|
||||
deferred.resolve(snapshots);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge snapshots', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function snapshot(id) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.getSnapshot({ id: id })
|
||||
.$promise.then(function success(data) {
|
||||
var snapshot = new StoridgeSnapshotModel(data.snapshot);
|
||||
deferred.resolve(snapshot);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge snapshot', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function create(volumeId, description) {
|
||||
var deferred = $q.defer();
|
||||
Storidge.createSnapshot({ id: volumeId, opts: { description: description } })
|
||||
.$promise.then(function success(data) {
|
||||
deferred.resolve(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to create Storidge volume snapshot', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function remove(id) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.removeSnapshot({ id: id })
|
||||
.$promise.then(function success() {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to remove Storidge volume snapshot', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -1,41 +0,0 @@
|
|||
import { StoridgeVolumeModel, StoridgeVolumeUpdateModel } from '../models/volume';
|
||||
|
||||
angular.module('portainer.integrations.storidge').factory('StoridgeVolumeService', [
|
||||
'$q',
|
||||
'Storidge',
|
||||
function StoridgeVolumeServiceFactory($q, Storidge) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.volume = function (id) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Storidge.getVolume({ id: id })
|
||||
.$promise.then(function success(data) {
|
||||
var volume = new StoridgeVolumeModel(data);
|
||||
deferred.resolve(volume);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve Storidge volume', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.update = function (data) {
|
||||
var deferred = $q.defer();
|
||||
var volume = new StoridgeVolumeUpdateModel(data);
|
||||
Storidge.updateVolume(volume)
|
||||
.$promise.then(function success(data) {
|
||||
deferred.resolve(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to update Storidge volume', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -1,60 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Storidge cluster">
|
||||
<a data-toggle="tooltip" title="Refresh" ui-sref="storidge.cluster" ui-sref-opts="{reload: true}">
|
||||
<i class="fa fa-sync" aria-hidden="true"></i>
|
||||
</a>
|
||||
</rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="storidge.cluster">Storidge</a>
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-bolt" title-text="Cluster details"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Domain</td>
|
||||
<td>{{ clusterInfo.Domain }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Condition</td>
|
||||
<td
|
||||
><i class="fa fa-heartbeat space-right {{ clusterInfo.Condition | storidgeClusterConditionBadge }}"></i>
|
||||
{{ clusterInfo.Condition }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{{ clusterVersion }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<form class="form-horizontal">
|
||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="shutdownCluster()" ng-disabled="state.shutdownInProgress" button-spinner="state.shutdownInProgress">
|
||||
<span ng-hide="state.shutdownInProgress"><i class="fa fa-power-off space-right" aria-hidden="true"></i> Shutdown the cluster</span>
|
||||
<span ng-show="state.shutdownInProgress">Shutting down cluster...</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="rebootCluster()" ng-disabled="state.rebootInProgress" button-spinner="state.shutdownInProgress">
|
||||
<span ng-hide="state.rebootInProgress"><i class="fa fa-sync space-right" aria-hidden="true"></i> Reboot the cluster</span>
|
||||
<span ng-show="state.rebootInProgress">Rebooting cluster...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<storidge-nodes-datatable title-text="Storage nodes" title-icon="fa-object-group" dataset="clusterNodes" table-key="storidge_nodes" order-by="Name"></storidge-nodes-datatable>
|
||||
</div>
|
||||
</div>
|
|
@ -1,89 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').controller('StoridgeClusterController', [
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
'Notifications',
|
||||
'StoridgeClusterService',
|
||||
'StoridgeNodeService',
|
||||
'ModalService',
|
||||
function ($q, $scope, $state, Notifications, StoridgeClusterService, StoridgeNodeService, ModalService) {
|
||||
$scope.state = {
|
||||
shutdownInProgress: false,
|
||||
rebootInProgress: false,
|
||||
};
|
||||
|
||||
$scope.rebootCluster = function () {
|
||||
ModalService.confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'All the nodes in the cluster will reboot during the process. Do you want to reboot the Storidge cluster?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Reboot',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
rebootCluster();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function rebootCluster() {
|
||||
$scope.state.rebootInProgress = true;
|
||||
StoridgeClusterService.reboot().finally(function final() {
|
||||
$scope.state.rebootInProgress = false;
|
||||
Notifications.success('Cluster successfully rebooted');
|
||||
$state.reload();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.shutdownCluster = function () {
|
||||
ModalService.confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'All the nodes in the cluster will shutdown. Do you want to shutdown the Storidge cluster?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Shutdown',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
shutdownCluster();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function shutdownCluster() {
|
||||
$scope.state.shutdownInProgress = true;
|
||||
StoridgeClusterService.shutdown().finally(function final() {
|
||||
$scope.state.shutdownInProgress = false;
|
||||
Notifications.success('Cluster successfully shutdown');
|
||||
$state.go('docker.dashboard');
|
||||
});
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$q.all({
|
||||
info: StoridgeClusterService.info(),
|
||||
version: StoridgeClusterService.version(),
|
||||
nodes: StoridgeNodeService.nodes(),
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.clusterInfo = data.info;
|
||||
$scope.clusterVersion = data.version;
|
||||
$scope.clusterNodes = data.nodes;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve cluster information');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -1,24 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Storidge drives">
|
||||
<a data-toggle="tooltip" title="Refresh" ui-sref="storidge.drives" ui-sref-opts="{reload: true}">
|
||||
<i class="fa fa-sync" aria-hidden="true"></i>
|
||||
</a>
|
||||
</rd-header-title>
|
||||
<rd-header-content> <a ui-sref="storidge.cluster">Storidge</a> > <a ui-sref="storidge.drives">Drives</a> </rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<storidge-drives-datatable
|
||||
title-text="Drives"
|
||||
title-icon="fa-hdd"
|
||||
dataset="drives"
|
||||
table-key="storidge_drives"
|
||||
order-by="Id"
|
||||
rescan-action="rescanAction"
|
||||
add-action="addAction"
|
||||
action-in-progress="state.actionInProgress"
|
||||
addition-in-progress="state.additionInProgress"
|
||||
></storidge-drives-datatable>
|
||||
</div>
|
||||
</div>
|
|
@ -1,51 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').controller('StoridgeDrivesController', [
|
||||
'$scope',
|
||||
'$state',
|
||||
'Notifications',
|
||||
'StoridgeDriveService',
|
||||
function ($scope, $state, Notifications, StoridgeDriveService) {
|
||||
$scope.state = {
|
||||
additionInProgress: [],
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
$scope.addAction = function (drive, idx) {
|
||||
$scope.state.additionInProgress[idx] = true;
|
||||
$scope.state.actionInProgress = true;
|
||||
StoridgeDriveService.add(drive.Device, drive.Node)
|
||||
.then(function success() {
|
||||
Notifications.success('Drive ' + drive.Device + ' successfully added on node ' + drive.Node);
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to add drive');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.additionInProgress[idx] = false;
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.rescanAction = function () {
|
||||
StoridgeDriveService.rescan()
|
||||
.then(function sucess() {
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to scan drives');
|
||||
});
|
||||
};
|
||||
|
||||
function initView() {
|
||||
StoridgeDriveService.drives()
|
||||
.then(function success(data) {
|
||||
$scope.drives = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve drives');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -1,56 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Drive details"></rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="storidge.cluster">Storidge</a> > <a ui-sref="storidge.drives">Drives</a> > <a ui-sref="storidge.drives.drive({id: drive.Id})">{{ drive.Id }}</a>
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-hdd" title-text="Drive details "></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>
|
||||
{{ drive.Id }}
|
||||
<button class="btn btn-xs btn-danger" ng-click="removeDrive()" ng-if="drive.Status === 'faulty'" button-spinner="actionInProgress" ng-disabled="actionInProgress">
|
||||
<span ng-hide="actionInProgress"><i class="fa fa-trash space-right" aria-hidden="true"></i>Remove from storage pool</span>
|
||||
<span ng-show="actionInProgress">Removal in progress...</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Node</td>
|
||||
<td>{{ drive.Node }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Device</td>
|
||||
<td>{{ drive.Device }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td>{{ drive.Size }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Use</td>
|
||||
<td>{{ drive.Use }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Type</td>
|
||||
<td>{{ drive.Type }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td>
|
||||
<span class="label label-{{ drive.Status | drivestatusbadge }}">{{ drive.Status | capitalize }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
|
@ -1,55 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').controller('StoridgeDriveController', [
|
||||
'$scope',
|
||||
'$state',
|
||||
'$transition$',
|
||||
'Notifications',
|
||||
'ModalService',
|
||||
'StoridgeDriveService',
|
||||
function ($scope, $state, $transition$, Notifications, ModalService, StoridgeDriveService) {
|
||||
$scope.actionInProgress = false;
|
||||
|
||||
$scope.removeDrive = function () {
|
||||
ModalService.confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'Do you want really want to remove this drive from the storage pool?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
$scope.actionInProgress = true;
|
||||
StoridgeDriveService.remove($scope.drive.Id)
|
||||
.then(function () {
|
||||
Notifications.success('Success', 'Drive removed from storage pool');
|
||||
$state.go('storidge.drives', {}, { reload: true });
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to remove drive from storage pool');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.actionInProgress = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function initView() {
|
||||
$scope.id = $transition$.params().id;
|
||||
|
||||
StoridgeDriveService.drive($scope.id)
|
||||
.then(function success(data) {
|
||||
$scope.drive = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve drive details');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -1,202 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Storidge monitor">
|
||||
<a data-toggle="tooltip" title="Refresh" ui-sref="storidge.monitor" ui-sref-opts="{reload: true}">
|
||||
<i class="fa fa-sync" aria-hidden="true"></i>
|
||||
</a>
|
||||
</rd-header-title>
|
||||
<rd-header-content> <a ui-sref="storidge.cluster">Storidge</a> > <a ui-sref="storidge.monitor">Cluster monitoring</a> </rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-tachometer-alt" title-text="Cluster capacity"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<div class="chart-container" style="position: relative">
|
||||
<canvas id="capacityChart" width="770" height="400"></canvas>
|
||||
</div>
|
||||
<div style="margin-top: 10px" ng-if="info">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Capacity available</td>
|
||||
<td>{{ ((info.FreeCapacity * 100) / info.TotalCapacity).toFixed(1) }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Provisioned capacity</td>
|
||||
<td>
|
||||
{{ info.ProvisionedCapacity | humansize }}
|
||||
<span ng-if="+info.ProvisionedCapacity >= +info.TotalCapacity">
|
||||
<i class="fa fa-exclamation-triangle red-icon" aria-hidden="true" style="margin-left: 2px"></i>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total capacity</td>
|
||||
<td>{{ info.TotalCapacity | humansize }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-chart-area" title-text="IOPS usage"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<div class="chart-container" style="position: relative">
|
||||
<canvas id="iopsChart" width="770" height="400"></canvas>
|
||||
</div>
|
||||
<div style="margin-top: 10px" ng-if="info">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>IOPS available</td>
|
||||
<td>{{ ((info.FreeIOPS * 100) / info.TotalIOPS).toFixed(1) }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Provisioned IOPS</td>
|
||||
<td>
|
||||
{{ info.ProvisionedIOPS | number }}
|
||||
<span ng-if="+info.ProvisionedIOPS >= +info.TotalIOPS">
|
||||
<i class="fa fa-exclamation-triangle red-icon" aria-hidden="true" style="margin-left: 2px"></i>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total IOPS</td>
|
||||
<td>{{ info.TotalIOPS | number }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-chart-area" title-text="Bandwith usage"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<div class="chart-container" style="position: relative">
|
||||
<canvas id="bandwithChart" width="770" height="400"></canvas>
|
||||
</div>
|
||||
<div style="margin-top: 10px" ng-if="info">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Bandwidth available</td>
|
||||
<td>{{ ((info.FreeBandwidth * 100) / info.TotalBandwidth).toFixed(1) }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Provisioned bandwidth</td>
|
||||
<td>
|
||||
{{ info.ProvisionedBandwidth | humansize }}
|
||||
<span ng-if="+info.ProvisionedBandwidth >= +info.TotalBandwidth">
|
||||
<i class="fa fa-exclamation-triangle red-icon" aria-hidden="true" style="margin-left: 2px"></i>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total bandwidth</td>
|
||||
<td>{{ info.TotalBandwidth | humansize }} /s</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<storidge-cluster-events-datatable
|
||||
title-text="Cluster events"
|
||||
title-icon="fa-history"
|
||||
dataset="events"
|
||||
table-key="storidge_cluster_events"
|
||||
order-by="Time"
|
||||
reverse-order="true"
|
||||
></storidge-cluster-events-datatable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="row">
|
||||
<div class="col-md-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-history" title-text="Cluster events">
|
||||
<div class="pull-right">
|
||||
Items per page:
|
||||
<select ng-model="state.pagination_count" ng-change="changePaginationCount()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-taskbar classes="col-lg-12">
|
||||
<div class="pull-right">
|
||||
<input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ng-click="order('Time')">
|
||||
Date
|
||||
<span ng-show="sortType == 'Time' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Time' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="order('Category')">
|
||||
Category
|
||||
<span ng-show="sortType == 'Category' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Category' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="order('Module')">
|
||||
Module
|
||||
<span ng-show="sortType == 'Module' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Module' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="order('Content')">
|
||||
Content
|
||||
<span ng-show="sortType == 'Content' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Content' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="event in (events | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count)">
|
||||
<td>{{ event.Time }}</td>
|
||||
<td>{{ event.Category }}</td>
|
||||
<td>{{ event.Module }}</td>
|
||||
<td>{{ event.Content }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!events">
|
||||
<td colspan="4" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="events.length === 0">
|
||||
<td colspan="4" class="text-center text-muted">No events available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="events" class="pull-left pagination-controls">
|
||||
<dir-pagination-controls></dir-pagination-controls>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div> -->
|
|
@ -1,109 +0,0 @@
|
|||
import moment from 'moment';
|
||||
|
||||
angular.module('portainer.integrations.storidge').controller('StoridgeMonitorController', [
|
||||
'$q',
|
||||
'$scope',
|
||||
'$interval',
|
||||
'$document',
|
||||
'Notifications',
|
||||
'StoridgeClusterService',
|
||||
'StoridgeChartService',
|
||||
function ($q, $scope, $interval, $document, Notifications, StoridgeClusterService, StoridgeChartService) {
|
||||
$scope.$on('$destroy', function () {
|
||||
stopRepeater();
|
||||
});
|
||||
|
||||
function stopRepeater() {
|
||||
var repeater = $scope.repeater;
|
||||
if (angular.isDefined(repeater)) {
|
||||
$interval.cancel(repeater);
|
||||
repeater = null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateIOPSChart(info, chart) {
|
||||
var usedIOPS = info.UsedIOPS;
|
||||
var label = moment(new Date()).format('HH:mm:ss');
|
||||
|
||||
StoridgeChartService.UpdateChart(label, usedIOPS, chart);
|
||||
}
|
||||
|
||||
function updateBandwithChart(info, chart) {
|
||||
var usedBandwidth = info.UsedBandwidth;
|
||||
var label = moment(new Date()).format('HH:mm:ss');
|
||||
|
||||
StoridgeChartService.UpdateChart(label, usedBandwidth, chart);
|
||||
}
|
||||
|
||||
function updateCapacityChart(info, chart) {
|
||||
var usedCapacity = info.UsedCapacity;
|
||||
var freeCapacity = info.FreeCapacity;
|
||||
|
||||
StoridgeChartService.UpdatePieChart('Free', freeCapacity, chart);
|
||||
StoridgeChartService.UpdatePieChart('Used', usedCapacity, chart);
|
||||
}
|
||||
|
||||
function setUpdateRepeater(iopsChart, bandwidthChart, capacityChart) {
|
||||
var refreshRate = 5000;
|
||||
$scope.repeater = $interval(function () {
|
||||
$q.all({
|
||||
events: StoridgeClusterService.events(),
|
||||
info: StoridgeClusterService.info(),
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.events = data.events;
|
||||
var info = data.info;
|
||||
$scope.info = info;
|
||||
updateIOPSChart(info, iopsChart);
|
||||
updateBandwithChart(info, bandwidthChart);
|
||||
updateCapacityChart(info, capacityChart);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
stopRepeater();
|
||||
Notifications.error('Failure', err, 'Unable to retrieve cluster information');
|
||||
});
|
||||
}, refreshRate);
|
||||
}
|
||||
|
||||
function startViewUpdate(iopsChart, bandwidthChart, capacityChart) {
|
||||
$q.all({
|
||||
events: StoridgeClusterService.events(),
|
||||
info: StoridgeClusterService.info(),
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.events = data.events;
|
||||
var info = data.info;
|
||||
$scope.info = info;
|
||||
updateIOPSChart(info, iopsChart);
|
||||
updateBandwithChart(info, bandwidthChart);
|
||||
updateCapacityChart(info, capacityChart);
|
||||
setUpdateRepeater(iopsChart, bandwidthChart, capacityChart);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
stopRepeater();
|
||||
Notifications.error('Failure', err, 'Unable to retrieve cluster information');
|
||||
});
|
||||
}
|
||||
|
||||
function initCharts() {
|
||||
var iopsChartCtx = $('#iopsChart');
|
||||
var iopsChart = StoridgeChartService.CreateIOPSChart(iopsChartCtx);
|
||||
|
||||
var bandwidthChartCtx = $('#bandwithChart');
|
||||
var bandwidthChart = StoridgeChartService.CreateBandwidthChart(bandwidthChartCtx);
|
||||
|
||||
var capacityChartCtx = $('#capacityChart');
|
||||
var capacityChart = StoridgeChartService.CreateCapacityChart(capacityChartCtx);
|
||||
|
||||
startViewUpdate(iopsChart, bandwidthChart, capacityChart);
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$document.ready(function () {
|
||||
initCharts();
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -1,166 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Node details"></rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="storidge.cluster">Storidge</a> > <a ui-sref="storidge.cluster.node({id: node.Name})">{{ node.Name }}</a>
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-object-group" title-text="Node details">
|
||||
<button type="button" class="btn btn-sm btn-danger" ng-disabled="(node.Status | lowercase) !== 'normal'" ng-click="removeNodeAction()">
|
||||
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove node
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" ng-disabled="(node.Status | lowercase) !== 'normal'" ng-click="cordonNodeAction()">
|
||||
<i class="fas fa-wrench space-right" aria-hidden="true"></i>Enter maintenance mode
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" ng-disabled="(node.Status | lowercase) !== 'cordoned'" ng-click="uncordonNodeAction()">
|
||||
<i class="fa fa-power-off space-right" aria-hidden="true"></i>Exit maintenance mode
|
||||
</button>
|
||||
</rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table description-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ node.Name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Domain</td>
|
||||
<td>{{ node.Domain }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Domain ID</td>
|
||||
<td>{{ node.DomainID }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Node status</td>
|
||||
<td>
|
||||
<i class="fa fa-heartbeat space-right {{ node.Status | storidgeNodeStatusBadge }}"></i>
|
||||
{{ node.Status }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Operating condition</td>
|
||||
<td>
|
||||
<i class="fa fa-heartbeat space-right {{ node.Condition | storidgeClusterConditionBadge }}"></i>
|
||||
{{ node.Condition }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Metadata version</td>
|
||||
<td>{{ node.MetadataVersion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nodes</td>
|
||||
<td>{{ node.Nodes }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HDDs</td>
|
||||
<td>{{ node.Hdds }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSDs</td>
|
||||
<td>{{ node.Ssds }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>VDisks</td>
|
||||
<td>{{ node.Vdisks }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-object-group" title-text="Bandwidth details"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table description-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Free</td>
|
||||
<td>{{ node.FreeBandwidth }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Used</td>
|
||||
<td>{{ node.UsedBandwidth }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Provisioned</td>
|
||||
<td>{{ node.ProvisionedBandwidth }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td>{{ node.TotalBandwidth }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-object-group" title-text="Capacity details"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table description-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Free</td>
|
||||
<td>{{ node.FreeCapacity | bytes }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Used</td>
|
||||
<td>{{ node.UsedCapacity | bytes }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Provisioned</td>
|
||||
<td>{{ node.ProvisionedCapacity | bytes }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td>{{ node.TotalCapacity | bytes }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-object-group" title-text="IOPS details"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table description-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Free</td>
|
||||
<td>{{ node.FreeIOPS }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Used</td>
|
||||
<td>{{ node.UsedIOPS }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Provisioned</td>
|
||||
<td>{{ node.ProvisionedIOPS }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td>{{ node.TotalIOPS }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
|
@ -1,117 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').controller('StoridgeNodeController', [
|
||||
'$scope',
|
||||
'$state',
|
||||
'$transition$',
|
||||
'Notifications',
|
||||
'StoridgeNodeService',
|
||||
'ModalService',
|
||||
function ($scope, $state, $transition$, Notifications, StoridgeNodeService, ModalService) {
|
||||
$scope.removeNodeAction = function (selectedItems) {
|
||||
ModalService.confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'Do you want really want to remove the node from the cluster?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
remove(selectedItems);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function remove() {
|
||||
StoridgeNodeService.remove($scope.node.Name)
|
||||
.then(function success() {
|
||||
Notifications.success('Node successfully removed', $scope.node.Name);
|
||||
$state.go('storidge.cluster');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to remove node');
|
||||
});
|
||||
}
|
||||
|
||||
$scope.cordonNodeAction = function (selectedItems) {
|
||||
ModalService.confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'Do you want really want to put the node in maintenance mode?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Enter maintenance',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
cordonNode(selectedItems);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function cordonNode() {
|
||||
StoridgeNodeService.cordon($scope.node.Name)
|
||||
.then(function success() {
|
||||
Notifications.success('Node successfully put in maintenance');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to put node in maintenance mode');
|
||||
})
|
||||
.finally(function final() {
|
||||
$state.reload();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.uncordonNodeAction = function (selectedItems) {
|
||||
ModalService.confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'Do you want really want to bring the nodes out of maintenance mode?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Exit maintenance',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
uncordonNode(selectedItems);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function uncordonNode() {
|
||||
StoridgeNodeService.uncordon($scope.node.Name)
|
||||
.then(function success() {
|
||||
Notifications.success('Node successfully bringed back');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to put node out of maintenance mode');
|
||||
})
|
||||
.finally(function final() {
|
||||
$state.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$scope.name = $transition$.params().name;
|
||||
|
||||
StoridgeNodeService.node($scope.name)
|
||||
.then(function success(data) {
|
||||
$scope.node = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve node details');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -1,112 +0,0 @@
|
|||
import _ from 'lodash-es';
|
||||
import { StoridgeProfileDefaultModel } from '../../../models/profile';
|
||||
|
||||
angular.module('portainer.integrations.storidge').controller('StoridgeCreateProfileController', [
|
||||
'$scope',
|
||||
'$state',
|
||||
'$transition$',
|
||||
'Notifications',
|
||||
'StoridgeProfileService',
|
||||
function ($scope, $state, $transition$, Notifications, StoridgeProfileService) {
|
||||
$scope.formValues = {
|
||||
Labels: [],
|
||||
};
|
||||
|
||||
$scope.state = {
|
||||
NoLimit: true,
|
||||
LimitIOPS: false,
|
||||
LimitBandwidth: false,
|
||||
ManualInputDirectory: false,
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
$scope.RedundancyOptions = [
|
||||
{ value: 2, label: '2-copy' },
|
||||
{ value: 3, label: '3-copy' },
|
||||
];
|
||||
|
||||
$scope.addLabel = function () {
|
||||
$scope.formValues.Labels.push({ name: '', value: '' });
|
||||
};
|
||||
|
||||
$scope.removeLabel = function (index) {
|
||||
$scope.formValues.Labels.splice(index, 1);
|
||||
};
|
||||
|
||||
function prepareLabels(profile) {
|
||||
var labels = {};
|
||||
$scope.formValues.Labels.forEach(function (label) {
|
||||
if (label.name && label.value) {
|
||||
labels[label.name] = label.value;
|
||||
}
|
||||
});
|
||||
profile.Labels = labels;
|
||||
}
|
||||
|
||||
$scope.create = function () {
|
||||
var profile = $scope.model;
|
||||
|
||||
if (!$scope.state.LimitIOPS) {
|
||||
delete profile.MinIOPS;
|
||||
delete profile.MaxIOPS;
|
||||
}
|
||||
|
||||
if (!$scope.state.LimitBandwidth) {
|
||||
delete profile.MinBandwidth;
|
||||
delete profile.MaxBandwidth;
|
||||
}
|
||||
|
||||
if (profile.SnapshotEnabled) {
|
||||
if (!profile.SnapshotMax || profile.SnapshotMax <= 0) {
|
||||
profile.SnapshotMax = 1;
|
||||
}
|
||||
if (!$scope.state.RecurringSnapshotEnabled) {
|
||||
delete profile.SnapshotInterval;
|
||||
}
|
||||
if ($scope.state.RecurringSnapshotEnabled && (!profile.SnapshotInterval || profile.SnapshotInterval <= 0)) {
|
||||
profile.SnapshotInterval = 1440;
|
||||
}
|
||||
} else {
|
||||
delete profile.SnapshotMax;
|
||||
delete profile.SnapshotInterval;
|
||||
}
|
||||
|
||||
prepareLabels(profile);
|
||||
|
||||
$scope.state.actionInProgress = true;
|
||||
StoridgeProfileService.create(profile)
|
||||
.then(function success() {
|
||||
Notifications.success('Profile successfully created');
|
||||
$state.go('storidge.profiles');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to create profile');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.updatedName = function () {
|
||||
if (!$scope.state.ManualInputDirectory) {
|
||||
var profile = $scope.model;
|
||||
profile.Directory = '/cio/' + (profile.Name ? _.toLower(profile.Name) : '');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.updatedDirectory = function () {
|
||||
if (!$scope.state.ManualInputDirectory) {
|
||||
$scope.state.ManualInputDirectory = true;
|
||||
}
|
||||
};
|
||||
|
||||
function initView() {
|
||||
var profile = new StoridgeProfileDefaultModel();
|
||||
profile.Name = $transition$.params().profileName;
|
||||
profile.Directory = profile.Directory + _.toLower(profile.Name);
|
||||
$scope.model = profile;
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -1,323 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Create profile"></rd-header-title>
|
||||
<rd-header-content> <a ui-sref="storidge.cluster">Storidge</a> > <a ui-sref="storidge.profiles">Profiles</a> > Add profile </rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" name="storidgeCreateProfileForm">
|
||||
<!-- name-input -->
|
||||
<div class="form-group" ng-class="{ 'has-error': storidgeCreateProfileForm.profile_name.$invalid }">
|
||||
<label for="profile_name" class="col-sm-2 col-lg-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="model.Name" name="profile_name" placeholder="e.g. myProfile" ng-change="updatedName()" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeCreateProfileForm.profile_name.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeCreateProfileForm.profile_name.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<div class="col-sm-12 form-section-title"> Profile configuration </div>
|
||||
<!-- directory -->
|
||||
<div class="form-group" ng-class="{ 'has-error': storidgeCreateProfileForm.profile_directory.$invalid }">
|
||||
<label for="profile_directory" class="col-sm-2 col-lg-1 control-label text-left">Directory</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="model.Directory"
|
||||
name="profile_directory"
|
||||
placeholder="e.g. /cio/myProfile"
|
||||
ng-change="updatedDirectory()"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeCreateProfileForm.profile_directory.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeCreateProfileForm.profile_directory.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !directory -->
|
||||
<!-- capacity -->
|
||||
<div class="form-group" ng-class="{ 'has-error': storidgeCreateProfileForm.profile_capacity.$invalid }">
|
||||
<label for="profile_capacity" class="col-sm-2 col-lg-1 control-label text-left">Capacity</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="number" class="form-control" ng-model="model.Capacity" name="profile_capacity" ng-min="1" ng-max="64000" placeholder="2" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeCreateProfileForm.profile_capacity.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeCreateProfileForm.profile_capacity.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for capacity: 1.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for capacity: 64000.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !capacity -->
|
||||
<!-- redundancy -->
|
||||
<div class="form-group">
|
||||
<label for="profile_redundancy" class="col-sm-2 col-lg-1 control-label text-left">Redundancy</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_redundancy" ng-model="model.Redundancy" ng-options="+(opt.value) as opt.label for opt in RedundancyOptions" class="form-control"> </select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !redudancy -->
|
||||
<!-- provisioning -->
|
||||
<div class="form-group">
|
||||
<label for="profile_provisioning" class="col-sm-2 col-lg-1 control-label text-left">Provisioning</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_provisioning" ng-model="model.Provisioning" class="form-control">
|
||||
<option value="thin">Thin</option>
|
||||
<option value="thick">Thick</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !provisioning -->
|
||||
<!-- type -->
|
||||
<div class="form-group">
|
||||
<label for="profile_type" class="col-sm-2 col-lg-1 control-label text-left">Type</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_type" ng-model="model.Type" class="form-control">
|
||||
<option value="ssd">SSD</option>
|
||||
<option value="hdd">HDD</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !type -->
|
||||
<!-- Filesystem -->
|
||||
<div class="form-group">
|
||||
<label for="profile_filesystem" class="col-sm-2 col-lg-1 control-label text-left">Filesystem</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_filesystem" ng-model="model.Filesystem" class="form-control">
|
||||
<option value="btrfs">btrfs</option>
|
||||
<option value="ext4">ext4</option>
|
||||
<option value="xfs">xfs</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Filesystem -->
|
||||
<!-- snapshotEnabled -->
|
||||
<div class="form-group">
|
||||
<label for="profile_snapshotEnabled" class="col-sm-2 col-lg-1 control-label text-left"> Enable snapshots </label>
|
||||
<label class="switch" style="margin-left: 20px"> <input name="profile_snapshotEnabled" type="checkbox" ng-model="model.SnapshotEnabled" /><i></i> </label>
|
||||
</div>
|
||||
<!-- !snapshotEnabled -->
|
||||
<!-- snapshotMax -->
|
||||
<div class="form-group" ng-if="model.SnapshotEnabled">
|
||||
<label for="profile_snapshotMax" class="col-sm-2 col-lg-1 control-label text-left" style="margin-top: 20px"> Snapshot max </label>
|
||||
<div class="col-sm-4">
|
||||
<slider model="model.SnapshotMax" floor="1" ceil="100" step="1"></slider>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="1" class="form-control" ng-model="model.SnapshotMax" id="profile_snapshotMax" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<p class="small text-muted" style="margin-top: 7px"> Snapshot max (<b>count</b>) </p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !snapshotMax -->
|
||||
<!-- snapshotEnabled -->
|
||||
<div class="form-group" ng-if="model.SnapshotEnabled">
|
||||
<label for="profile_recurringSnapshotEnabled" class="col-sm-2 col-lg-1 control-label text-left"> Enable periodic snapshots </label>
|
||||
<label class="switch" style="margin-left: 20px">
|
||||
<input name="profile_recurringSnapshotEnabled" type="checkbox" ng-model="state.RecurringSnapshotEnabled" /><i></i>
|
||||
</label>
|
||||
</div>
|
||||
<!-- !snapshotEnabled -->
|
||||
<!-- snapshotInterval -->
|
||||
<div class="form-group" ng-if="model.SnapshotEnabled && state.RecurringSnapshotEnabled">
|
||||
<label for="profile_snapshotInterval" class="col-sm-2 col-lg-1 control-label text-left" style="margin-top: 20px"> Snapshot interval </label>
|
||||
<div class="col-sm-4">
|
||||
<slider model="model.SnapshotInterval" floor="1" ceil="2880" step="1"></slider>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="1" class="form-control" ng-model="model.SnapshotInterval" id="profile_snapshotInterval" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<p class="small text-muted" style="margin-top: 7px"> Snapshot interval (<b>minutes</b>) </p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !snapshotInterval -->
|
||||
<!-- encryptionEnabled -->
|
||||
<div class="form-group">
|
||||
<label for="profile_encryptionEnabled" class="col-sm-2 col-lg-1 control-label text-left"> Enable encryption </label>
|
||||
<label class="switch" style="margin-left: 20px"> <input name="profile_encryptionEnabled" type="checkbox" ng-model="model.EncryptionEnabled" /><i></i> </label>
|
||||
</div>
|
||||
<!-- !encryptionEnabled -->
|
||||
<!-- interfaceType -->
|
||||
<div class="form-group">
|
||||
<label for="profile_interfaceType" class="col-sm-2 col-lg-1 control-label text-left">Interface type</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_interfaceType" ng-model="model.InterfaceType" class="form-control">
|
||||
<option value=""></option>
|
||||
<option value="nfs">nfs</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !interfaceType -->
|
||||
<!-- interfaceDriver -->
|
||||
<div class="form-group">
|
||||
<label for="profile_interfaceDriver" class="col-sm-2 col-lg-1 control-label text-left">Network driver</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_interfaceDriver" ng-model="model.InterfaceDriver" class="form-control">
|
||||
<option value=""></option>
|
||||
<option value="macvlan">macvlan</option>
|
||||
<option value="overlay">overlay</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !interfaceDriver -->
|
||||
<!-- interfaceNetwork -->
|
||||
<div class="form-group">
|
||||
<label for="profile_interfaceNetwork" class="col-sm-2 col-lg-1 control-label text-left">Network name</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="model.InterfaceNetwork" name="profile_interfaceNetwork" pattern="[a-zA-Z0-9]+" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !interfaceNetwork -->
|
||||
<!-- interfaceConf -->
|
||||
<div class="form-group">
|
||||
<label for="profile_interfaceConf" class="col-sm-2 col-lg-1 control-label text-left">Interface conf</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="model.InterfaceConf" name="profile_interfaceConf" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !interfaceConf -->
|
||||
<!-- labels -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12" style="margin-top: 5px">
|
||||
<label class="control-label text-left">Labels</label>
|
||||
<span class="label label-default interactive" style="margin-left: 10px" ng-click="addLabel()"> <i class="fa fa-plus-circle" aria-hidden="true"></i> add label </span>
|
||||
</div>
|
||||
<!-- labels-input-list -->
|
||||
<div class="col-sm-12 form-inline" style="margin-top: 10px">
|
||||
<div ng-repeat="label in formValues.Labels" style="margin-top: 2px">
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">name</span>
|
||||
<input type="text" class="form-control" ng-model="label.name" placeholder="e.g. com.example.foo" />
|
||||
</div>
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">value</span>
|
||||
<input type="text" class="form-control" ng-model="label.value" placeholder="e.g. bar" />
|
||||
</div>
|
||||
<button class="btn btn-sm btn-danger" type="button" ng-click="removeLabel($index)">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !labels-input-list -->
|
||||
</div>
|
||||
<!-- !labels -->
|
||||
<!-- iops -->
|
||||
<div ng-if="!state.LimitBandwidth || state.NoLimit">
|
||||
<div class="col-sm-12 form-section-title"> IOPS </div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-1 col-sm-2">
|
||||
<label for="permissions" class="control-label text-left"> Limit IOPS </label>
|
||||
</div>
|
||||
<label class="switch" style="margin-left: 20px">
|
||||
<input type="checkbox" ng-model="state.LimitIOPS" ng-change="state.NoLimit = (!state.LimitBandwidth && !state.LimitIOPS)" /><i></i>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" ng-if="state.LimitIOPS">
|
||||
<label for="min_iops" class="col-sm-1 control-label text-left">Min</label>
|
||||
<div class="col-sm-5" ng-class="{ 'has-error': storidgeCreateProfileForm.min_iops.$invalid }">
|
||||
<input type="number" class="form-control" ng-model="model.MinIOPS" name="min_iops" ng-min="30" ng-max="999999" placeholder="100" required />
|
||||
</div>
|
||||
<label for="max_iops" class="col-sm-1 control-label text-left">Max</label>
|
||||
<div class="col-sm-5" ng-class="{ 'has-error': storidgeCreateProfileForm.max_iops.$invalid }">
|
||||
<input type="number" class="form-control" ng-model="model.MaxIOPS" name="max_iops" ng-min="30" ng-max="999999" placeholder="2000" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeCreateProfileForm.min_iops.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeCreateProfileForm.min_iops.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A value is required for Min IOPS.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for Min IOPS: 30.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for Min IOPS: 999999.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeCreateProfileForm.max_iops.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeCreateProfileForm.max_iops.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A value is required for Max IOPS.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for Max IOPS: 30.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for Max IOPS: 999999.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !iops -->
|
||||
<!-- bandwidth -->
|
||||
<div ng-if="!state.LimitIOPS || state.NoLimit">
|
||||
<div class="col-sm-12 form-section-title"> Bandwidth </div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-1 col-sm-2">
|
||||
<label for="permissions" class="control-label text-left"> Limit bandwidth </label>
|
||||
</div>
|
||||
<label class="switch" style="margin-left: 20px">
|
||||
<input type="checkbox" ng-model="state.LimitBandwidth" ng-change="state.NoLimit = (!state.LimitBandwidth && !state.LimitIOPS)" /><i></i>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" ng-if="state.LimitBandwidth">
|
||||
<label for="min_bandwidth" class="col-sm-1 control-label text-left">Min</label>
|
||||
<div class="col-sm-5" ng-class="{ 'has-error': storidgeCreateProfileForm.min_bandwidth.$invalid }">
|
||||
<input type="number" class="form-control" ng-model="model.MinBandwidth" name="min_bandwidth" ng-min="1" ng-max="5000" placeholder="1" required />
|
||||
</div>
|
||||
<label for="max_bandwidth" class="col-sm-1 control-label text-left">Max</label>
|
||||
<div class="col-sm-5" ng-class="{ 'has-error': storidgeCreateProfileForm.max_bandwidth.$invalid }">
|
||||
<input type="number" class="form-control" ng-model="model.MaxBandwidth" name="max_bandwidth" ng-min="1" ng-max="5000" placeholder="100" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeCreateProfileForm.min_bandwidth.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeCreateProfileForm.min_bandwidth.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A value is required for Min bandwidth.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for Min bandwidth: 1.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for Min bandwidth: 5000.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeCreateProfileForm.max_bandwidth.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeCreateProfileForm.max_bandwidth.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A value is required for Max bandwidth.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for Max bandwidth: 1.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for Max bandwidth: 5000.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !bandwidth -->
|
||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-click="create()"
|
||||
ng-disabled="state.actionInProgress || !storidgeCreateProfileForm.$valid"
|
||||
button-spinner="state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress">Create the profile</span>
|
||||
<span ng-show="state.actionInProgress">Creating profile...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
|
@ -1,313 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Profile details"></rd-header-title>
|
||||
<rd-header-content> <a ui-sref="storidge.cluster">Storidge</a> > <a ui-sref="storidge.profiles">Profiles</a> > {{ profile.Name }} </rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row" ng-if="profile">
|
||||
<div class="col-md-12">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" name="storidgeUpdateProfileForm">
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="profile_name" class="col-sm-2 col-lg-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="profile.Name" name="profile_name" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<div class="col-sm-12 form-section-title"> Profile configuration </div>
|
||||
<!-- directory -->
|
||||
<div class="form-group" ng-class="{ 'has-error': storidgeUpdateProfileForm.profile_directory.$invalid }">
|
||||
<label for="profile_directory" class="col-sm-2 col-lg-1 control-label text-left">Directory</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="profile.Directory" name="profile_directory" placeholder="e.g. /cio/myProfile" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeUpdateProfileForm.profile_directory.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeUpdateProfileForm.profile_directory.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !directory -->
|
||||
<!-- capacity -->
|
||||
<div class="form-group" ng-class="{ 'has-error': storidgeUpdateProfileForm.profile_capacity.$invalid }">
|
||||
<label for="profile_capacity" class="col-sm-2 col-lg-1 control-label text-left">Capacity</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="number" class="form-control" ng-model="profile.Capacity" name="profile_capacity" ng-min="1" ng-max="64000" placeholder="2" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeUpdateProfileForm.profile_capacity.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeUpdateProfileForm.profile_capacity.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for capacity: 1.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for capacity: 64000.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !capacity -->
|
||||
<!-- redundancy -->
|
||||
<div class="form-group">
|
||||
<label for="profile_redundancy" class="col-sm-2 col-lg-1 control-label text-left">Redundancy</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_redundancy" ng-model="profile.Redundancy" ng-options="+(opt.value) as opt.label for opt in RedundancyOptions" class="form-control"> </select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !redudancy -->
|
||||
<!-- provisioning -->
|
||||
<div class="form-group">
|
||||
<label for="profile_provisioning" class="col-sm-2 col-lg-1 control-label text-left">Provisioning</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_provisioning" ng-model="profile.Provisioning" class="form-control">
|
||||
<option value="thin">Thin</option>
|
||||
<option value="thick">Thick</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !provisioning -->
|
||||
<!-- type -->
|
||||
<div class="form-group">
|
||||
<label for="profile_type" class="col-sm-2 col-lg-1 control-label text-left">Type</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_type" ng-model="profile.Type" class="form-control">
|
||||
<option value="ssd">SSD</option>
|
||||
<option value="hdd">HDD</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !type -->
|
||||
<!-- Filesystem -->
|
||||
<div class="form-group">
|
||||
<label for="profile_filesystem" class="col-sm-2 col-lg-1 control-label text-left">Filesystem</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_filesystem" ng-model="profile.Filesystem" class="form-control">
|
||||
<option value="btrfs">btrfs</option>
|
||||
<option value="ext4">ext4</option>
|
||||
<option value="xfs">xfs</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Filesystem -->
|
||||
<!-- snapshotEnabled -->
|
||||
<div class="form-group">
|
||||
<label for="profile_snapshotEnabled" class="col-sm-2 col-lg-1 control-label text-left"> Enable snapshots </label>
|
||||
<label class="switch" style="margin-left: 20px"> <input name="profile_snapshotEnabled" type="checkbox" ng-model="profile.SnapshotEnabled" /><i></i> </label>
|
||||
</div>
|
||||
<!-- !snapshotEnabled -->
|
||||
<!-- snapshotMax -->
|
||||
<div class="form-group" ng-if="profile.SnapshotEnabled">
|
||||
<label for="profile_snapshotMax" class="col-sm-2 col-lg-1 control-label text-left" style="margin-top: 20px"> Snapshot max </label>
|
||||
<div class="col-sm-4">
|
||||
<slider model="profile.SnapshotMax" floor="1" ceil="100" step="1"></slider>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="1" class="form-control" ng-model="profile.SnapshotMax" id="profile_snapshotMax" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<p class="small text-muted" style="margin-top: 7px"> Snapshot max (<b>count</b>) </p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !snapshotMax -->
|
||||
<!-- snapshotEnabled -->
|
||||
<div class="form-group" ng-if="profile.SnapshotEnabled">
|
||||
<label for="profile_recurringSnapshotEnabled" class="col-sm-2 col-lg-1 control-label text-left"> Enable periodic snapshots </label>
|
||||
<label class="switch" style="margin-left: 20px">
|
||||
<input name="profile_recurringSnapshotEnabled" type="checkbox" ng-model="state.RecurringSnapshotEnabled" /><i></i>
|
||||
</label>
|
||||
</div>
|
||||
<!-- !snapshotEnabled -->
|
||||
<!-- snapshotInterval -->
|
||||
<div class="form-group" ng-if="profile.SnapshotEnabled && state.RecurringSnapshotEnabled">
|
||||
<label for="profile_snapshotInterval" class="col-sm-2 col-lg-1 control-label text-left" style="margin-top: 20px"> Snapshot interval </label>
|
||||
<div class="col-sm-4">
|
||||
<slider model="profile.SnapshotInterval" floor="1" ceil="2880" step="1"></slider>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="1" class="form-control" ng-model="profile.SnapshotInterval" id="profile_snapshotInterval" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<p class="small text-muted" style="margin-top: 7px"> Snapshot interval (<b>minutes</b>) </p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !snapshotInterval -->
|
||||
<!-- encryptionEnabled -->
|
||||
<div class="form-group">
|
||||
<label for="profile_encryptionEnabled" class="col-sm-2 col-lg-1 control-label text-left"> Enable encryption </label>
|
||||
<label class="switch" style="margin-left: 20px"> <input name="profile_encryptionEnabled" type="checkbox" ng-model="profile.EncryptionEnabled" /><i></i> </label>
|
||||
</div>
|
||||
<!-- !encryptionEnabled -->
|
||||
<!-- interfaceType -->
|
||||
<div class="form-group">
|
||||
<label for="profile_interfaceType" class="col-sm-2 col-lg-1 control-label text-left">Interface type</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_interfaceType" ng-model="profile.InterfaceType" class="form-control">
|
||||
<option value=""></option>
|
||||
<option value="nfs">nfs</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !interfaceType -->
|
||||
<!-- interfaceDriver -->
|
||||
<div class="form-group">
|
||||
<label for="profile_interfaceDriver" class="col-sm-2 col-lg-1 control-label text-left">Network driver</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<select name="profile_interfaceDriver" ng-model="profile.InterfaceDriver" class="form-control">
|
||||
<option value=""></option>
|
||||
<option value="macvlan">macvlan</option>
|
||||
<option value="overlay">overlay</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !interfaceDriver -->
|
||||
<!-- interfaceNetwork -->
|
||||
<div class="form-group">
|
||||
<label for="profile_interfaceNetwork" class="col-sm-2 col-lg-1 control-label text-left">Network name</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="profile.InterfaceNetwork" name="profile_interfaceNetwork" pattern="[a-zA-Z0-9]+" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !interfaceNetwork -->
|
||||
<!-- interfaceConf -->
|
||||
<div class="form-group">
|
||||
<label for="profile_interfaceConf" class="col-sm-2 col-lg-1 control-label text-left">Interface conf</label>
|
||||
<div class="col-sm-10 col-lg-11">
|
||||
<input type="text" class="form-control" ng-model="profile.InterfaceConf" name="profile_interfaceConf" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !interfaceConf -->
|
||||
<!-- labels -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12" style="margin-top: 5px">
|
||||
<label class="control-label text-left">Labels</label>
|
||||
<span class="label label-default interactive" style="margin-left: 10px" ng-click="addLabel()"> <i class="fa fa-plus-circle" aria-hidden="true"></i> add label </span>
|
||||
</div>
|
||||
<!-- labels-input-list -->
|
||||
<div class="col-sm-12 form-inline" style="margin-top: 10px">
|
||||
<div ng-repeat="label in formValues.Labels" style="margin-top: 2px">
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">name</span>
|
||||
<input type="text" class="form-control" ng-model="label.name" placeholder="e.g. com.example.foo" />
|
||||
</div>
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">value</span>
|
||||
<input type="text" class="form-control" ng-model="label.value" placeholder="e.g. bar" />
|
||||
</div>
|
||||
<button class="btn btn-sm btn-danger" type="button" ng-click="removeLabel($index)">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !labels-input-list -->
|
||||
</div>
|
||||
<!-- !labels -->
|
||||
<!-- iops -->
|
||||
<div ng-if="!state.LimitBandwidth || state.NoLimit">
|
||||
<div class="col-sm-12 form-section-title"> IOPS </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label for="permissions" class="control-label text-left"> Limit IOPS </label>
|
||||
<label class="switch" style="margin-left: 20px">
|
||||
<input type="checkbox" ng-model="state.LimitIOPS" ng-change="state.NoLimit = (!state.LimitBandwidth && !state.LimitIOPS)" /><i></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="state.LimitIOPS">
|
||||
<label for="min_iops" class="col-sm-1 control-label text-left">Min</label>
|
||||
<div class="col-sm-5" ng-class="{ 'has-error': storidgeUpdateProfileForm.min_iops.$invalid }">
|
||||
<input type="number" class="form-control" ng-model="profile.MinIOPS" name="min_iops" ng-min="30" ng-max="999999" placeholder="100" required />
|
||||
</div>
|
||||
<label for="max_iops" class="col-sm-1 control-label text-left">Max</label>
|
||||
<div class="col-sm-5" ng-class="{ 'has-error': storidgeUpdateProfileForm.max_iops.$invalid }">
|
||||
<input type="number" class="form-control" ng-model="profile.MaxIOPS" name="max_iops" ng-min="30" ng-max="999999" placeholder="2000" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeUpdateProfileForm.min_iops.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeUpdateProfileForm.min_iops.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A value is required for Min IOPS.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for Min IOPS: 30.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for Min IOPS: 999999.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeUpdateProfileForm.max_iops.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeUpdateProfileForm.max_iops.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A value is required for Max IOPS.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for Max IOPS: 30.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for Max IOPS: 999999.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !iops -->
|
||||
<!-- bandwidth -->
|
||||
<div ng-if="!state.LimitIOPS || state.NoLimit">
|
||||
<div class="col-sm-12 form-section-title"> Bandwidth </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label for="permissions" class="control-label text-left"> Limit bandwidth </label>
|
||||
<label class="switch" style="margin-left: 20px">
|
||||
<input type="checkbox" ng-model="state.LimitBandwidth" ng-change="state.NoLimit = (!state.LimitBandwidth && !state.LimitIOPS)" /><i></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="state.LimitBandwidth">
|
||||
<label for="min_bandwidth" class="col-sm-1 control-label text-left">Min</label>
|
||||
<div class="col-sm-5" ng-class="{ 'has-error': storidgeUpdateProfileForm.min_bandwidth.$invalid }">
|
||||
<input type="number" class="form-control" ng-model="profile.MinBandwidth" name="min_bandwidth" ng-min="1" ng-max="5000" placeholder="1" required />
|
||||
</div>
|
||||
<label for="max_bandwidth" class="col-sm-1 control-label text-left">Max</label>
|
||||
<div class="col-sm-5" ng-class="{ 'has-error': storidgeUpdateProfileForm.max_bandwidth.$invalid }">
|
||||
<input type="number" class="form-control" ng-model="profile.MaxBandwidth" name="max_bandwidth" ng-min="1" ng-max="5000" placeholder="100" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeUpdateProfileForm.min_bandwidth.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeUpdateProfileForm.min_bandwidth.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A value is required for Min bandwidth.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for Min bandwidth: 1.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for Min bandwidth: 5000.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="storidgeUpdateProfileForm.max_bandwidth.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="storidgeUpdateProfileForm.max_bandwidth.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> A value is required for Max bandwidth.</p>
|
||||
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum value for Max bandwidth: 1.</p>
|
||||
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum value for Max bandwidth: 5000.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !bandwidth -->
|
||||
<!-- actions -->
|
||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-click="update()"
|
||||
ng-disabled="state.updateInProgress || !storidgeUpdateProfileForm.$valid"
|
||||
button-spinner="state.updateInProgress"
|
||||
>
|
||||
<span ng-hide="state.updateInProgress">Update the profile</span>
|
||||
<span ng-show="state.updateInProgress">Updating profile...</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" ng-click="delete()" ng-disabled="state.deleteInProgress" button-spinner="state.deleteInProgress">
|
||||
<span ng-hide="state.deleteInProgress">Delete the profile</span>
|
||||
<span ng-show="state.deleteInProgress">Deleting profile...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
|
@ -1,145 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').controller('StoridgeProfileController', [
|
||||
'$scope',
|
||||
'$state',
|
||||
'$transition$',
|
||||
'Notifications',
|
||||
'StoridgeProfileService',
|
||||
'ModalService',
|
||||
function ($scope, $state, $transition$, Notifications, StoridgeProfileService, ModalService) {
|
||||
$scope.formValues = {
|
||||
Labels: [],
|
||||
};
|
||||
|
||||
$scope.state = {
|
||||
NoLimit: false,
|
||||
LimitIOPS: false,
|
||||
LimitBandwidth: false,
|
||||
updateInProgress: false,
|
||||
deleteInProgress: false,
|
||||
RecurringSnapshotEnabled: false,
|
||||
};
|
||||
|
||||
$scope.addLabel = function () {
|
||||
$scope.formValues.Labels.push({ name: '', value: '' });
|
||||
};
|
||||
|
||||
$scope.removeLabel = function (index) {
|
||||
$scope.formValues.Labels.splice(index, 1);
|
||||
};
|
||||
|
||||
function prepareLabels(profile) {
|
||||
var labels = {};
|
||||
$scope.formValues.Labels.forEach(function (label) {
|
||||
if (label.name && label.value) {
|
||||
labels[label.name] = label.value;
|
||||
}
|
||||
});
|
||||
profile.Labels = labels;
|
||||
}
|
||||
|
||||
function initLabels(labels) {
|
||||
$scope.formValues.Labels = Object.keys(labels).map(function (key) {
|
||||
return { name: key, value: labels[key] };
|
||||
});
|
||||
}
|
||||
|
||||
$scope.RedundancyOptions = [
|
||||
{ value: 2, label: '2-copy' },
|
||||
{ value: 3, label: '3-copy' },
|
||||
];
|
||||
|
||||
$scope.update = function () {
|
||||
var profile = $scope.profile;
|
||||
|
||||
if (!$scope.state.LimitIOPS) {
|
||||
delete profile.MinIOPS;
|
||||
delete profile.MaxIOPS;
|
||||
}
|
||||
|
||||
if (!$scope.state.LimitBandwidth) {
|
||||
delete profile.MinBandwidth;
|
||||
delete profile.MaxBandwidth;
|
||||
}
|
||||
|
||||
if (profile.SnapshotEnabled) {
|
||||
if (!profile.SnapshotMax || profile.SnapshotMax <= 0) {
|
||||
profile.SnapshotMax = 1;
|
||||
}
|
||||
if (!$scope.state.RecurringSnapshotEnabled) {
|
||||
delete profile.SnapshotInterval;
|
||||
}
|
||||
if ($scope.state.RecurringSnapshotEnabled && (!profile.SnapshotInterval || profile.SnapshotInterval <= 0)) {
|
||||
profile.SnapshotInterval = 1440;
|
||||
}
|
||||
} else {
|
||||
delete profile.SnapshotMax;
|
||||
delete profile.SnapshotInterval;
|
||||
}
|
||||
|
||||
prepareLabels(profile);
|
||||
|
||||
$scope.state.updateInProgress = true;
|
||||
StoridgeProfileService.update(profile)
|
||||
.then(function success() {
|
||||
Notifications.success('Profile successfully updated');
|
||||
$state.go('storidge.profiles');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update profile');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.updateInProgress = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.delete = function () {
|
||||
ModalService.confirmDeletion('Do you want to remove this profile?', function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
deleteProfile();
|
||||
});
|
||||
};
|
||||
|
||||
function deleteProfile() {
|
||||
var profile = $scope.profile;
|
||||
|
||||
$scope.state.deleteInProgress = true;
|
||||
StoridgeProfileService.delete(profile.Name)
|
||||
.then(function success() {
|
||||
Notifications.success('Profile successfully deleted');
|
||||
$state.go('storidge.profiles');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to delete profile');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.deleteInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
function initView() {
|
||||
StoridgeProfileService.profile($transition$.params().id)
|
||||
.then(function success(data) {
|
||||
var profile = data;
|
||||
if ((profile.MinIOPS && profile.MinIOPS !== 0) || (profile.MaxIOPS && profile.MaxIOPS !== 0)) {
|
||||
$scope.state.LimitIOPS = true;
|
||||
} else if ((profile.MinBandwidth && profile.MinBandwidth !== 0) || (profile.MaxBandwidth && profile.MaxBandwidth !== 0)) {
|
||||
$scope.state.LimitBandwidth = true;
|
||||
} else {
|
||||
$scope.state.NoLimit = true;
|
||||
}
|
||||
if (profile.SnapshotEnabled && profile.SnapshotInterval !== 0) {
|
||||
$scope.state.RecurringSnapshotEnabled = true;
|
||||
}
|
||||
initLabels(profile.Labels);
|
||||
$scope.profile = profile;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve profile details');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -1,129 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Storidge profiles">
|
||||
<a data-toggle="tooltip" title="Refresh" ui-sref="storidge.profiles" ui-sref-opts="{reload: true}">
|
||||
<i class="fa fa-sync" aria-hidden="true"></i>
|
||||
</a>
|
||||
</rd-header-title>
|
||||
<rd-header-content> <a ui-sref="storidge.cluster">Storidge</a> > <a ui-sref="storidge.profiles">Profiles</a> </rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-plus" title-text="Add a profile"> </rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="profile_name" class="col-sm-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-11">
|
||||
<input type="text" class="form-control" ng-model="formValues.Name" id="profile_name" placeholder="e.g. myProfile" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- tag-note -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small text-muted">Note: The profile will be created using the default properties.</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !tag-note -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-click="create()"
|
||||
ng-disabled="state.actionInProgress || !formValues.Name"
|
||||
button-spinner="state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress">Create the profile</span>
|
||||
<span ng-show="state.actionInProgress">Creating profile...</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Name" ui-sref="storidge.profiles.new({ profileName: formValues.Name })"
|
||||
>Modify defaults...</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<storidge-profiles-datatable
|
||||
title-text="Profiles"
|
||||
title-icon="fa-sticky-note"
|
||||
dataset="profiles"
|
||||
table-key="storidge_profiles"
|
||||
order-by="Name"
|
||||
remove-action="removeAction"
|
||||
></storidge-profiles-datatable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="row">
|
||||
<div class="col-md-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-sticky-note" title-text="Profiles">
|
||||
<div class="pull-right">
|
||||
Items per page:
|
||||
<select ng-model="state.pagination_count" ng-change="changePaginationCount()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-taskbar classes="col-md-12">
|
||||
<div class="pull-left">
|
||||
<button type="button" class="btn btn-danger" ng-click="removeProfiles()" ng-disabled="!state.selectedItemCount"><i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove</button>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input type="checkbox" ng-model="allSelected" ng-change="selectItems(allSelected)" />
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="order('Name')">
|
||||
Name
|
||||
<span ng-show="sortType == 'Name' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Name' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="profile in (state.filteredProfiles = (profiles | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
|
||||
<td><input type="checkbox" ng-model="profile.Checked" ng-change="selectItem(profile)" /></td>
|
||||
<td>
|
||||
<a ui-sref="storidge.profiles.profile({id: profile.Name})">{{ profile.Name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!profiles">
|
||||
<td colspan="3" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="state.filteredProfiles.length == 0">
|
||||
<td colspan="3" class="text-center text-muted">No profiles available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="profiles" class="pull-left pagination-controls">
|
||||
<dir-pagination-controls></dir-pagination-controls>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div> -->
|
|
@ -1,76 +0,0 @@
|
|||
import _ from 'lodash-es';
|
||||
import { StoridgeProfileDefaultModel } from '../../models/profile';
|
||||
|
||||
angular.module('portainer.integrations.storidge').controller('StoridgeProfilesController', [
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
'Notifications',
|
||||
'StoridgeProfileService',
|
||||
function ($q, $scope, $state, Notifications, StoridgeProfileService) {
|
||||
$scope.state = {
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
$scope.formValues = {
|
||||
Name: '',
|
||||
};
|
||||
|
||||
$scope.removeAction = function (selectedItems) {
|
||||
var actionCount = selectedItems.length;
|
||||
angular.forEach(selectedItems, function (profile) {
|
||||
StoridgeProfileService.delete(profile.Name)
|
||||
.then(function success() {
|
||||
Notifications.success('Profile successfully removed', profile.Name);
|
||||
var index = $scope.profiles.indexOf(profile);
|
||||
$scope.profiles.splice(index, 1);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to remove profile');
|
||||
})
|
||||
.finally(function final() {
|
||||
--actionCount;
|
||||
if (actionCount === 0) {
|
||||
$state.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.create = function () {
|
||||
var model = new StoridgeProfileDefaultModel();
|
||||
model.Labels = {};
|
||||
model.Name = $scope.formValues.Name;
|
||||
model.Directory = model.Directory + _.toLower(model.Name);
|
||||
delete model.MinBandwidth;
|
||||
delete model.MaxBandwidth;
|
||||
delete model.MinIOPS;
|
||||
delete model.MaxIOPS;
|
||||
|
||||
$scope.state.actionInProgress = true;
|
||||
StoridgeProfileService.create(model)
|
||||
.then(function success() {
|
||||
Notifications.success('Profile successfully created');
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to create profile');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
};
|
||||
|
||||
function initView() {
|
||||
StoridgeProfileService.profiles()
|
||||
.then(function success(data) {
|
||||
$scope.profiles = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve profiles');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -1,52 +0,0 @@
|
|||
<rd-header>
|
||||
<rd-header-title title-text="Snasphot details"></rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="docker.volumes">Volumes</a> > <a ui-sref="docker.volumes.volume({id: volumeId})">{{ volumeId }}</a> > Snapshots >
|
||||
<a ui-sref="docker.volumes.volume.snapshot({id: snapshot.Id})">{{ snapshot.Id }}</a>
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-hdd" title-text="Snapshot details "></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>
|
||||
{{ snapshot.Id }}
|
||||
<button class="btn btn-xs btn-danger" ng-click="removeSnapshot()"> <i class="fa fa-trash space-right" aria-hidden="true"></i>Remove snapshot </button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Date</td>
|
||||
<td>{{ snapshot.Date }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ snapshot.Description }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SourceID</td>
|
||||
<td>{{ snapshot.SourceID }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Type</td>
|
||||
<td>{{ snapshot.Type }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Directory</td>
|
||||
<td>{{ snapshot.Directory }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Source</td>
|
||||
<td>{{ snapshot.Source }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
|
@ -1,50 +0,0 @@
|
|||
angular.module('portainer.integrations.storidge').controller('StoridgeSnapshotController', [
|
||||
'$scope',
|
||||
'$state',
|
||||
'$transition$',
|
||||
'Notifications',
|
||||
'ModalService',
|
||||
'StoridgeSnapshotService',
|
||||
function ($scope, $state, $transition$, Notifications, ModalService, StoridgeSnapshotService) {
|
||||
$scope.removeSnapshot = function () {
|
||||
ModalService.confirm({
|
||||
title: 'Are you sure?',
|
||||
message: 'Do you want really want to remove this snapshot?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Remove',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
},
|
||||
callback: function onConfirm(confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
StoridgeSnapshotService.remove($scope.snapshot.Id)
|
||||
.then(function () {
|
||||
Notifications.success('Success', 'Snapshot removed');
|
||||
$state.go('portainer.volumes.volume', { id: $scope.volumeId });
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to remove snapshot');
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function initView() {
|
||||
$scope.volumeId = $transition$.params().id;
|
||||
$scope.snapshotId = $transition$.params().snapshotId;
|
||||
|
||||
StoridgeSnapshotService.snapshot($scope.snapshotId)
|
||||
.then(function success(data) {
|
||||
$scope.snapshot = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve snapshot details');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
},
|
||||
]);
|
|
@ -30,7 +30,7 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
|
|||
}
|
||||
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
await StateManager.updateEndpointState(endpoint, []);
|
||||
await StateManager.updateEndpointState(endpoint);
|
||||
|
||||
if (endpoint.Type === 7 && endpoint.Status === 2) {
|
||||
throw new Error('Unable to contact Edge agent, please ensure that the agent is properly running on the remote environment.');
|
||||
|
|
|
@ -9,8 +9,6 @@ export function createMockUser(id: number, username: string): UserViewModel {
|
|||
EndpointAuthorizations: {},
|
||||
PortainerAuthorizations: {
|
||||
PortainerDockerHubInspect: true,
|
||||
PortainerEndpointExtensionAdd: true,
|
||||
PortainerEndpointExtensionRemove: true,
|
||||
PortainerEndpointGroupInspect: true,
|
||||
PortainerEndpointGroupList: true,
|
||||
PortainerEndpointInspect: true,
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
// TODO: legacy extension management
|
||||
angular.module('portainer.app').factory('LegacyExtensions', [
|
||||
'$resource',
|
||||
'EndpointProvider',
|
||||
'API_ENDPOINT_ENDPOINTS',
|
||||
function LegacyExtensions($resource, EndpointProvider, API_ENDPOINT_ENDPOINTS) {
|
||||
'use strict';
|
||||
return $resource(
|
||||
API_ENDPOINT_ENDPOINTS + '/:endpointId/extensions/:type',
|
||||
{
|
||||
endpointId: EndpointProvider.endpointID,
|
||||
},
|
||||
{
|
||||
register: { method: 'POST' },
|
||||
deregister: { method: 'DELETE', params: { type: '@type' } },
|
||||
}
|
||||
);
|
||||
},
|
||||
]);
|
|
@ -1,23 +0,0 @@
|
|||
// TODO: legacy extension management
|
||||
angular.module('portainer.app').factory('LegacyExtensionService', [
|
||||
'LegacyExtensions',
|
||||
function LegacyExtensionServiceFactory(LegacyExtensions) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.registerStoridgeExtension = function (url) {
|
||||
var payload = {
|
||||
Type: 1,
|
||||
URL: url,
|
||||
};
|
||||
|
||||
return LegacyExtensions.register(payload).$promise;
|
||||
};
|
||||
|
||||
service.deregisterStoridgeExtension = function () {
|
||||
return LegacyExtensions.deregister({ type: 1 }).$promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -1,86 +0,0 @@
|
|||
import _ from 'lodash-es';
|
||||
|
||||
// TODO: legacy extension management
|
||||
angular.module('portainer.app').factory('LegacyExtensionManager', [
|
||||
'$q',
|
||||
'PluginService',
|
||||
'SystemService',
|
||||
'NodeService',
|
||||
'LegacyExtensionService',
|
||||
function ExtensionManagerFactory($q, PluginService, SystemService, NodeService, LegacyExtensionService) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.initEndpointExtensions = function (endpoint) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (endpoint.Status !== 1) {
|
||||
deferred.resolve([]);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
SystemService.version()
|
||||
.then(function success(data) {
|
||||
var endpointAPIVersion = parseFloat(data.ApiVersion);
|
||||
|
||||
return $q.all([endpointAPIVersion >= 1.25 ? initStoridgeExtension() : {}]);
|
||||
})
|
||||
.then(function success(data) {
|
||||
var extensions = data;
|
||||
deferred.resolve(extensions);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to connect to the Docker environment', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
function initStoridgeExtension() {
|
||||
var deferred = $q.defer();
|
||||
|
||||
PluginService.volumePlugins()
|
||||
.then(function success(data) {
|
||||
var volumePlugins = data;
|
||||
if (_.includes(volumePlugins, 'cio:latest')) {
|
||||
return registerStoridgeUsingSwarmManagerIP();
|
||||
} else {
|
||||
return deregisterStoridgeExtension();
|
||||
}
|
||||
})
|
||||
.then(function success(data) {
|
||||
deferred.resolve(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'An error occured during Storidge extension check', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function registerStoridgeUsingSwarmManagerIP() {
|
||||
var deferred = $q.defer();
|
||||
|
||||
NodeService.getActiveManager()
|
||||
.then(function success(data) {
|
||||
var managerIP = data.Addr;
|
||||
var storidgeAPIURL = 'tcp://' + managerIP + ':8282';
|
||||
return LegacyExtensionService.registerStoridgeExtension(storidgeAPIURL);
|
||||
})
|
||||
.then(function success(data) {
|
||||
deferred.resolve(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'An error occured during Storidge extension initialization', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function deregisterStoridgeExtension() {
|
||||
return LegacyExtensionService.deregisterStoridgeExtension();
|
||||
}
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
|
@ -26,7 +26,6 @@ function StateManagerFactory(
|
|||
dismissedInfoPanels: {},
|
||||
dismissedInfoHash: '',
|
||||
},
|
||||
extensions: [],
|
||||
};
|
||||
|
||||
manager.setVersionInfo = function (versionInfo) {
|
||||
|
@ -153,20 +152,7 @@ function StateManagerFactory(
|
|||
return cacheValidity < APPLICATION_CACHE_VALIDITY;
|
||||
}
|
||||
|
||||
function assignExtensions(endpointExtensions) {
|
||||
var extensions = [];
|
||||
|
||||
for (var i = 0; i < endpointExtensions.length; i++) {
|
||||
var extension = endpointExtensions[i];
|
||||
if (extension.Type === 1) {
|
||||
extensions.push('storidge');
|
||||
}
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
manager.updateEndpointState = function (endpoint, extensions) {
|
||||
manager.updateEndpointState = function (endpoint) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (endpoint.Type === 3) {
|
||||
|
@ -196,7 +182,6 @@ function StateManagerFactory(
|
|||
state.endpoint.name = endpoint.Name;
|
||||
state.endpoint.type = endpoint.Type;
|
||||
state.endpoint.apiVersion = endpointAPIVersion;
|
||||
state.endpoint.extensions = assignExtensions(extensions);
|
||||
|
||||
if (endpointMode.agentProxy && endpoint.Status === 1) {
|
||||
return AgentPingService.ping().then(function onPingSuccess(data) {
|
||||
|
|
|
@ -24,8 +24,6 @@ export function mockExampleData() {
|
|||
EndpointAuthorizations: {},
|
||||
PortainerAuthorizations: {
|
||||
PortainerDockerHubInspect: true,
|
||||
PortainerEndpointExtensionAdd: true,
|
||||
PortainerEndpointExtensionRemove: true,
|
||||
PortainerEndpointGroupInspect: true,
|
||||
PortainerEndpointGroupList: true,
|
||||
PortainerEndpointInspect: true,
|
||||
|
@ -51,8 +49,6 @@ export function mockExampleData() {
|
|||
EndpointAuthorizations: {},
|
||||
PortainerAuthorizations: {
|
||||
PortainerDockerHubInspect: true,
|
||||
PortainerEndpointExtensionAdd: true,
|
||||
PortainerEndpointExtensionRemove: true,
|
||||
PortainerEndpointGroupInspect: true,
|
||||
PortainerEndpointGroupList: true,
|
||||
PortainerEndpointInspect: true,
|
||||
|
|
|
@ -39,26 +39,6 @@
|
|||
></docker-sidebar>
|
||||
</div>
|
||||
|
||||
<sidebar-section title="Integrations" ng-if="applicationState.endpoint.mode && applicationState.endpoint.extensions.length > 0" authorization="IntegrationStoridgeAdmin">
|
||||
<sidebar-menu
|
||||
ng-if="
|
||||
applicationState.endpoint.mode &&
|
||||
applicationState.endpoint.extensions.indexOf('storidge') !== -1 &&
|
||||
applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' &&
|
||||
applicationState.endpoint.mode.role === 'MANAGER'
|
||||
"
|
||||
icon-class="fa-bolt fa-fw"
|
||||
label="Storidge"
|
||||
path="storidge.cluster"
|
||||
is-sidebar-open="toggle"
|
||||
children-paths="['storidge.cluster', 'storidge.profiles', 'storidge.monitor', 'storidge.profiles.new', 'storidge.profiles.profile', 'storidge.drives', 'storidge.drives.drive', 'storidge.cluster.node']"
|
||||
>
|
||||
<sidebar-menu-item path="storidge.monitor" class-name="sidebar-sublist">Monitor</sidebar-menu-item>
|
||||
<sidebar-menu-item path="storidge.profiles" class-name="sidebar-sublist">Profiles</sidebar-menu-item>
|
||||
<sidebar-menu-item path="storidge.drives" class-name="sidebar-sublist">Drives</sidebar-menu-item>
|
||||
</sidebar-menu>
|
||||
</sidebar-section>
|
||||
|
||||
<sidebar-section title="Edge compute" ng-if="isAdmin && applicationState.application.enableEdgeComputeFeatures">
|
||||
<sidebar-menu-item path="edge.devices" icon-class="fas fa-laptop-code fa-fw" class-name="sidebar-list" data-cy="portainerSidebar-edgeDevices">
|
||||
Edge Devices
|
||||
|
|
|
@ -12597,7 +12597,7 @@ locate-path@^6.0.0:
|
|||
dependencies:
|
||||
p-locate "^5.0.0"
|
||||
|
||||
lodash-es@^4.17.15, lodash-es@^4.17.21:
|
||||
lodash-es@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
|
||||
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
|
||||
|
|
Loading…
Reference in New Issue