]> xenbits.xensource.com Git - libvirt.git/commitdiff
esx: Support folders in the path of vpx:// connection URIs
authorMatthias Bolte <matthias.bolte@googlemail.com>
Tue, 1 Nov 2011 16:12:37 +0000 (17:12 +0100)
committerMatthias Bolte <matthias.bolte@googlemail.com>
Tue, 1 Nov 2011 17:45:42 +0000 (18:45 +0100)
Allow the datacenter and compute resource parts of the path
to be prefixed with folders. Therefore, the way the path is
parsed has changed. Before, it was split in 2 or 3 items and
the items' meanings were determined by their positions. Now
the path can have 2 or more items and the the vCenter server
is asked whether a folder, datacenter of compute resource
with the specified name exists at the current hierarchy level.

Before the datacenter and compute resource lookup automatically
traversed folders during lookup. This is logic got removed
and folders have to be specified explicitly.

The proper datacenter path including folders is now used when
accessing a datastore over HTTPS. This makes virsh dumpxml
and define work for datacenters in folders.

https://bugzilla.redhat.com/show_bug.cgi?id=732676

docs/drvesx.html.in
src/esx/esx_driver.c
src/esx/esx_util.c
src/esx/esx_util.h
src/esx/esx_vi.c
src/esx/esx_vi.h
src/esx/esx_vi_generator.input

index da9d2a1ba711051b7a79aebca44ab1628edcd60d..7d323b352169cb0f3a78687784e0e55f93524161 100644 (file)
@@ -56,7 +56,7 @@ esx://example-esx.com/?no_verify=1     (ESX over HTTPS, but doesn't verify the s
         URIs have this general form (<code>[...]</code> marks an optional part).
     </p>
 <pre>
-type://[username@]hostname[:port]/[datacenter[/cluster]/server][?extraparameters]
+type://[username@]hostname[:port]/[[folder/...]datacenter/[folder/...][cluster/]server][?extraparameters]
 </pre>
     <p>
         The <code>type://</code> is either <code>esx://</code> or
@@ -79,6 +79,14 @@ type://[username@]hostname[:port]/[datacenter[/cluster]/server][?extraparameters
     </p>
 <pre>
 vpx://example-vcenter.com/dc1/cluster1/example-esx.com
+</pre>
+    <p>
+        Datacenters and clusters can be organized in folders, those have to be
+        specified as well. The driver can handle folders
+        <span class="since">since 0.9.7</span>.
+    </p>
+<pre>
+vpx://example-vcenter.com/folder1/dc1/folder2/example-esx.com
 </pre>
 
 
index 5d0b6dbd51e04aa0ba62648e3d40bb6441e2e701..dfeef8533239a53f3297b8f6a1b43f7916dfbe96 100644 (file)
@@ -722,7 +722,7 @@ esxConnectToHost(esxPrivate *priv, virConnectAuthPtr auth,
     if (esxVI_Context_Alloc(&priv->host) < 0 ||
         esxVI_Context_Connect(priv->host, url, ipAddress, username, password,
                               priv->parsedUri) < 0 ||
-        esxVI_Context_LookupObjectsByPath(priv->host, priv->parsedUri) < 0) {
+        esxVI_Context_LookupManagedObjects(priv->host) < 0) {
         goto cleanup;
     }
 
@@ -804,8 +804,7 @@ esxConnectToVCenter(esxPrivate *priv, virConnectAuthPtr auth,
     char *url = NULL;
 
     if (hostSystemIpAddress == NULL &&
-        (priv->parsedUri->path_datacenter == NULL ||
-         priv->parsedUri->path_computeResource == NULL)) {
+        (priv->parsedUri->path == NULL || STREQ(priv->parsedUri->path, "/"))) {
         ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
                   _("Path has to specify the datacenter and compute resource"));
         return -1;
@@ -869,13 +868,13 @@ esxConnectToVCenter(esxPrivate *priv, virConnectAuthPtr auth,
     }
 
     if (hostSystemIpAddress != NULL) {
-        if (esxVI_Context_LookupObjectsByHostSystemIp(priv->vCenter,
-                                                      hostSystemIpAddress) < 0) {
+        if (esxVI_Context_LookupManagedObjectsByHostSystemIp
+              (priv->vCenter, hostSystemIpAddress) < 0) {
             goto cleanup;
         }
     } else {
-        if (esxVI_Context_LookupObjectsByPath(priv->vCenter,
-                                              priv->parsedUri) < 0) {
+        if (esxVI_Context_LookupManagedObjectsByPath(priv->vCenter,
+                                                     priv->parsedUri->path) < 0) {
             goto cleanup;
         }
     }
@@ -894,8 +893,8 @@ esxConnectToVCenter(esxPrivate *priv, virConnectAuthPtr auth,
 
 
 /*
- * URI format: {vpx|esx|gsx}://[<username>@]<hostname>[:<port>]/[<path>][?<query parameter> ...]
- *             <path> = <datacenter>/<computeresource>[/<hostsystem>]
+ * URI format: {vpx|esx|gsx}://[<username>@]<hostname>[:<port>]/[<path>][?<query parameter>...]
+ *             <path> = [<folder>/...]<datacenter>/[<folder>/...]<computeresource>[/<hostsystem>]
  *
  * If no port is specified the default port is set dependent on the scheme and
  * transport parameter:
@@ -909,7 +908,8 @@ esxConnectToVCenter(esxPrivate *priv, virConnectAuthPtr auth,
  * For a vpx:// connection <path> references a host managed by the vCenter.
  * In case the host is part of a cluster then <computeresource> is the cluster
  * name. Otherwise <computeresource> and <hostsystem> are equal and the later
- * can be omitted.
+ * can be omitted. As datacenters and computeresources can be organized in
+ * folders those have to be included in <path>.
  *
  * Optional query parameters:
  * - transport={http|https}
@@ -977,6 +977,12 @@ esxOpen(virConnectPtr conn, virConnectAuthPtr auth,
         return VIR_DRV_OPEN_ERROR;
     }
 
+    if (STRCASENEQ(conn->uri->scheme, "vpx") &&
+        conn->uri->path != NULL && STRNEQ(conn->uri->path, "/")) {
+        VIR_WARN("Ignoring unexpected path '%s' for non-vpx scheme '%s'",
+                 conn->uri->path, conn->uri->scheme);
+    }
+
     /* Require server part */
     if (conn->uri->server == NULL) {
         ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
@@ -2769,7 +2775,7 @@ esxDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
                       domain->conn->uri->server, domain->conn->uri->port);
     virBufferURIEncodeString(&buffer, directoryAndFileName);
     virBufferAddLit(&buffer, "?dcPath=");
-    virBufferURIEncodeString(&buffer, priv->primary->datacenter->name);
+    virBufferURIEncodeString(&buffer, priv->primary->datacenterPath);
     virBufferAddLit(&buffer, "&dsName=");
     virBufferURIEncodeString(&buffer, datastoreName);
 
@@ -3237,7 +3243,7 @@ esxDomainDefineXML(virConnectPtr conn, const char *xml)
 
     virBufferURIEncodeString(&buffer, escapedName);
     virBufferAddLit(&buffer, ".vmx?dcPath=");
-    virBufferURIEncodeString(&buffer, priv->primary->datacenter->name);
+    virBufferURIEncodeString(&buffer, priv->primary->datacenterPath);
     virBufferAddLit(&buffer, "&dsName=");
     virBufferURIEncodeString(&buffer, datastoreName);
 
index c14179d7f1971fee9aa05a28fbe10e79a98df7e4..1925802f490ae551ad2d8270f21cdf3e10daa063 100644 (file)
@@ -51,7 +51,6 @@ esxUtil_ParseUri(esxUtil_ParsedUri **parsedUri, xmlURIPtr uri)
     int noVerify;
     int autoAnswer;
     char *tmp;
-    char *saveptr;
 
     if (parsedUri == NULL || *parsedUri != NULL) {
         ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
@@ -184,26 +183,13 @@ esxUtil_ParseUri(esxUtil_ParsedUri **parsedUri, xmlURIPtr uri)
         }
     }
 
-    /* Expected format: [/]<datacenter>/<computeresource>[/<hostsystem>] */
     if (uri->path != NULL) {
-        tmp = strdup(uri->path);
+        (*parsedUri)->path = strdup(uri->path);
 
-        if (tmp == NULL) {
+        if ((*parsedUri)->path == NULL) {
             virReportOOMError();
             goto cleanup;
         }
-
-        if (esxVI_String_DeepCopyValue(&(*parsedUri)->path_datacenter,
-                                       strtok_r(tmp, "/", &saveptr)) < 0 ||
-            esxVI_String_DeepCopyValue(&(*parsedUri)->path_computeResource,
-                                       strtok_r(NULL, "/", &saveptr)) < 0 ||
-            esxVI_String_DeepCopyValue(&(*parsedUri)->path_hostSystem,
-                                       strtok_r(NULL, "", &saveptr)) < 0) {
-            VIR_FREE(tmp);
-            goto cleanup;
-        }
-
-        VIR_FREE(tmp);
     }
 
     if ((*parsedUri)->transport == NULL) {
@@ -242,9 +228,7 @@ esxUtil_FreeParsedUri(esxUtil_ParsedUri **parsedUri)
     VIR_FREE((*parsedUri)->transport);
     VIR_FREE((*parsedUri)->vCenter);
     VIR_FREE((*parsedUri)->proxy_hostname);
-    VIR_FREE((*parsedUri)->path_datacenter);
-    VIR_FREE((*parsedUri)->path_computeResource);
-    VIR_FREE((*parsedUri)->path_hostSystem);
+    VIR_FREE((*parsedUri)->path);
 
     VIR_FREE(*parsedUri);
 }
index 39fdb6db415f4e8ff07a435c299988cd85088912..8d172e10a709c4ede2e28150148936e12440ccf4 100644 (file)
@@ -37,9 +37,7 @@ struct _esxUtil_ParsedUri {
     int proxy_type;
     char *proxy_hostname;
     int proxy_port;
-    char *path_datacenter;
-    char *path_computeResource;
-    char *path_hostSystem;
+    char *path;
 };
 
 int esxUtil_ParseUri(esxUtil_ParsedUri **parsedUri, xmlURIPtr uri);
index 86e5739a34080bc17982a915723a5b754bf324be..21892a0119225bae1dd7963f6a863a10911952de 100644 (file)
@@ -616,8 +616,11 @@ ESX_VI__TEMPLATE__FREE(Context,
     esxVI_UserSession_Free(&item->session);
     VIR_FREE(item->sessionLock);
     esxVI_Datacenter_Free(&item->datacenter);
+    VIR_FREE(item->datacenterPath);
     esxVI_ComputeResource_Free(&item->computeResource);
+    VIR_FREE(item->computeResourcePath);
     esxVI_HostSystem_Free(&item->hostSystem);
+    VIR_FREE(item->hostSystemName);
     esxVI_SelectionSpec_Free(&item->selectSet_folderToChildEntity);
     esxVI_SelectionSpec_Free(&item->selectSet_hostSystemToParent);
     esxVI_SelectionSpec_Free(&item->selectSet_hostSystemToVm);
@@ -789,21 +792,25 @@ esxVI_Context_Connect(esxVI_Context *ctx, const char *url,
 }
 
 int
-esxVI_Context_LookupObjectsByPath(esxVI_Context *ctx,
-                                  esxUtil_ParsedUri *parsedUri)
+esxVI_Context_LookupManagedObjects(esxVI_Context *ctx)
 {
-    char *hostSystemName = NULL;
     /* Lookup Datacenter */
-    if (esxVI_LookupDatacenter(ctx, parsedUri->path_datacenter,
-                               ctx->service->rootFolder, NULL, &ctx->datacenter,
+    if (esxVI_LookupDatacenter(ctx, NULL, ctx->service->rootFolder, NULL,
+                               &ctx->datacenter,
                                esxVI_Occurrence_RequiredItem) < 0) {
         return -1;
     }
 
+    ctx->datacenterPath = strdup(ctx->datacenter->name);
+
+    if (ctx->datacenterPath == NULL) {
+        virReportOOMError();
+        return -1;
+    }
+
     /* Lookup (Cluster)ComputeResource */
-    if (esxVI_LookupComputeResource(ctx, parsedUri->path_computeResource,
-                                    ctx->datacenter->hostFolder, NULL,
-                                    &ctx->computeResource,
+    if (esxVI_LookupComputeResource(ctx, NULL, ctx->datacenter->hostFolder,
+                                    NULL, &ctx->computeResource,
                                     esxVI_Occurrence_RequiredItem) < 0) {
         return -1;
     }
@@ -814,38 +821,240 @@ esxVI_Context_LookupObjectsByPath(esxVI_Context *ctx,
         return -1;
     }
 
+    ctx->computeResourcePath = strdup(ctx->computeResource->name);
+
+    if (ctx->computeResourcePath == NULL) {
+        virReportOOMError();
+        return -1;
+    }
+
     /* Lookup HostSystem */
-    if (parsedUri->path_hostSystem == NULL &&
-        STREQ(ctx->computeResource->_reference->type,
-              "ClusterComputeResource")) {
-        ESX_VI_ERROR(VIR_ERR_INVALID_ARG, "%s",
-                     _("Path has to specify the host system"));
+    if (esxVI_LookupHostSystem(ctx, NULL, ctx->computeResource->_reference,
+                               NULL, &ctx->hostSystem,
+                               esxVI_Occurrence_RequiredItem) < 0) {
         return -1;
     }
 
-    if (parsedUri->path_hostSystem != NULL ||
-        (parsedUri->path_computeResource != NULL &&
-         parsedUri->path_hostSystem == NULL)) {
-        if (parsedUri->path_hostSystem != NULL) {
-            hostSystemName = parsedUri->path_hostSystem;
+    ctx->hostSystemName = strdup(ctx->hostSystem->name);
+
+    if (ctx->hostSystemName == NULL) {
+        virReportOOMError();
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+esxVI_Context_LookupManagedObjectsByPath(esxVI_Context *ctx, const char *path)
+{
+    int result = -1;
+    char *tmp = NULL;
+    char *saveptr = NULL;
+    char *previousItem = NULL;
+    char *item = NULL;
+    virBuffer buffer = VIR_BUFFER_INITIALIZER;
+    esxVI_ManagedObjectReference *root = NULL;
+    esxVI_Folder *folder = NULL;
+
+    tmp = strdup(path);
+
+    if (tmp == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    /* Lookup Datacenter */
+    item = strtok_r(tmp, "/", &saveptr);
+
+    if (item == NULL) {
+        ESX_VI_ERROR(VIR_ERR_INVALID_ARG,
+                     _("Path '%s' does not specify a datacenter"), path);
+        goto cleanup;
+    }
+
+    root = ctx->service->rootFolder;
+
+    while (ctx->datacenter == NULL && item != NULL) {
+        esxVI_Folder_Free(&folder);
+
+        /* Try to lookup item as a folder */
+        if (esxVI_LookupFolder(ctx, item, root, NULL, &folder,
+                               esxVI_Occurrence_OptionalItem) < 0) {
+            goto cleanup;
+        }
+
+        if (folder != NULL) {
+            /* It's a folder, use it as new lookup root */
+            if (root != ctx->service->rootFolder) {
+                esxVI_ManagedObjectReference_Free(&root);
+            }
+
+            root = folder->_reference;
+            folder->_reference = NULL;
+        } else {
+            /* Try to lookup item as a datacenter */
+            if (esxVI_LookupDatacenter(ctx, item, root, NULL, &ctx->datacenter,
+                                       esxVI_Occurrence_OptionalItem) < 0) {
+                goto cleanup;
+            }
+        }
+
+        /* Build datacenter path */
+        if (virBufferUse(&buffer) > 0) {
+            virBufferAddChar(&buffer, '/');
+        }
+
+        virBufferAdd(&buffer, item, -1);
+
+        previousItem = item;
+        item = strtok_r(NULL, "/", &saveptr);
+    }
+
+    if (ctx->datacenter == NULL) {
+        ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
+                     _("Could not find datacenter specified in '%s'"), path);
+        goto cleanup;
+    }
+
+    if (virBufferError(&buffer)) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    ctx->datacenterPath = virBufferContentAndReset(&buffer);
+
+    /* Lookup (Cluster)ComputeResource */
+    if (item == NULL) {
+        ESX_VI_ERROR(VIR_ERR_INVALID_ARG,
+                     _("Path '%s' does not specify a compute resource"), path);
+        goto cleanup;
+    }
+
+    if (root != ctx->service->rootFolder) {
+        esxVI_ManagedObjectReference_Free(&root);
+    }
+
+    root = ctx->datacenter->hostFolder;
+
+    while (ctx->computeResource == NULL && item != NULL) {
+        esxVI_Folder_Free(&folder);
+
+        /* Try to lookup item as a folder */
+        if (esxVI_LookupFolder(ctx, item, root, NULL, &folder,
+                               esxVI_Occurrence_OptionalItem) < 0) {
+            goto cleanup;
+        }
+
+        if (folder != NULL) {
+            /* It's a folder, use it as new lookup root */
+            if (root != ctx->datacenter->hostFolder) {
+                esxVI_ManagedObjectReference_Free(&root);
+            }
+
+            root = folder->_reference;
+            folder->_reference = NULL;
         } else {
-            hostSystemName = parsedUri->path_computeResource;
+            /* Try to lookup item as a compute resource */
+            if (esxVI_LookupComputeResource(ctx, item, root, NULL,
+                                            &ctx->computeResource,
+                                            esxVI_Occurrence_OptionalItem) < 0) {
+                goto cleanup;
+            }
+        }
+
+        /* Build compute resource path */
+        if (virBufferUse(&buffer) > 0) {
+            virBufferAddChar(&buffer, '/');
+        }
+
+        virBufferAdd(&buffer, item, -1);
+
+        previousItem = item;
+        item = strtok_r(NULL, "/", &saveptr);
+    }
+
+    if (ctx->computeResource == NULL) {
+        ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
+                     _("Could not find compute resource specified in '%s'"),
+                     path);
+        goto cleanup;
+    }
+
+    if (ctx->computeResource->resourcePool == NULL) {
+        ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Could not retrieve resource pool"));
+        goto cleanup;
+    }
+
+    if (virBufferError(&buffer)) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    ctx->computeResourcePath = virBufferContentAndReset(&buffer);
+
+    /* Lookup HostSystem */
+    if (STREQ(ctx->computeResource->_reference->type,
+              "ClusterComputeResource")) {
+        if (item == NULL) {
+            ESX_VI_ERROR(VIR_ERR_INVALID_ARG,
+                         _("Path '%s' does not specify a host system"), path);
+            goto cleanup;
         }
+
+        /* The path specified a cluster, it has to specify a host system too */
+        previousItem = item;
+        item = strtok_r(NULL, "/", &saveptr);
+    }
+
+    if (item != NULL) {
+        ESX_VI_ERROR(VIR_ERR_INVALID_ARG,
+                     _("Path '%s' ends with an excess item"), path);
+        goto cleanup;
     }
 
-    if (esxVI_LookupHostSystem(ctx, hostSystemName,
+    ctx->hostSystemName = strdup(previousItem);
+
+    if (ctx->hostSystemName == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (esxVI_LookupHostSystem(ctx, ctx->hostSystemName,
                                ctx->computeResource->_reference, NULL,
                                &ctx->hostSystem,
-                               esxVI_Occurrence_RequiredItem) < 0) {
-        return -1;
+                               esxVI_Occurrence_OptionalItem) < 0) {
+        goto cleanup;
     }
 
-    return 0;
+    if (ctx->hostSystem == NULL) {
+        ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR,
+                     _("Could not find host system specified in '%s'"), path);
+        goto cleanup;
+    }
+
+    result = 0;
+
+  cleanup:
+    if (result < 0) {
+        virBufferFreeAndReset(&buffer);
+    }
+
+    if (root != ctx->service->rootFolder &&
+        (ctx->datacenter == NULL || root != ctx->datacenter->hostFolder)) {
+        esxVI_ManagedObjectReference_Free(&root);
+    }
+
+    VIR_FREE(tmp);
+    esxVI_Folder_Free(&folder);
+
+    return result;
 }
 
 int
-esxVI_Context_LookupObjectsByHostSystemIp(esxVI_Context *ctx,
-                                          const char *hostSystemIpAddress)
+esxVI_Context_LookupManagedObjectsByHostSystemIp(esxVI_Context *ctx,
+                                                 const char *hostSystemIpAddress)
 {
     int result = -1;
     esxVI_ManagedObjectReference *managedObjectReference = NULL;
@@ -1491,8 +1700,7 @@ esxVI_BuildSelectSetCollection(esxVI_Context *ctx)
     /* Folder -> childEntity (ManagedEntity) */
     if (esxVI_BuildSelectSet(&ctx->selectSet_folderToChildEntity,
                              "folderToChildEntity",
-                             "Folder", "childEntity",
-                             "folderToChildEntity\0") < 0) {
+                             "Folder", "childEntity", NULL) < 0) {
         return -1;
     }
 
@@ -1689,9 +1897,10 @@ esxVI_LookupObjectContentByType(esxVI_Context *ctx,
     objectSpec->obj = root;
     objectSpec->skip = esxVI_Boolean_False;
 
-    if (STRNEQ(root->type, type)) {
+    if (STRNEQ(root->type, type) || STREQ(root->type, "Folder")) {
         if (STREQ(root->type, "Folder")) {
-            if (STREQ(type, "Datacenter") || STREQ(type, "ComputeResource") ||
+            if (STREQ(type, "Folder") || STREQ(type, "Datacenter") ||
+                STREQ(type, "ComputeResource") ||
                 STREQ(type, "ClusterComputeResource")) {
                 objectSpec->selectSet = ctx->selectSet_folderToChildEntity;
             } else {
index 0b3f889b5ec81e668c010b10584445a230c8752f..78d3986096471547d8ebab57d7b38b1d5af4ef7a 100644 (file)
@@ -204,8 +204,11 @@ struct _esxVI_Context {
     esxVI_UserSession *session; /* ... except the session ... */
     virMutexPtr sessionLock; /* ... that is protected by this mutex */
     esxVI_Datacenter *datacenter;
+    char *datacenterPath; /* including folders */
     esxVI_ComputeResource *computeResource;
+    char *computeResourcePath; /* including folders */
     esxVI_HostSystem *hostSystem;
+    char *hostSystemName;
     esxVI_SelectionSpec *selectSet_folderToChildEntity;
     esxVI_SelectionSpec *selectSet_hostSystemToParent;
     esxVI_SelectionSpec *selectSet_hostSystemToVm;
@@ -221,10 +224,10 @@ void esxVI_Context_Free(esxVI_Context **ctx);
 int esxVI_Context_Connect(esxVI_Context *ctx, const char *ipAddress,
                           const char *url, const char *username,
                           const char *password, esxUtil_ParsedUri *parsedUri);
-int esxVI_Context_LookupObjectsByPath(esxVI_Context *ctx,
-                                      esxUtil_ParsedUri *parsedUri);
-int esxVI_Context_LookupObjectsByHostSystemIp(esxVI_Context *ctx,
-                                              const char *hostSystemIpAddress);
+int esxVI_Context_LookupManagedObjects(esxVI_Context *ctx);
+int esxVI_Context_LookupManagedObjectsByPath(esxVI_Context *ctx, const char *path);
+int esxVI_Context_LookupManagedObjectsByHostSystemIp(esxVI_Context *ctx,
+                                                     const char *hostSystemIpAddress);
 int esxVI_Context_Execute(esxVI_Context *ctx, const char *methodName,
                           const char *request, esxVI_Response **response,
                           esxVI_Occurrence occurrence);
index 361a6e70458163057b5272787d650a48c8105af1..1a67a8cadb3a10d291a18b2e9683922356b9ce8f 100644 (file)
@@ -755,6 +755,10 @@ managed object Datacenter            extends ManagedEntity
 end
 
 
+managed object Folder                extends ManagedEntity
+end
+
+
 managed object HostSystem            extends ManagedEntity
     HostConfigManager                        configManager                  r
 end