// This not just a copy operation since the config manager takes care of construction the znode path to configsets public static async Task upConfig(SolrZkClient zkClient, string confPath, string confName, CancellationToken token) { ZkConfigManager manager = new ZkConfigManager(zkClient); // Try to download the configset await manager.uploadConfigDir(confPath, confName, token); }
/// <summary> /// Delete a path and all of its sub nodes /// yeah, it's recursive :( /// </summary> /// <param name="zkClient"></param> /// <param name="path"></param> /// <param name="filter">For node to be deleted</param> /// <returns></returns> public static async Task clean(SolrZkClient zkClient, string path, CancellationToken token, Predicate <string> filter = null) { var paths = new List <string>(); await traverseZkTree(zkClient, path, VISIT_ORDER.VISIT_POST, znode => { if (!znode.Equals("/") && (filter?.Invoke(znode) ?? true)) { paths.Add(znode); } }); foreach (var subpath in paths.OrderByDescending(s => s.Length)) { if (!subpath.Equals("/")) { try { token.ThrowIfCancellationRequested(); await zkClient.delete(subpath, -1, true); } catch (Exception ex) { if (ex is KeeperException.NotEmptyException || ex is KeeperException.NoNodeException) { //expected } else { throw; } } } } }
public static async Task moveZnode(SolrZkClient zkClient, string src, string dst, CancellationToken token) { String destName = normalizeDest(src, dst, true, true); // Special handling if the source has no children, i.e. copying just a single file. if (!(await zkClient.getChildren(src, null, true)).Any()) { await zkClient.makePath(destName, false, true); await zkClient.setData(destName, await zkClient.getData(src, null, null, true), true); } else { await traverseZkTree(zkClient, src, VISIT_ORDER.VISIT_PRE, async path => { var finalDestination = dst; if (path.Equals(src) == false) { finalDestination += "/" + path.Substring(src.Length + 1); } await zkClient.makePath(finalDestination, false, true); await zkClient.setData(finalDestination, await zkClient.getData(path, null, null, true), true); }); } // Insure all source znodes are present in dest before deleting the source. // throws error if not all there so the source is left intact. Throws error if source and dest don't match. await checkAllZnodesThere(zkClient, src, destName); await clean(zkClient, src, token); }
public static async Task uploadToZK(SolrZkClient zkClient, string fromPath, string zkPath, Regex filenameExclusions, CancellationToken token) { var path = fromPath; if (path.EndsWith("*")) { path = path.Substring(0, path.Length - 1); } var rootPath = path; if (!Directory.Exists(rootPath)) { if (!File.Exists(rootPath)) { throw new IOException("Path " + rootPath + " does not exist"); } var filePath = path; token.ThrowIfCancellationRequested(); await uploadFileToZk(zkClient, zkPath, filePath, filenameExclusions); } WalkFileTree(rootPath, async(file) => { var zkNode = createZkNodeName(zkPath, rootPath, file); await uploadFileToZk(zkClient, zkNode, file, filenameExclusions); }, token); }
private static async Task uploadFileToZk(SolrZkClient zkClient, string zkNode, string file, Regex filenameExclusions) { var fielName = Path.GetFileName(file); if (filenameExclusions != null && filenameExclusions.Match(fielName ?? throw new InvalidOperationException("File name is empty")).Success) { //TODO: Log here //log.info("uploadToZK skipping '{}' due to filenameExclusions '{}'", filename, filenameExclusions); return; } try { // if the path exists (and presumably we're uploading data to it) just set its data if (Path.GetFileName(file).Equals(ZKNODE_DATA_FILE) && (await zkClient.exists(zkNode, true))) { await zkClient.setData(zkNode, file, true); } else { //Can't work async because it will try to create same path zkClient.makePath(zkNode, file, false, true).Wait(); } } catch (KeeperException ex) { throw new Exception("Error uploading file " + file + " to zookeeper path " + zkNode, SolrZkClient.checkInterrupted(ex)); } }
public static async Task downloadFromZK(SolrZkClient zkClient, string zkPath, string filePath, CancellationToken token) { try { token.ThrowIfCancellationRequested(); var children = await zkClient.getChildren(zkPath, null, true); // If it has no children, it's a leaf node, write the associated data from the ZNode. // Otherwise, continue recursing, but write the associated data to a special file if any if (!children.Any()) { // If we didn't copy data down, then we also didn't create the file. But we still need a marker on the local // disk so create an empty file. if (await copyDataDown(zkClient, zkPath, filePath) == 0) { Debug.WriteLine("Download: " + filePath); File.Create(filePath); } } else { Directory.CreateDirectory(filePath); // Make parent dir. // ZK nodes, whether leaf or not can have data. If it's a non-leaf node and // has associated data write it into the special file. token.ThrowIfCancellationRequested(); Debug.WriteLine("Download: " + filePath); await copyDataDown(zkClient, zkPath, Path.Combine(filePath, ZKNODE_DATA_FILE)); foreach (var child in children) { var zkChild = zkPath; if (zkChild.EndsWith("/") == false) { zkChild += "/"; } zkChild += child; if (await isEphemeral(zkClient, zkChild)) { // Don't copy ephemeral nodes continue; } // Go deeper into the tree now await downloadFromZK(zkClient, zkChild, Path.Combine(filePath, child), token); } } } catch (Exception ex) { if (ex is KeeperException || ex is ThreadInterruptedException) { throw new IOException("Error downloading files from zookeeper path " + zkPath + " to " + filePath, SolrZkClient.checkInterrupted(ex)); } throw; } }
// Insure that all the nodes in one path match the nodes in the other as a safety check before removing // the source in a 'mv' command. private static async Task checkAllZnodesThere(SolrZkClient zkClient, string src, string dst) { foreach (var node in await zkClient.getChildren(src, null, true)) { if (await zkClient.exists(dst + "/" + node, true) == false) { throw new Exception("mv command did not move node " + dst + "/" + node + " source left intact"); } await checkAllZnodesThere(zkClient, src + "/" + node, dst + "/" + node); } }
public static async Task <int> copyDataDown(SolrZkClient zkClient, string zkPath, string filePath) { var data = await zkClient.getData(zkPath, null, null, true); if (data == null || data.Length <= 1) { return(0); // There are apparently basically empty ZNodes. } //TODO: Log here //log.info("Writing file {}", file.toString()); File.WriteAllBytes(filePath, data); return(data.Length); }
//TODO: Check if works #region CHECK IF WORKS /// <summary> /// Lists a ZNode child and (optionally) the znodes of all the children. No data is dumped /// </summary> /// <param name="zkClient"></param> /// <param name="path">The node to remove on Zookeeper</param> /// <param name="recurse">Whether to remove children</param> /// <returns>An indented list of the znodes suitable for display</returns> /// <exception cref="KeeperException"> Could not perform the Zookeeper operation</exception> public static async Task <string> listZnode(SolrZkClient zkClient, string path, bool recurse) { var root = path; if (path.ToLower(CultureInfo.InvariantCulture).StartsWith("zk:")) { root = path.Substring(3); } if (root.Equals("/") == false && root.EndsWith("/")) { root = root.Substring(0, root.Length - 1); } var sb = new StringBuilder(); if (recurse == false) { foreach (var node in await zkClient.getChildren(root, null, true)) { if (node.Equals("zookeeper") == false) { sb.Append(node).Append(Environment.NewLine); } } return(sb.ToString()); } await traverseZkTree(zkClient, root, VISIT_ORDER.VISIT_PRE, znode => { if (znode.StartsWith("/zookeeper")) { return; // can't do anything with this node! } int iPos = znode.LastIndexOf("/"); if (iPos > 0) { for (int idx = 0; idx < iPos; ++idx) { sb.Append(" "); } sb.Append(znode.Substring(iPos + 1)).Append(Environment.NewLine); } else { sb.Append(znode).Append(Environment.NewLine); } }); return(sb.ToString()); }
/// <summary> /// Check whether a config exists in Zookeeper /// </summary> /// <param name="configName">The config to check existance on</param> /// <returns>Whether the config exists or not</returns> /// <exception cref="IOException">If an I/O error occurs</exception> public async Task <bool> configExists(string configName) { try { return(await zkClient.exists(ConfigsZKnode + "/" + configName, true)); } catch (Exception ex) { if (ex is KeeperException || ex is ThreadInterruptedException) { throw new IOException("Error checking whether config exists", SolrZkClient.checkInterrupted(ex)); } throw; } }
/// <summary> /// Delete a config in ZooKeeper /// </summary> /// <param name="configName">The config to delete</param> /// <param name="token"></param> /// <exception cref="IOException">If an I/O error occurs</exception> public async Task deleteConfigDir(string configName, CancellationToken token) { try { await zkClient.clean(ConfigsZKnode + "/" + configName, token); } catch (Exception ex) { if (ex is KeeperException || ex is ThreadInterruptedException) { throw new IOException("Error checking whether config exists", SolrZkClient.checkInterrupted(ex)); } throw; } }
public async Task <List <string> > listConfigs() { try { return(await zkClient.getChildren(ConfigsZKnode, null, true)); } catch (KeeperException.NoNodeException) { return(new List <string>()); } catch (Exception ex) { if (ex is KeeperException || ex is ThreadInterruptedException) { throw new IOException("Error listing configs", SolrZkClient.checkInterrupted(ex)); } throw; } }
public static async Task <IEnumerable <string> > GetTree(SolrZkClient zkCnxn, string zooPath = "/") { var children = (await zkCnxn.getChildren(zooPath, null, false)); var nodes = new List <string>(); if (!children.Any()) { return(nodes); } if (zooPath.Last() != '/') { zooPath += "/"; } foreach (var child in children) { nodes.Add(zooPath + child); nodes.AddRange(await GetTree(zkCnxn, zooPath + child)); } return(nodes); }
/// <summary> /// Recursively visit a zk tree rooted at path and apply the given visitor to each path. Exists as a separate method /// because some of the logic can get nuanced. /// </summary> /// <param name="zkClient"></param> /// <param name="path">The path to start from</param> /// <param name="visitOrder">Whether to call the visitor at the at the ending or beginning of the run.</param> /// <param name="visitor">The operation to perform on each path</param> public static async Task traverseZkTree(SolrZkClient zkClient, string path, VISIT_ORDER visitOrder, Action <string> visitor) { if (visitOrder == VISIT_ORDER.VISIT_PRE) { visitor.Invoke(path); } List <string> children; try { children = await zkClient.getChildren(path, null, true); } catch (KeeperException.NoNodeException) { return; } foreach (var child in children) { // we can't do anything to the built-in zookeeper node if (path.Equals("/") && child.Equals("zookeeper")) { continue; } if (path.StartsWith("/zookeeper")) { continue; } if (path.Equals("/")) { await traverseZkTree(zkClient, path + child, visitOrder, visitor); } else { await traverseZkTree(zkClient, path + "/" + child, visitOrder, visitor); } } if (visitOrder == VISIT_ORDER.VISIT_POST) { visitor.Invoke(path); } }
public async void EnsureExists(string path, byte[] data, CreateMode createMode, SolrZkClient zkClient, int skipPathParts) { if (await zkClient.exists(path, true)) { return; } try { await zkClient.makePath(path, data, createMode, null, true, true, skipPathParts); } catch (KeeperException.NodeExistsException ex) { // it's okay if another beats us creating the node } }
public void EnsureExists(string path, byte[] data, CreateMode createMode, SolrZkClient zkClient) { EnsureExists(path, data, createMode, zkClient, 0); }
public void EnsureExists(string path, byte[] data, SolrZkClient zkClient) { EnsureExists(path, data, CreateMode.PERSISTENT, zkClient, 0); }
public void EnsureExists(string path, SolrZkClient zkClient) { EnsureExists(path, null, CreateMode.PERSISTENT, zkClient, 0); }
/// <summary> /// Copy between local file system and Zookeeper, or from one Zookeeper node to another, optionally copying recursively. /// </summary> /// <param name="zkClient"></param> /// <param name="src">Source to copy from. Both src and dst may be Znodes. However, both may NOT be local</param> /// <param name="srcIsZk"></param> /// <param name="dst">The place to copy the files too. Both src and dst may be Znodes. However both may NOT be local</param> /// <param name="dstIsZk"></param> /// <param name="recurse">If the source is a directory, reccursively copy the contents iff this is true.</param> /// <exception cref="ArgumentException">Explanatory exception due to bad params, failed operation, etc.</exception> public static async Task zkTransfer(SolrZkClient zkClient, string src, bool srcIsZk, string dst, bool dstIsZk, bool recurse, CancellationToken token) { if (srcIsZk == false && dstIsZk == false) { throw new Exception("One or both of source or destination must specify ZK nodes."); } // Make sure -recurse is specified if the source has children. if (recurse == false) { if (srcIsZk) { if ((await zkClient.getChildren(src, null, true)).Any()) { throw new ArgumentException("Zookeeper node " + src + " has children and recurse is false"); } } else if (IsDirectory(src)) { throw new ArgumentException("Local path " + src + " is a directory and recurse is false"); } } if (dstIsZk && dst.Length == 0) { dst = "/"; // for consistency, one can copy from zk: and send to zk:/ } dst = normalizeDest(src, dst, srcIsZk, dstIsZk); // ZK -> ZK copy. if (srcIsZk && dstIsZk) { await traverseZkTree(zkClient, src, VISIT_ORDER.VISIT_PRE, async path => { var finalDestination = dst; if (path.Equals(src) == false) { finalDestination += "/" + path.Substring(src.Length + 1); } await zkClient.makePath(finalDestination, false, true); await zkClient.setData(finalDestination, await zkClient.getData(path, null, null, true), true); }); return; } //local -> ZK copy if (dstIsZk) { await uploadToZK(zkClient, src, dst, null, token); return; } // Copying individual files from ZK requires special handling since downloadFromZK assumes the node has children. // This is kind of a weak test for the notion of "directory" on Zookeeper. // ZK -> local copy where ZK is a parent node if ((await zkClient.getChildren(src, null, true)).Any()) { await downloadFromZK(zkClient, src, dst, token); return; } // Single file ZK -> local copy where ZK is a leaf node if (IsDirectory(dst)) { if (dst.EndsWith(Path.DirectorySeparatorChar.ToString()) == false) { dst += Path.DirectorySeparatorChar; } dst = normalizeDest(src, dst, srcIsZk, dstIsZk); } byte[] data = await zkClient.getData(src, null, null, true); Directory.CreateDirectory(Path.GetDirectoryName(dst)); //TODO: Log here //log.info("Writing file {}", filename); File.WriteAllBytes(dst, data); }
/// <summary> /// Creates a new ZkConfigManager /// </summary> /// <param name="zkClient">zkClient the SolrZkClient to use</param> public ZkConfigManager(SolrZkClient zkClient) { this.zkClient = zkClient; }
public static async Task <bool> isEphemeral(SolrZkClient zkClient, string zkPath) { var znodeStat = await zkClient.exists(zkPath, null, true); return(znodeStat.getEphemeralOwner() != 0); }
private async Task copyConfigDirFromZk(string fromZkPath, string toZkPath, ISet <string> copiedToZkPaths = null) { try { var files = await zkClient.getChildren(fromZkPath, null, true); foreach (var file in files) { var children = await zkClient.getChildren(fromZkPath + "/" + file, null, true); if (!children.Any()) { var toZkFilePath = toZkPath + "/" + file; //TODO: Log here //logger.info("Copying zk node {} to {}",fromZkPath + "/" + file, toZkFilePath); var data = await zkClient.getData(fromZkPath + "/" + file, null, null, true); //Take care it fails on Exists await zkClient.makePath(toZkFilePath, data, true); copiedToZkPaths?.Add(toZkFilePath); } else { await copyConfigDirFromZk(fromZkPath + "/" + file, toZkPath + "/" + file, copiedToZkPaths); } } } catch (Exception ex) { if (ex is KeeperException || ex is ThreadInterruptedException) { throw new IOException("Error copying nodes from zookeeper path " + fromZkPath + " to " + toZkPath, SolrZkClient.checkInterrupted(ex)); } throw; } }