001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.action.hadoop; 020 021import java.io.IOException; 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.security.PrivilegedExceptionAction; 025import java.util.ArrayList; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.fs.FSDataOutputStream; 032import org.apache.hadoop.fs.FileStatus; 033import org.apache.hadoop.fs.FileSystem; 034import org.apache.hadoop.fs.FileUtil; 035import org.apache.hadoop.fs.Path; 036import org.apache.hadoop.fs.Trash; 037import org.apache.hadoop.fs.permission.FsPermission; 038import org.apache.hadoop.security.AccessControlException; 039import org.apache.hadoop.security.UserGroupInformation; 040import org.apache.oozie.action.ActionExecutor; 041import org.apache.oozie.action.ActionExecutorException; 042import org.apache.oozie.client.OozieClient; 043import org.apache.oozie.client.WorkflowAction; 044import org.apache.oozie.command.wf.WorkflowXCommand; 045import org.apache.oozie.dependency.FSURIHandler; 046import org.apache.oozie.dependency.URIHandler; 047import org.apache.oozie.service.ConfigurationService; 048import org.apache.oozie.service.HadoopAccessorException; 049import org.apache.oozie.service.HadoopAccessorService; 050import org.apache.oozie.service.Services; 051import org.apache.oozie.service.UserGroupInformationService; 052import org.apache.oozie.service.URIHandlerService; 053import org.apache.oozie.util.XConfiguration; 054import org.apache.oozie.util.XLog; 055import org.apache.oozie.util.XmlUtils; 056import org.jdom.Element; 057 058/** 059 * File system action executor. <p> This executes the file system mkdir, move and delete commands 060 */ 061public class FsActionExecutor extends ActionExecutor { 062 063 public static final String ACTION_TYPE = "fs"; 064 065 private final int maxGlobCount; 066 067 private final XLog LOG = XLog.getLog(getClass()); 068 069 public FsActionExecutor() { 070 super(ACTION_TYPE); 071 maxGlobCount = ConfigurationService.getInt(LauncherAMUtils.CONF_OOZIE_ACTION_FS_GLOB_MAX); 072 } 073 074 /** 075 * Initialize Action. 076 */ 077 @Override 078 public void initActionType() { 079 super.initActionType(); 080 registerError(AccessControlException.class.getName(), ActionExecutorException.ErrorType.ERROR, "FS014"); 081 } 082 083 Path getPath(Element element, String attribute) { 084 String str = element.getAttributeValue(attribute).trim(); 085 return new Path(str); 086 } 087 088 void validatePath(Path path, boolean withScheme) throws ActionExecutorException { 089 try { 090 String scheme = path.toUri().getScheme(); 091 if (withScheme) { 092 if (scheme == null) { 093 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS001", 094 "Missing scheme in path [{0}]", path); 095 } 096 else { 097 Services.get().get(HadoopAccessorService.class).checkSupportedFilesystem(path.toUri()); 098 } 099 } 100 else { 101 if (scheme != null) { 102 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS002", 103 "Scheme [{0}] not allowed in path [{1}]", scheme, path); 104 } 105 } 106 } 107 catch (HadoopAccessorException hex) { 108 throw convertException(hex); 109 } 110 } 111 112 Path resolveToFullPath(Path nameNode, Path path, boolean withScheme) throws ActionExecutorException { 113 Path fullPath; 114 115 // If no nameNode is given, validate the path as-is and return it as-is 116 if (nameNode == null) { 117 validatePath(path, withScheme); 118 fullPath = path; 119 } else { 120 // If the path doesn't have a scheme or authority, use the nameNode which should have already been verified earlier 121 String pathScheme = path.toUri().getScheme(); 122 String pathAuthority = path.toUri().getAuthority(); 123 if (pathScheme == null || pathAuthority == null) { 124 if (path.isAbsolute()) { 125 String nameNodeSchemeAuthority = nameNode.toUri().getScheme() + "://" + nameNode.toUri().getAuthority(); 126 fullPath = new Path(nameNodeSchemeAuthority + path.toString()); 127 } else { 128 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS011", 129 "Path [{0}] cannot be relative", path); 130 } 131 } else { 132 // If the path has a scheme and authority, but its not the nameNode then validate the path as-is and return it as-is 133 // If it is the nameNode, then it should have already been verified earlier so return it as-is 134 if (!nameNode.toUri().getScheme().equals(pathScheme) || !nameNode.toUri().getAuthority().equals(pathAuthority)) { 135 validatePath(path, withScheme); 136 } 137 fullPath = path; 138 } 139 } 140 return fullPath; 141 } 142 143 void validateSameNN(Path source, Path dest) throws ActionExecutorException { 144 Path destPath = new Path(source, dest); 145 String t = destPath.toUri().getScheme() + destPath.toUri().getAuthority(); 146 String s = source.toUri().getScheme() + source.toUri().getAuthority(); 147 148 //checking whether NN prefix of source and target is same. can modify this to adjust for a set of multiple whitelisted NN 149 if(!t.equals(s)) { 150 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS007", 151 "move, target NN URI different from that of source", dest); 152 } 153 } 154 155 @SuppressWarnings("unchecked") 156 void doOperations(Context context, Element element) throws ActionExecutorException { 157 try { 158 FileSystem fs = context.getAppFileSystem(); 159 boolean recovery = fs.exists(getRecoveryPath(context)); 160 if (!recovery) { 161 fs.mkdirs(getRecoveryPath(context)); 162 } 163 164 Path nameNodePath = null; 165 Element nameNodeElement = element.getChild("name-node", element.getNamespace()); 166 if (nameNodeElement != null) { 167 String nameNode = nameNodeElement.getTextTrim(); 168 if (nameNode != null) { 169 nameNodePath = new Path(nameNode); 170 // Verify the name node now 171 validatePath(nameNodePath, true); 172 } 173 } 174 175 XConfiguration fsConf = new XConfiguration(); 176 Path appPath = new Path(context.getWorkflow().getAppPath()); 177 // app path could be a file 178 if (fs.isFile(appPath)) { 179 appPath = appPath.getParent(); 180 } 181 JavaActionExecutor.parseJobXmlAndConfiguration(context, element, appPath, fsConf); 182 183 for (Element commandElement : (List<Element>) element.getChildren()) { 184 String command = commandElement.getName(); 185 if (command.equals("mkdir")) { 186 Path path = getPath(commandElement, "path"); 187 mkdir(context, fsConf, nameNodePath, path); 188 } 189 else { 190 if (command.equals("delete")) { 191 Path path = getPath(commandElement, "path"); 192 boolean skipTrash = true; 193 if (commandElement.getAttributeValue("skip-trash") != null && 194 commandElement.getAttributeValue("skip-trash").equals("false")) { 195 skipTrash = false; 196 } 197 delete(context, fsConf, nameNodePath, path, skipTrash); 198 } 199 else { 200 if (command.equals("move")) { 201 Path source = getPath(commandElement, "source"); 202 Path target = getPath(commandElement, "target"); 203 move(context, fsConf, nameNodePath, source, target, recovery); 204 } 205 else { 206 if (command.equals("chmod")) { 207 Path path = getPath(commandElement, "path"); 208 boolean recursive = commandElement.getChild("recursive", commandElement.getNamespace()) != null; 209 String str = commandElement.getAttributeValue("dir-files"); 210 boolean dirFiles = (str == null) || Boolean.parseBoolean(str); 211 String permissionsMask = commandElement.getAttributeValue("permissions").trim(); 212 chmod(context, fsConf, nameNodePath, path, permissionsMask, dirFiles, recursive); 213 } 214 else { 215 if (command.equals("touchz")) { 216 Path path = getPath(commandElement, "path"); 217 touchz(context, fsConf, nameNodePath, path); 218 } 219 else { 220 if (command.equals("chgrp")) { 221 Path path = getPath(commandElement, "path"); 222 boolean recursive = commandElement.getChild("recursive", 223 commandElement.getNamespace()) != null; 224 String group = commandElement.getAttributeValue("group"); 225 String str = commandElement.getAttributeValue("dir-files"); 226 boolean dirFiles = (str == null) || Boolean.parseBoolean(str); 227 chgrp(context, fsConf, nameNodePath, path, context.getWorkflow().getUser(), 228 group, dirFiles, recursive); 229 } 230 else { 231 if (command.equals("setrep")) { 232 Path path = getPath(commandElement, "path"); 233 String replicationFactor = 234 commandElement.getAttributeValue("replication-factor"); 235 if (commandElement.getAttributeValue("replication-factor") != null) { 236 setrep(context, path, Short.parseShort(replicationFactor)); 237 } 238 } 239 } 240 } 241 } 242 } 243 } 244 } 245 } 246 } 247 catch (Exception ex) { 248 throw convertException(ex); 249 } 250 } 251 252 void chgrp(Context context, XConfiguration fsConf, Path nameNodePath, Path path, String user, String group, 253 boolean dirFiles, boolean recursive) throws ActionExecutorException { 254 255 LOG.info("Setting ownership for [{0}] to group: [{1}], user: [{2}]. Recursive mode: [{3}]", path, group, user, recursive); 256 HashMap<String, String> argsMap = new HashMap<String, String>(); 257 argsMap.put("user", user); 258 argsMap.put("group", group); 259 try { 260 FileSystem fs = getFileSystemFor(path, context, fsConf); 261 path = resolveToFullPath(nameNodePath, path, true); 262 Path[] pathArr = FileUtil.stat2Paths(fs.globStatus(path)); 263 if (pathArr == null || pathArr.length == 0) { 264 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS009", "chgrp" 265 + ", path(s) that matches [{0}] does not exist", path); 266 } 267 checkGlobMax(pathArr); 268 for (Path p : pathArr) { 269 recursiveFsOperation("chgrp", fs, nameNodePath, p, argsMap, dirFiles, recursive, true); 270 } 271 } 272 catch (Exception ex) { 273 throw convertException(ex); 274 } 275 } 276 277 private void recursiveFsOperation(String op, FileSystem fs, Path nameNodePath, Path path, 278 Map<String, String> argsMap, boolean dirFiles, boolean recursive, boolean isRoot) 279 throws ActionExecutorException { 280 281 try { 282 FileStatus pathStatus = fs.getFileStatus(path); 283 List<Path> paths = new ArrayList<Path>(); 284 285 if (dirFiles && pathStatus.isDirectory()) { 286 if (isRoot) { 287 paths.add(path); 288 } 289 FileStatus[] filesStatus = fs.listStatus(path); 290 for (int i = 0; i < filesStatus.length; i++) { 291 Path p = filesStatus[i].getPath(); 292 paths.add(p); 293 if (recursive && filesStatus[i].isDirectory()) { 294 recursiveFsOperation(op, fs, null, p, argsMap, dirFiles, recursive, false); 295 } 296 } 297 } 298 else { 299 paths.add(path); 300 } 301 for (Path p : paths) { 302 doFsOperation(op, fs, p, argsMap); 303 } 304 } 305 catch (Exception ex) { 306 throw convertException(ex); 307 } 308 } 309 310 private void doFsOperation(String op, FileSystem fs, Path p, Map<String, String> argsMap) 311 throws ActionExecutorException, IOException { 312 if (op.equals("chmod")) { 313 String permissions = argsMap.get("permissions"); 314 FsPermission newFsPermission = createShortPermission(permissions, p); 315 fs.setPermission(p, newFsPermission); 316 } 317 else if (op.equals("chgrp")) { 318 String user = argsMap.get("user"); 319 String group = argsMap.get("group"); 320 fs.setOwner(p, user, group); 321 } 322 } 323 324 /** 325 * @param path 326 * @param context 327 * @param fsConf 328 * @return FileSystem 329 * @throws HadoopAccessorException 330 */ 331 private FileSystem getFileSystemFor(Path path, Context context, XConfiguration fsConf) throws HadoopAccessorException { 332 String user = context.getWorkflow().getUser(); 333 HadoopAccessorService has = Services.get().get(HadoopAccessorService.class); 334 Configuration conf = has.createConfiguration(path.toUri().getAuthority()); 335 XConfiguration.copy(context.getProtoActionConf(), conf); 336 if (fsConf != null) { 337 XConfiguration.copy(fsConf, conf); 338 } 339 return has.createFileSystem(user, path.toUri(), conf); 340 } 341 342 /** 343 * @param path 344 * @param user 345 * @return FileSystem 346 * @throws HadoopAccessorException 347 */ 348 private FileSystem getFileSystemFor(Path path, String user) throws HadoopAccessorException { 349 HadoopAccessorService has = Services.get().get(HadoopAccessorService.class); 350 Configuration jobConf = has.createConfiguration(path.toUri().getAuthority()); 351 return has.createFileSystem(user, path.toUri(), jobConf); 352 } 353 354 void mkdir(Context context, Path path) throws ActionExecutorException { 355 mkdir(context, null, null, path); 356 } 357 358 void mkdir(Context context, XConfiguration fsConf, Path nameNodePath, Path path) throws ActionExecutorException { 359 LOG.info("Creating directory [{0}]", path); 360 try { 361 path = resolveToFullPath(nameNodePath, path, true); 362 FileSystem fs = getFileSystemFor(path, context, fsConf); 363 364 if (!fs.exists(path)) { 365 if (!fs.mkdirs(path)) { 366 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS004", 367 "mkdir, path [{0}] could not create directory", path); 368 } 369 } else { 370 LOG.info("[{0}] already exist, no need for creation", path); 371 } 372 } 373 catch (Exception ex) { 374 throw convertException(ex); 375 } 376 } 377 378 /** 379 * Delete path 380 * 381 * @param context 382 * @param path 383 * @throws ActionExecutorException 384 */ 385 public void delete(Context context, Path path) throws ActionExecutorException { 386 delete(context, null, null, path, true); 387 } 388 389 /** 390 * Delete path 391 * 392 * @param context 393 * @param fsConf 394 * @param nameNodePath 395 * @param path 396 * @param skipTrash flag to skip the trash. 397 * @throws ActionExecutorException 398 */ 399 public void delete(Context context, XConfiguration fsConf, Path nameNodePath, Path path, boolean skipTrash) 400 throws ActionExecutorException { 401 LOG.info("Deleting [{0}]. Skipping trash: [{1}]", path, skipTrash); 402 URI uri = path.toUri(); 403 URIHandler handler; 404 org.apache.oozie.dependency.URIHandler.Context hcatContext = null; 405 try { 406 handler = Services.get().get(URIHandlerService.class).getURIHandler(uri); 407 if (handler instanceof FSURIHandler) { 408 // Use legacy code to handle hdfs partition deletion 409 path = resolveToFullPath(nameNodePath, path, true); 410 final FileSystem fs = getFileSystemFor(path, context, fsConf); 411 Path[] pathArr = FileUtil.stat2Paths(fs.globStatus(path)); 412 if (pathArr != null && pathArr.length > 0) { 413 checkGlobMax(pathArr); 414 for (final Path p : pathArr) { 415 if (fs.exists(p)) { 416 if (!skipTrash) { 417 // Moving directory/file to trash of user. 418 UserGroupInformationService ugiService = Services.get().get(UserGroupInformationService.class); 419 UserGroupInformation ugi = ugiService.getProxyUser(fs.getConf().get(OozieClient.USER_NAME)); 420 ugi.doAs(new PrivilegedExceptionAction<FileSystem>() { 421 @Override 422 public FileSystem run() throws Exception { 423 Trash trash = new Trash(fs.getConf()); 424 if (!trash.moveToTrash(p)) { 425 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS005", 426 "Could not move path [{0}] to trash on delete", p); 427 } 428 return null; 429 } 430 }); 431 } 432 else if (!fs.delete(p, true)) { 433 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS005", 434 "delete, path [{0}] could not delete path", p); 435 } 436 } 437 } 438 } 439 } else { 440 hcatContext = handler.getContext(uri, fsConf, context.getWorkflow().getUser(), false); 441 handler.delete(uri, hcatContext); 442 } 443 } 444 catch (Exception ex) { 445 throw convertException(ex); 446 } 447 finally{ 448 if (hcatContext != null) { 449 hcatContext.destroy(); 450 } 451 } 452 } 453 454 /** 455 * Delete path 456 * 457 * @param user 458 * @param group 459 * @param path 460 * @throws ActionExecutorException 461 */ 462 public void delete(String user, String group, Path path) throws ActionExecutorException { 463 try { 464 validatePath(path, true); 465 FileSystem fs = getFileSystemFor(path, user); 466 467 if (fs.exists(path)) { 468 if (!fs.delete(path, true)) { 469 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS005", 470 "delete, path [{0}] could not delete path", path); 471 } 472 } 473 } 474 catch (Exception ex) { 475 throw convertException(ex); 476 } 477 } 478 479 /** 480 * Move source to target 481 * 482 * @param context 483 * @param source 484 * @param target 485 * @param recovery 486 * @throws ActionExecutorException 487 */ 488 public void move(Context context, Path source, Path target, boolean recovery) throws ActionExecutorException { 489 move(context, null, null, source, target, recovery); 490 } 491 492 /** 493 * Move source to target 494 * 495 * @param context 496 * @param fsConf 497 * @param nameNodePath 498 * @param source 499 * @param target 500 * @param recovery 501 * @throws ActionExecutorException 502 */ 503 public void move(Context context, XConfiguration fsConf, Path nameNodePath, Path source, Path target, boolean recovery) 504 throws ActionExecutorException { 505 LOG.info("Moving [{0}] to [{1}]", source, target); 506 try { 507 source = resolveToFullPath(nameNodePath, source, true); 508 validateSameNN(source, target); 509 FileSystem fs = getFileSystemFor(source, context, fsConf); 510 Path[] pathArr = FileUtil.stat2Paths(fs.globStatus(source)); 511 if (( pathArr == null || pathArr.length == 0 ) ){ 512 if (!recovery) { 513 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS006", 514 "move, source path [{0}] does not exist", source); 515 } else { 516 return; 517 } 518 } 519 if (pathArr.length > 1 && (!fs.exists(target) || fs.isFile(target))) { 520 if(!recovery) { 521 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS012", 522 "move, could not rename multiple sources to the same target name"); 523 } else { 524 return; 525 } 526 } 527 checkGlobMax(pathArr); 528 for (Path p : pathArr) { 529 if (!fs.rename(p, target) && !recovery) { 530 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS008", 531 "move, could not move [{0}] to [{1}]", p, target); 532 } 533 } 534 } 535 catch (Exception ex) { 536 throw convertException(ex); 537 } 538 } 539 540 void chmod(Context context, Path path, String permissions, boolean dirFiles, boolean recursive) throws ActionExecutorException { 541 chmod(context, null, null, path, permissions, dirFiles, recursive); 542 } 543 544 void chmod(Context context, XConfiguration fsConf, Path nameNodePath, Path path, String permissions, 545 boolean dirFiles, boolean recursive) throws ActionExecutorException { 546 547 LOG.info("Setting permissions [{0}] on [{1}]. Recursive mode: [{2}]", permissions, path, recursive); 548 HashMap<String, String> argsMap = new HashMap<String, String>(); 549 argsMap.put("permissions", permissions); 550 try { 551 FileSystem fs = getFileSystemFor(path, context, fsConf); 552 path = resolveToFullPath(nameNodePath, path, true); 553 Path[] pathArr = FileUtil.stat2Paths(fs.globStatus(path)); 554 if (pathArr == null || pathArr.length == 0) { 555 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS009", "chmod" 556 + ", path(s) that matches [{0}] does not exist", path); 557 } 558 checkGlobMax(pathArr); 559 for (Path p : pathArr) { 560 recursiveFsOperation("chmod", fs, nameNodePath, p, argsMap, dirFiles, recursive, true); 561 } 562 563 } 564 catch (Exception ex) { 565 throw convertException(ex); 566 } 567 } 568 569 void touchz(Context context, Path path) throws ActionExecutorException { 570 touchz(context, null, null, path); 571 } 572 573 void touchz(Context context, XConfiguration fsConf, Path nameNodePath, Path path) throws ActionExecutorException { 574 575 LOG.info ("Performing touch on [{0}]", path); 576 try { 577 path = resolveToFullPath(nameNodePath, path, true); 578 FileSystem fs = getFileSystemFor(path, context, fsConf); 579 580 FileStatus st; 581 if (fs.exists(path)) { 582 st = fs.getFileStatus(path); 583 if (st.isDirectory()) { 584 throw new Exception(path.toString() + " is a directory"); 585 } else if (st.getLen() != 0) { 586 throw new Exception(path.toString() + " must be a zero-length file"); 587 } 588 } 589 FSDataOutputStream out = fs.create(path); 590 out.close(); 591 } 592 catch (Exception ex) { 593 throw convertException(ex); 594 } 595 } 596 597 FsPermission createShortPermission(String permissions, Path path) throws ActionExecutorException { 598 if (permissions.length() == 3) { 599 char user = permissions.charAt(0); 600 char group = permissions.charAt(1); 601 char other = permissions.charAt(2); 602 int useri = user - '0'; 603 int groupi = group - '0'; 604 int otheri = other - '0'; 605 int mask = useri * 100 + groupi * 10 + otheri; 606 short omask = Short.parseShort(Integer.toString(mask), 8); 607 return new FsPermission(omask); 608 } 609 else { 610 if (permissions.length() == 10) { 611 return FsPermission.valueOf(permissions); 612 } 613 else { 614 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS010", 615 "chmod, path [{0}] invalid permissions mask [{1}]", path, permissions); 616 } 617 } 618 } 619 620 @Override 621 public void check(Context context, WorkflowAction action) throws ActionExecutorException { 622 } 623 624 @Override 625 public void kill(Context context, WorkflowAction action) throws ActionExecutorException { 626 } 627 628 @Override 629 public void start(Context context, WorkflowAction action) throws ActionExecutorException { 630 LOG.info("Starting action"); 631 try { 632 context.setStartData("-", "-", "-"); 633 Element actionXml = XmlUtils.parseXml(action.getConf()); 634 doOperations(context, actionXml); 635 context.setExecutionData("OK", null); 636 } 637 catch (Exception ex) { 638 throw convertException(ex); 639 } 640 } 641 642 @Override 643 public void end(Context context, WorkflowAction action) throws ActionExecutorException { 644 String externalStatus = action.getExternalStatus(); 645 WorkflowAction.Status status = externalStatus.equals("OK") ? WorkflowAction.Status.OK : 646 WorkflowAction.Status.ERROR; 647 context.setEndData(status, getActionSignal(status)); 648 if (!context.getProtoActionConf().getBoolean(WorkflowXCommand.KEEP_WF_ACTION_DIR, false)) { 649 try { 650 FileSystem fs = context.getAppFileSystem(); 651 fs.delete(context.getActionDir(), true); 652 } 653 catch (Exception ex) { 654 throw convertException(ex); 655 } 656 } 657 LOG.info("Action ended with external status [{0}]", action.getExternalStatus()); 658 } 659 660 @Override 661 public boolean isCompleted(String externalStatus) { 662 return true; 663 } 664 665 /** 666 * @param context 667 * @return Path returns recovery path 668 * @throws HadoopAccessorException 669 * @throws IOException 670 * @throws URISyntaxException 671 */ 672 public Path getRecoveryPath(Context context) throws HadoopAccessorException, IOException, URISyntaxException { 673 return new Path(context.getActionDir(), "fs-" + context.getRecoveryId()); 674 } 675 676 private void checkGlobMax(Path[] pathArr) throws ActionExecutorException { 677 if(pathArr.length > maxGlobCount) { 678 throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "FS013", 679 "too many globbed files/dirs to do FS operation"); 680 } 681 } 682 683 void setrep(Context context, Path path, short replicationFactor) 684 throws ActionExecutorException, HadoopAccessorException { 685 LOG.info("Setting replication factor: [{0}] for [{1}]", replicationFactor, path); 686 try { 687 path = resolveToFullPath(null, path, true); 688 FileSystem fs = getFileSystemFor(path, context, null); 689 690 if (fs.isFile(path)) { 691 fs.setReplication(path, replicationFactor); 692 } 693 } catch (IOException ex) { 694 convertException(ex); 695 } 696 } 697 698 public boolean supportsConfigurationJobXML() { 699 return true; 700 } 701}