diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index 9ad3576a20..e43b907b18 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -267,6 +267,7 @@ enable_senlin: "no"
 enable_swift: "no"
 enable_telegraf: "no"
 enable_tempest: "no"
+enable_vmtp: "no"
 enable_watcher: "no"
 
 ironic_keystone_user: "ironic"
diff --git a/ansible/inventory/all-in-one b/ansible/inventory/all-in-one
index d4c96d7da9..69b05281a4 100644
--- a/ansible/inventory/all-in-one
+++ b/ansible/inventory/all-in-one
@@ -126,6 +126,9 @@ control
 [senlin:children]
 control
 
+[vmtp:children]
+control
+
 [watcher:children]
 control
 
diff --git a/ansible/inventory/multinode b/ansible/inventory/multinode
index f54341a151..bde17c2d54 100644
--- a/ansible/inventory/multinode
+++ b/ansible/inventory/multinode
@@ -144,6 +144,9 @@ control
 [senlin:children]
 control
 
+[vmtp:children]
+control
+
 [watcher:children]
 control
 
diff --git a/ansible/roles/vmtp/defaults/main.yml b/ansible/roles/vmtp/defaults/main.yml
new file mode 100644
index 0000000000..1899d8be0b
--- /dev/null
+++ b/ansible/roles/vmtp/defaults/main.yml
@@ -0,0 +1,25 @@
+---
+project_name: "vmtp"
+
+####################
+# Docker
+####################
+vmtp_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-vmtp"
+vmtp_tag: "{{ openstack_release }}"
+vmtp_image_full: "{{ vmtp_image }}:{{ vmtp_tag }}"
+
+#########################
+# VMTP Specific resources
+#########################
+vmtp_vm_image_name: "Ubuntu Server 16.04"
+vmtp_vm_ssh_username: "ubuntu"
+vmtp_vm_flavor_type: "m1.small"
+vmtp_vm_nameservers: ['8.8.8.8', '8.8.4.4']
+vmtp_vm_image_url: "https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img"
+vmtp_internal_network_name: ['vmtp-demo-net']
+vmtp_internal_subnet_name: ['vmtp-demo-subnet']
+vmtp_internal_subnet_name_ipv6: ['vmtp-demo-v6-subnet']
+vmtp_internal_cidr: ['10.0.0.0/24']
+vmtp_internal_cidr_v6: ['2001:45::/64']
+vmtp_router_name: "pns-router"
+vmtp_os_dp_network: "physnet1"
diff --git a/ansible/roles/vmtp/meta/main.yml b/ansible/roles/vmtp/meta/main.yml
new file mode 100644
index 0000000000..6b4fff8fef
--- /dev/null
+++ b/ansible/roles/vmtp/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+  - { role: common }
diff --git a/ansible/roles/vmtp/tasks/config.yml b/ansible/roles/vmtp/tasks/config.yml
new file mode 100644
index 0000000000..c63c93e806
--- /dev/null
+++ b/ansible/roles/vmtp/tasks/config.yml
@@ -0,0 +1,26 @@
+---
+- name: Ensuring config directories exist
+  file:
+    path: "{{ node_config_directory }}/vmtp"
+    state: "directory"
+    recurse: yes
+
+- name: Register binary python path
+  command: echo /usr/lib/python2.7/site-packages
+  register: python_path
+  when: kolla_install_type == 'binary'
+
+- name: Register source python path
+  command: echo /var/lib/kolla/venv/lib/python2.7/site-packages
+  register: python_path
+  when: kolla_install_type != 'binary'
+
+- name: Copying over configuration file for vmtp
+  merge_yaml:
+    sources:
+      - "{{ role_path }}/templates/{{ item }}.j2"
+      - "{{ node_custom_config }}/{{ item }}"
+      - "{{ node_custom_config }}/vmtp/{{ item }}"
+    dest: "{{ python_path }}/vmtp/{{ item }}"
+  with_items:
+    - "cfg.default.yaml"
diff --git a/ansible/roles/vmtp/tasks/deploy.yml b/ansible/roles/vmtp/tasks/deploy.yml
new file mode 100644
index 0000000000..d4daf108c1
--- /dev/null
+++ b/ansible/roles/vmtp/tasks/deploy.yml
@@ -0,0 +1,6 @@
+---
+- include: config.yml
+  when: inventory_hostname in groups['vmtp']
+
+- include: start.yml
+  when: inventory_hostname in groups['vmtp']
diff --git a/ansible/roles/vmtp/tasks/do_reconfigure.yml b/ansible/roles/vmtp/tasks/do_reconfigure.yml
new file mode 100644
index 0000000000..3cf5f29402
--- /dev/null
+++ b/ansible/roles/vmtp/tasks/do_reconfigure.yml
@@ -0,0 +1,58 @@
+---
+- name: Ensure container is up
+  kolla_docker:
+    name: "vmtp"
+    action: "get_container_state"
+  register: container_state
+  failed_when: container_state.Running == false
+  when: inventory_hostname in groups['vmtp']
+
+- include: config.yml
+
+- name: Check configuration
+  command: docker exec {{ item.name }} /usr/local/bin/kolla_set_configs --check
+  changed_when: false
+  failed_when: false
+  register: check_results
+  when: inventory_hostname in groups['vmtp']
+  with_items:
+    - { name: vmtp, group: vmtp }
+
+# NOTE(jeffrey4l): when config_strategy == 'COPY_ALWAYS'
+# and container env['KOLLA_CONFIG_STRATEGY'] == 'COPY_ONCE',
+# just remove the container and start again
+- name: Containers config strategy
+  kolla_docker:
+    name: "vmtp"
+    action: "get_container_env"
+  register: container_envs
+  when: inventory_hostname in groups['vmtp']
+
+- name: Remove the containers
+  kolla_docker:
+    name: "vmtp"
+    action: "remove_container"
+  register: remove_containers
+  when:
+    - config_strategy == "COPY_ONCE" or item[0]['KOLLA_CONFIG_STRATEGY'] == 'COPY_ONCE'
+    - item[1]['rc'] == 1
+    - inventory_hostname in groups['vmtp']
+  with_together:
+    - "{{ container_envs.results }}"
+    - "{{ check_results.results }}"
+
+- include: start.yml
+  when: remove_containers.changed
+
+- name: Restart containers
+  kolla_docker:
+    name: "vmtp"
+    action: "restart_container"
+  when:
+    - config_strategy == 'COPY_ALWAYS'
+    - item[0]['KOLLA_CONFIG_STRATEGY'] != 'COPY_ONCE'
+    - item[1]['rc'] == 1
+    - inventory_hostname in groups['vmtp']
+  with_together:
+    - "{{ container_envs.results }}"
+    - "{{ check_results.results }}"
diff --git a/ansible/roles/vmtp/tasks/main.yml b/ansible/roles/vmtp/tasks/main.yml
new file mode 100644
index 0000000000..bef222f6a5
--- /dev/null
+++ b/ansible/roles/vmtp/tasks/main.yml
@@ -0,0 +1,2 @@
+---
+- include: "{{ action }}.yml"
\ No newline at end of file
diff --git a/ansible/roles/vmtp/tasks/pull.yml b/ansible/roles/vmtp/tasks/pull.yml
new file mode 100644
index 0000000000..0be3c5eece
--- /dev/null
+++ b/ansible/roles/vmtp/tasks/pull.yml
@@ -0,0 +1,7 @@
+---
+- name: Pulling vmtp image
+  kolla_docker:
+    action: "pull_image"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ vmtp_image_full }}"
+  when: inventory_hostname in groups['vmtp']
diff --git a/ansible/roles/vmtp/tasks/reconfigure.yml b/ansible/roles/vmtp/tasks/reconfigure.yml
new file mode 100644
index 0000000000..559f7c954c
--- /dev/null
+++ b/ansible/roles/vmtp/tasks/reconfigure.yml
@@ -0,0 +1,4 @@
+---
+- include: do_reconfigure.yml
+  serial: "30%"
+  when: inventory_hostname in groups['vmtp']
diff --git a/ansible/roles/vmtp/tasks/start.yml b/ansible/roles/vmtp/tasks/start.yml
new file mode 100644
index 0000000000..c3a2b19b73
--- /dev/null
+++ b/ansible/roles/vmtp/tasks/start.yml
@@ -0,0 +1,11 @@
+---
+- name: Starting vmtp container
+  kolla_docker:
+    action: "start_container"
+    common_options: "{{ docker_common_options }}"
+    image: "{{ vmtp_image_full }}"
+    name: "vmtp"
+    volumes:
+      - "{{ node_config_directory }}/vmtp/:{{ container_config_directory }}/:ro"
+      - "/etc/localtime:/etc/localtime:ro"
+      - "kolla_logs:/var/log/kolla"
diff --git a/ansible/roles/vmtp/tasks/upgrade.yml b/ansible/roles/vmtp/tasks/upgrade.yml
new file mode 100644
index 0000000000..1f16915ad9
--- /dev/null
+++ b/ansible/roles/vmtp/tasks/upgrade.yml
@@ -0,0 +1,4 @@
+---
+- include: config.yml
+
+- include: start.yml
diff --git a/ansible/roles/vmtp/templates/cfg.default.yaml.j2 b/ansible/roles/vmtp/templates/cfg.default.yaml.j2
new file mode 100644
index 0000000000..991c86c698
--- /dev/null
+++ b/ansible/roles/vmtp/templates/cfg.default.yaml.j2
@@ -0,0 +1,41 @@
+image_name: {{ vmtp_vm_image_name }}
+ssh_vm_username: {{ vmtp_vm_ssh_username }}
+flavor_type: {{ vmtp_vm_flavor_type }}
+availability_zone: {{ vmtp_vm_availability_zone }}
+dns_nameservers: {{ vmtp_vm_nameservers }}
+vm_image_url: {{ vmtp_vm_image_url }}
+
+reuse_network_name:
+floating_ip: True
+reuse_existing_vm:
+config_drive:
+user_data_file:
+ipv6_mode:
+router_name:  {{ vmtp_router_name }}
+
+internal_network_name: {{ vmtp_internal_network_name }}
+internal_subnet_name: {{ vmtp_internal_subnet_name }}
+internal_subnet_name_ipv6: {{ vmtp_internal_subnet_name_ipv6 }}
+internal_cidr: {{ vmtp_internal_cidr }}
+internal_cidr_v6: {{ vmtp_internal_cidr_v6 }}
+
+public_key_file:
+private_key_file:
+public_key_name: 'pns_public_key'
+vm_name_server:  'TestServer'
+vm_name_client:   'TestClient'
+security_group_name: 'pns-security'
+
+ping_count: 2
+ping_pass_threshold: 80
+ssh_retry_count: 50
+generic_retry_count: 50
+
+tcp_tp_loop_count: 3
+tcp_pkt_sizes: [65536]
+udp_pkt_sizes: [128, 1024, 8192]
+icmp_pkt_sizes: [64, 391, 1500]
+udp_loss_rate_range: [2, 5]
+
+vm_bandwidth: 0
+os_dataplane_network: {{ vmtp_os_dp_network }}
diff --git a/ansible/site.yml b/ansible/site.yml
index 9134db55ce..c1ab1b06c4 100644
--- a/ansible/site.yml
+++ b/ansible/site.yml
@@ -310,6 +310,13 @@
         tags: rally,
         when: enable_rally | bool }
 
+- hosts:
+    - vmtp
+  roles:
+    - { role: vmtp,
+        tags: vmtp,
+        when: enable_vmtp | bool }
+
 - hosts:
     - watcher-api
     - watcher-engine