diff --git a/doc/source/command-objects/port.rst b/doc/source/command-objects/port.rst
index 414b7437f5..0c91f3ac00 100644
--- a/doc/source/command-objects/port.rst
+++ b/doc/source/command-objects/port.rst
@@ -4,6 +4,84 @@ port
 
 Network v2
 
+port create
+-----------
+
+Create new port
+
+.. program:: port create
+.. code:: bash
+
+    os port create
+        --network <network>
+        [--fixed-ip subnet=<subnet>,ip-address=<ip-address>]
+        [--device-id <device-id>]
+        [--device-owner <device-owner>]
+        [--vnic-type <vnic-type>]
+        [--binding-profile <binding-profile>]
+        [--host-id <host-id>]
+        [--enable | --disable]
+        [--mac-address <mac-address>]
+        [--project <project> [--project-domain <project-domain>]]
+        <name>
+
+.. option:: --network <network>
+
+    Network this port belongs to (name or ID)
+
+.. option:: --fixed-ip subnet=<subnet>,ip-address=<ip-address>
+
+    Desired IP and/or subnet (name or ID) for this port:
+    subnet=<subnet>,ip-address=<ip-address>
+    (this option can be repeated)
+
+.. option:: --device-id <device-id>
+
+    Device ID of this port
+
+.. option:: --device-owner <device-owner>
+
+    Device owner of this port
+
+.. option:: --vnic-type <vnic-type>
+
+    VNIC type for this port (direct | direct-physical | macvtap | normal(default) | baremetal)
+
+.. option:: --binding-profile <binding-profile>
+
+    Custom data to be passed as binding:profile: <key>=<value>
+    (this option can be repeated)
+
+.. option:: --host-id <host-id>
+
+    The ID of the host where the port is allocated
+
+.. option:: --enable
+
+    Enable port (default)
+
+.. option:: --disable
+
+    Disable port
+
+.. option:: --mac-address <mac-address>
+
+    MAC address of this port
+
+.. option:: --project <project>
+
+    Owner's project (name or ID)
+
+.. option:: --project-domain <project-domain>
+
+    Domain the project belongs to (name or ID).
+    This can be used in case collisions between project names exist.
+
+.. _port_create-name:
+.. describe:: <name>
+
+    Name of this port
+
 port delete
 -----------
 
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 19b3701d87..f9d0fc957f 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -14,13 +14,14 @@
 """Port action implementations"""
 
 from openstackclient.common import command
+from openstackclient.common import parseractions
 from openstackclient.common import utils
+from openstackclient.identity import common as identity_common
 
 
 def _format_admin_state(state):
     return 'UP' if state else 'DOWN'
 
-
 _formatters = {
     'admin_state_up': _format_admin_state,
     'allowed_address_pairs': utils.format_list_of_dicts,
@@ -49,7 +50,171 @@ def _get_columns(item):
         if binding_column in columns:
             columns.remove(binding_column)
             columns.append(binding_column.replace('binding:', 'binding_', 1))
-    return sorted(columns)
+    return tuple(sorted(columns))
+
+
+def _get_attrs(client_manager, parsed_args):
+    attrs = {}
+
+    if parsed_args.name is not None:
+        attrs['name'] = str(parsed_args.name)
+    if parsed_args.fixed_ip is not None:
+        attrs['fixed_ips'] = parsed_args.fixed_ip
+    if parsed_args.device_id is not None:
+        attrs['device_id'] = parsed_args.device_id
+    if parsed_args.device_owner is not None:
+        attrs['device_owner'] = parsed_args.device_owner
+    if parsed_args.admin_state is not None:
+        attrs['admin_state_up'] = parsed_args.admin_state
+    if parsed_args.binding_profile is not None:
+        attrs['binding:profile'] = parsed_args.binding_profile
+    if parsed_args.vnic_type is not None:
+        attrs['binding:vnic_type'] = parsed_args.vnic_type
+    if parsed_args.host_id is not None:
+        attrs['binding:host_id'] = parsed_args.host_id
+
+    # The remaining options do not support 'port set' command, so they require
+    # additional check
+    if 'mac_address' in parsed_args and parsed_args.mac_address is not None:
+        attrs['mac_address'] = parsed_args.mac_address
+    if 'network' in parsed_args and parsed_args.network is not None:
+        attrs['network_id'] = parsed_args.network
+    if 'project' in parsed_args and parsed_args.project is not None:
+        # TODO(singhj): since 'project' logic is common among
+        # router, network, port etc., maybe move it to a common file.
+        identity_client = client_manager.identity
+        project_id = identity_common.find_project(
+            identity_client,
+            parsed_args.project,
+            parsed_args.project_domain,
+        ).id
+        attrs['tenant_id'] = project_id
+
+    return attrs
+
+
+def _prepare_fixed_ips(client_manager, parsed_args):
+    """Fix and properly format fixed_ip option.
+
+    Appropriately convert any subnet names to their respective ids.
+    Convert fixed_ips in parsed args to be in valid dictionary format:
+    {'subnet': 'foo'}.
+    """
+    client = client_manager.network
+    ips = []
+
+    if parsed_args.fixed_ip:
+        for ip_spec in parsed_args.fixed_ip:
+            if 'subnet' in ip_spec:
+                subnet_name_id = ip_spec['subnet']
+                if subnet_name_id:
+                    _subnet = client.find_subnet(subnet_name_id,
+                                                 ignore_missing=False)
+                    ip_spec['subnet_id'] = _subnet.id
+                    del ip_spec['subnet']
+
+            if 'ip-address' in ip_spec:
+                ip_spec['ip_address'] = ip_spec['ip-address']
+                del ip_spec['ip-address']
+
+            ips.append(ip_spec)
+
+    if ips:
+        parsed_args.fixed_ip = ips
+
+
+def _add_updatable_args(parser):
+        parser.add_argument(
+            '--fixed-ip',
+            metavar='subnet=<subnet>,ip-address=<ip-address>',
+            action=parseractions.MultiKeyValueAction,
+            optional_keys=['subnet', 'ip-address'],
+            help='Desired IP and/or subnet (name or ID) for this port: '
+                 'subnet=<subnet>,ip-address=<ip-address> '
+                 '(this option can be repeated)')
+        parser.add_argument(
+            '--device-id',
+            metavar='<device-id>',
+            help='Device ID of this port')
+        parser.add_argument(
+            '--device-owner',
+            metavar='<device-owner>',
+            help='Device owner of this port')
+        parser.add_argument(
+            '--vnic-type',
+            metavar='<vnic-type>',
+            choices=['direct', 'direct-physical', 'macvtap',
+                     'normal', 'baremetal'],
+            help='VNIC type for this port (direct | direct-physical |'
+                 ' macvtap | normal(default) | baremetal)')
+        parser.add_argument(
+            '--binding-profile',
+            metavar='<binding-profile>',
+            action=parseractions.KeyValueAction,
+            help='Custom data to be passed as binding:profile: <key>=<value> '
+                 '(this option can be repeated)')
+        parser.add_argument(
+            '--host-id',
+            metavar='<host-id>',
+            help='The ID of the host where the port is allocated'
+        )
+
+
+class CreatePort(command.ShowOne):
+    """Create a new port"""
+
+    def get_parser(self, prog_name):
+        parser = super(CreatePort, self).get_parser(prog_name)
+
+        parser.add_argument(
+            '--network',
+            metavar='<network>',
+            required=True,
+            help='Network this port belongs to (name or ID)')
+        _add_updatable_args(parser)
+        admin_group = parser.add_mutually_exclusive_group()
+        admin_group.add_argument(
+            '--enable',
+            dest='admin_state',
+            action='store_true',
+            default=True,
+            help='Enable port (default)',
+        )
+        admin_group.add_argument(
+            '--disable',
+            dest='admin_state',
+            action='store_false',
+            help='Disable port',
+        )
+        parser.add_argument(
+            '--mac-address',
+            metavar='<mac-address>',
+            help='MAC address of this port')
+        parser.add_argument(
+            '--project',
+            metavar='<project>',
+            help="Owner's project (name or ID)")
+        parser.add_argument(
+            'name',
+            metavar='<name>',
+            help='Name of this port')
+        identity_common.add_project_domain_option_to_parser(parser)
+        # TODO(singhj): Add support for extended options:
+        # qos,security groups,dhcp, address pairs
+        return parser
+
+    def take_action(self, parsed_args):
+        client = self.app.client_manager.network
+        _network = client.find_network(parsed_args.network,
+                                       ignore_missing=False)
+        parsed_args.network = _network.id
+        _prepare_fixed_ips(self.app.client_manager, parsed_args)
+        attrs = _get_attrs(self.app.client_manager, parsed_args)
+        obj = client.create_port(**attrs)
+        columns = _get_columns(obj)
+        data = utils.get_item_properties(obj, columns, formatters=_formatters)
+
+        return columns, data
 
 
 class DeletePort(command.Command):
@@ -90,4 +255,4 @@ class ShowPort(command.ShowOne):
         obj = client.find_port(parsed_args.port, ignore_missing=False)
         columns = _get_columns(obj)
         data = utils.get_item_properties(obj, columns, formatters=_formatters)
-        return (tuple(columns), data)
+        return columns, data
diff --git a/openstackclient/tests/network/v2/test_port.py b/openstackclient/tests/network/v2/test_port.py
index bc246bd877..907d8a7d10 100644
--- a/openstackclient/tests/network/v2/test_port.py
+++ b/openstackclient/tests/network/v2/test_port.py
@@ -27,6 +27,150 @@ class TestPort(network_fakes.TestNetworkV2):
         # Get a shortcut to the network client
         self.network = self.app.client_manager.network
 
+    def _get_common_cols_data(self, fake_port):
+        columns = (
+            'admin_state_up',
+            'allowed_address_pairs',
+            'binding_host_id',
+            'binding_profile',
+            'binding_vif_details',
+            'binding_vif_type',
+            'binding_vnic_type',
+            'device_id',
+            'device_owner',
+            'dns_assignment',
+            'dns_name',
+            'extra_dhcp_opts',
+            'fixed_ips',
+            'id',
+            'mac_address',
+            'name',
+            'network_id',
+            'port_security_enabled',
+            'project_id',
+            'security_groups',
+            'status',
+        )
+
+        data = (
+            port._format_admin_state(fake_port.admin_state_up),
+            utils.format_list_of_dicts(fake_port.allowed_address_pairs),
+            fake_port.binding_host_id,
+            utils.format_dict(fake_port.binding_profile),
+            utils.format_dict(fake_port.binding_vif_details),
+            fake_port.binding_vif_type,
+            fake_port.binding_vnic_type,
+            fake_port.device_id,
+            fake_port.device_owner,
+            utils.format_list_of_dicts(fake_port.dns_assignment),
+            fake_port.dns_name,
+            utils.format_list_of_dicts(fake_port.extra_dhcp_opts),
+            utils.format_list_of_dicts(fake_port.fixed_ips),
+            fake_port.id,
+            fake_port.mac_address,
+            fake_port.name,
+            fake_port.network_id,
+            fake_port.port_security_enabled,
+            fake_port.project_id,
+            utils.format_list(fake_port.security_groups),
+            fake_port.status,
+        )
+
+        return columns, data
+
+
+class TestCreatePort(TestPort):
+
+    _port = network_fakes.FakePort.create_one_port()
+
+    def setUp(self):
+        super(TestCreatePort, self).setUp()
+
+        self.network.create_port = mock.Mock(return_value=self._port)
+        fake_net = network_fakes.FakeNetwork.create_one_network({
+            'id': self._port.network_id,
+        })
+        self.network.find_network = mock.Mock(return_value=fake_net)
+        self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet()
+        self.network.find_subnet = mock.Mock(return_value=self.fake_subnet)
+        # Get the command object to test
+        self.cmd = port.CreatePort(self.app, self.namespace)
+
+    def test_create_default_options(self):
+        arglist = [
+            '--network', self._port.network_id,
+            'test-port',
+        ]
+        verifylist = [
+            ('network', self._port.network_id,),
+            ('admin_state', True),
+            ('name', 'test-port'),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = (self.cmd.take_action(parsed_args))
+
+        self.network.create_port.assert_called_with(**{
+            'admin_state_up': True,
+            'network_id': self._port.network_id,
+            'name': 'test-port',
+        })
+
+        ref_columns, ref_data = self._get_common_cols_data(self._port)
+        self.assertEqual(ref_columns, columns)
+        self.assertEqual(ref_data, data)
+
+    def test_create_full_options(self):
+        arglist = [
+            '--mac-address', 'aa:aa:aa:aa:aa:aa',
+            '--fixed-ip', 'subnet=%s,ip-address=10.0.0.2'
+            % self.fake_subnet.id,
+            '--device-id', 'deviceid',
+            '--device-owner', 'fakeowner',
+            '--disable',
+            '--vnic-type', 'macvtap',
+            '--binding-profile', 'foo=bar',
+            '--binding-profile', 'foo2=bar2',
+            '--network', self._port.network_id,
+            'test-port',
+
+        ]
+        verifylist = [
+            ('mac_address', 'aa:aa:aa:aa:aa:aa'),
+            (
+                'fixed_ip',
+                [{'subnet': self.fake_subnet.id, 'ip-address': '10.0.0.2'}]
+            ),
+            ('device_id', 'deviceid'),
+            ('device_owner', 'fakeowner'),
+            ('admin_state', False),
+            ('vnic_type', 'macvtap'),
+            ('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}),
+            ('network', self._port.network_id),
+            ('name', 'test-port'),
+
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = (self.cmd.take_action(parsed_args))
+
+        self.network.create_port.assert_called_with(**{
+            'mac_address': 'aa:aa:aa:aa:aa:aa',
+            'fixed_ips': [{'subnet_id': self.fake_subnet.id,
+                           'ip_address': '10.0.0.2'}],
+            'device_id': 'deviceid',
+            'device_owner': 'fakeowner',
+            'admin_state_up': False,
+            'binding:vnic_type': 'macvtap',
+            'binding:profile': {'foo': 'bar', 'foo2': 'bar2'},
+            'network_id': self._port.network_id,
+            'name': 'test-port',
+        })
+
+        ref_columns, ref_data = self._get_common_cols_data(self._port)
+        self.assertEqual(ref_columns, columns)
+        self.assertEqual(ref_data, data)
+
 
 class TestDeletePort(TestPort):
 
@@ -60,54 +204,6 @@ class TestShowPort(TestPort):
     # The port to show.
     _port = network_fakes.FakePort.create_one_port()
 
-    columns = (
-        'admin_state_up',
-        'allowed_address_pairs',
-        'binding_host_id',
-        'binding_profile',
-        'binding_vif_details',
-        'binding_vif_type',
-        'binding_vnic_type',
-        'device_id',
-        'device_owner',
-        'dns_assignment',
-        'dns_name',
-        'extra_dhcp_opts',
-        'fixed_ips',
-        'id',
-        'mac_address',
-        'name',
-        'network_id',
-        'port_security_enabled',
-        'project_id',
-        'security_groups',
-        'status',
-    )
-
-    data = (
-        port._format_admin_state(_port.admin_state_up),
-        utils.format_list_of_dicts(_port.allowed_address_pairs),
-        _port.binding_host_id,
-        utils.format_dict(_port.binding_profile),
-        utils.format_dict(_port.binding_vif_details),
-        _port.binding_vif_type,
-        _port.binding_vnic_type,
-        _port.device_id,
-        _port.device_owner,
-        utils.format_list_of_dicts(_port.dns_assignment),
-        _port.dns_name,
-        utils.format_list_of_dicts(_port.extra_dhcp_opts),
-        utils.format_list_of_dicts(_port.fixed_ips),
-        _port.id,
-        _port.mac_address,
-        _port.name,
-        _port.network_id,
-        _port.port_security_enabled,
-        _port.project_id,
-        utils.format_list(_port.security_groups),
-        _port.status,
-    )
-
     def setUp(self):
         super(TestShowPort, self).setUp()
 
@@ -136,5 +232,7 @@ class TestShowPort(TestPort):
 
         self.network.find_port.assert_called_with(self._port.name,
                                                   ignore_missing=False)
-        self.assertEqual(tuple(self.columns), columns)
-        self.assertEqual(self.data, data)
+
+        ref_columns, ref_data = self._get_common_cols_data(self._port)
+        self.assertEqual(ref_columns, columns)
+        self.assertEqual(ref_data, data)
diff --git a/releasenotes/notes/add-port-create-command-a3580662721a6312.yaml b/releasenotes/notes/add-port-create-command-a3580662721a6312.yaml
new file mode 100644
index 0000000000..4fafb42c00
--- /dev/null
+++ b/releasenotes/notes/add-port-create-command-a3580662721a6312.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add support for the ``port create`` command.
+    [Bug `1519909 <https://bugs.launchpad.net/python-openstackclient/+bug/1519909>`_]
diff --git a/setup.cfg b/setup.cfg
index 284e6dec12..d00c5c5f92 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -331,6 +331,7 @@ openstack.network.v2 =
     network_list = openstackclient.network.v2.network:ListNetwork
     network_set = openstackclient.network.v2.network:SetNetwork
     network_show = openstackclient.network.v2.network:ShowNetwork
+    port_create = openstackclient.network.v2.port:CreatePort
     port_delete = openstackclient.network.v2.port:DeletePort
     port_show = openstackclient.network.v2.port:ShowPort
     router_create = openstackclient.network.v2.router:CreateRouter