diff --git a/cccli/command/command.py b/cccli/command/command.py
index 90f42ad6168c57102ef103cfce9e37542382ddf8..61bafac527e16f12fdaa8c6bbeaf69de7d635118 100644
--- a/cccli/command/command.py
+++ b/cccli/command/command.py
@@ -127,6 +127,7 @@ class TqlCommand(OptionCommand):
         _tql_index: is index in args where filter should be appended (def: 1)
         _tql_print: print tql with filter
         _exception: catch or not RPCError exception
+        _direct: call directly, no listing before
         '''
 
         # set rpccall default option value
diff --git a/cccli/command/vm.py b/cccli/command/vm.py
index f5db6325cf47bfdccf94ad74783d28f8a3d62d4d..d9d44c4f9ee039f61de746afcc62c6ccfaec7326 100644
--- a/cccli/command/vm.py
+++ b/cccli/command/vm.py
@@ -103,3 +103,49 @@ class Command_undefine(TqlCommand):
             raise cmdBadArgument()
         # rpc call
         self.rpccall("undefine", self.args[0], self.options.clean)
+
+class Command_clone(TqlCommand):
+    '''Clone vm'''
+
+    def __init__(self, cli, argv0):
+        TqlCommand.__init__(self, cli, argv0)
+        self.set_usage("%prog [options] <source tql> <dest tql> <name>")
+
+    def __call__(self, argv):
+        # Parse argline
+        self.parse_args(argv)
+        # Check args count
+        if len(self.args) != 3:
+            raise cmdBadArgument()
+        stql = self.args[0]
+        dtql = self.args[1]
+        newname = self.args[2]
+        if self.options.direct:
+            self.rpccall("clone", stql, dtql, newname)
+        else:
+            # list stql
+            self.printer.out("<= Source VM:")
+            if not self.options.raw:
+                self.tql_filter = "&r=vm$status"
+            o = self.rpccall("list", stql, _direct=True, _status=True)
+            if len(o["objects"]) != 1:
+                raise cmdError("You must select only one VM")
+            # list dtql
+            self.printer.out("=> Destination HV:")
+            if not self.options.raw:
+                self.tql_filter = "&r=hv$nvm"
+            o = self.rpccall("list", dtql, _direct=True, _status=True)
+            if len(o["objects"]) != 1:
+                raise cmdError("You must select only one HV")
+            # check if destination vm does not exist
+            o = self.rpccall("list", "%s&h:%s"%(dtql,newname), _direct=True, _status=False)
+            if not self.options.raw:
+                self.tql_filter = "&r=vm"
+            if len(o["objects"]) != 0:
+                raise cmdError("Destination VM exist on HV")
+            # ask confirmation
+            if self.printer.ask("Do you confirm? (I'm god) ") != "I'm god":
+                raise cmdWarning("User resign")
+            # run migration
+            self.tql_filter = ""
+            self.rpccall("clone", stql, dtql, newname, _direct=True)