summaryrefslogtreecommitdiffstats
path: root/docs/usecases/vppinazure.rst
blob: f64e2a7e7d1542de36c5b0ae470e5f3ffe8b2f8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
.. _vppinazure:

.. toctree::

VPP in Azure
___________________




Before starting, a few notes:

* in our configuration we use only DPDK 18.02, since with the newer versions, such as DPDK 18.05, we obtained several problems during VPP installation (mostly related with MLX4 PMD Drivers). 

* Some of the commands are taken from `Azure’s DPDK page <https://docs.microsoft.com/en-us/azure/virtual-network/setup-dpdk>`_.

To bring DPDK inside Azure, we perform the following procedure:

Firstly, we install the DPDK dependencies:

.. code-block:: console

 $ sudo add-apt-repository ppa:canonical-server/dpdk-azure –y
 $ sudo apt-get update
 $ sudo apt-get install -y librdmacm-dev librdmacm1 build-essential libnuma-dev libmnl-dev

Then, we download DPDK 18.02:

.. code-block:: console

 $ sudo wget https://fast.dpdk.org/rel/dpdk-18.02.2.tar.xz
 $ tar -xvf dpdk-18.02.2.tar.xz

Finally, we build DPDK, modifying first its configuration files in order to make VPP compatible with MLX4 drivers:

Inside config/common_base, modify:

.. code-block:: console

  CONFIG_RTE_BUILD_SHARED_LIB=n
  CONFIG_RTE_LIBRTE_MLX4_PMD=y
  CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=y
  CONFIG_RTE_LIBRTE_TAP_PMD=y
  CONFIG_RTE_LIBRTE_FAILSAFE_PMD=y

and then:

.. code-block:: console

 $ make config T=x86_64-native-linuxapp-gcc
 $ sed -ri 's,(MLX._PMD=)n,\1y,' build/.config
 $ make

Finally we build DPDK:

.. code-block:: console

 $ make install T=x86_64-native-linuxapp-gcc DESTDIR=/home/ciscotest/test EXTRA_CFLAGS='-fPIC -pie'

And we reboot the instance:

.. code-block:: console

 $ reboot instance

After the reboot, we type these commands:

.. code-block:: console

 $ echo 1024 | sudo tee /sys/devices/system/node/node*/hugepages/hugepages-2048kB/nr_hugepages
 $ mkdir /mnt/huge
 $ sudo mount -t hugetlbfs nodev /mnt/huge
 $ grep Huge /proc/meminfo
 $ modprobe -a ib_uverbs
 $ cd x86_64-native-linuxapp-gcc/
 $ ls
 $ cd lib/
 $ ls
 $ sudo cp librte_pmd_mlx4_glue.so.18.02.0 /usr/lib

**Now we focus on VPP installation:**

In our configuration we use VPP 18.07.

We perform this procedure in order to install VPP 18.07 with an external DPDK configuration inside Azure.

Firstly, we download VPP

.. code-block:: console

 $ git clone https://gerrit.fd.io/r/vpp
 $ git checkout v18.07

Then, we build VPP, using the external DPDK configuration we previously made:

We modify the path inside the vpp.mk file:

.. code-block:: console

 $ build-data/platforms/vpp.mk
 $ vpp_uses_external_dpdk = yes
 $ vpp_dpdk_inc_dir = <PATH_TO_DESTDIR_NAME_FROM_ABOVE>/include/dpdk/
 $ vpp_dpdk_lib_dir =<PATH_TO_DESTDIR_NAME_FROM_ABOVE>/lib

<PATH_TO_DESTDIR_NAME_FROM_ABOVE> is whatever the path used when compiling DPDK above. These paths have to be absolute path in order for it to work.

we modify build-data/platforms/vpp.mk to use

.. code-block:: console

 vpp_uses_dpdk_mlx4_pmd = yes

.. code-block:: console

 $ make build
 $ cd build-root/
 $ make V=0 PLATFORM=vpp TAG=vpp install-deb
 $ sudo dpkg -i *.deb

Finally, we modify the startup.conf file:

.. code-block:: console

 $ cd /etc/vpp
 $ sudo nano startup.conf

Inside the DPDK block, the following commands:

.. code-block:: console


 ## Whitelist specific interface by specifying PCI address
 dev 000X:00:0X.0
 dev 000X:00:0X.0
 
 # Running failsafe
 vdev net_vdev_netvsc0,iface=eth1
 vdev net_vdev_netvsc1,iface=eth2

*Please refer to Azure DPDK document to pick the right iface to use for failsafe vdev.*


and finally:

.. code-block:: console

 $ sudo service vpp stop
 $ sudo service vpp start
 $ sudo service vpp status
 $ sudo vppctl

Now VPP will work inside Azure!
#!/usr/bin/env python
#
# Copyright (c) 2016 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
# l
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import os, util
from string import Template

def is_manually_generated(f_name):
    return f_name in {'control_ping_reply'}


class_reference_template = Template("""jclass ${ref_name}Class;
""")

find_class_invocation_template = Template("""
    ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "org/openvpp/jvpp/dto/${class_name}"));
    if ((*env)->ExceptionCheck(env)) {
        (*env)->ExceptionDescribe(env);
        return JNI_ERR;
    }""")

class_cache_template = Template("""
$class_references
static int cache_class_references(JNIEnv* env) {
    $find_class_invocations
    return 0;
}""")

def generate_class_cache(func_list):
    class_references = []
    find_class_invocations = []
    for f in func_list:
        c_name = f['name']
        class_name = util.underscore_to_camelcase_upper(c_name)
        ref_name = util.underscore_to_camelcase(c_name)

        if not util.is_reply(class_name) or util.is_ignored(c_name) or util.is_notification(c_name):
            # TODO handle notifications
            continue

        class_references.append(class_reference_template.substitute(
                ref_name=ref_name))

        find_class_invocations.append(find_class_invocation_template.substitute(
                ref_name=ref_name,
                class_name=class_name))

    return class_cache_template.substitute(
            class_references="".join(class_references), find_class_invocations="".join(find_class_invocations))


# TODO: cache method and field identifiers to achieve better performance
# https://jira.fd.io/browse/HONEYCOMB-42
request_class_template = Template("""
    jclass requestClass = (*env)->FindClass(env, "org/openvpp/jvpp/dto/${java_name_upper}");""")

request_field_identifier_template = Template("""
    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, requestClass, "${java_name}", "${jni_signature}");
    ${jni_type} ${java_name} = (*env)->Get${jni_getter}(env, request, ${java_name}FieldId);
    """)

u8_struct_setter_template = Template("""
    mp->${c_name} = ${java_name};""")

u16_struct_setter_template = Template("""
    mp->${c_name} = clib_host_to_net_u16(${java_name});""")

u32_struct_setter_template = Template("""
    mp->${c_name} = clib_host_to_net_u32(${java_name});""")

i32_struct_setter_template = Template("""
    mp->${c_name} = clib_host_to_net_i32(${java_name});!""")

u64_struct_setter_template = Template("""
    mp->${c_name} = clib_host_to_net_u64(${java_name});""")

u8_array_struct_setter_template = Template("""
    {
        jsize cnt = (*env)->GetArrayLength (env, ${java_name});
        size_t max_size = ${field_length};
        if (max_size != 0 && cnt > max_size) cnt = max_size;
        (*env)->GetByteArrayRegion(env, ${java_name}, 0, cnt, (jbyte *)mp->${c_name});
    }
""")

u32_array_struct_setter_template = Template("""
    jint * ${java_name}ArrayElements = (*env)->GetIntArrayElements(env, ${java_name}, NULL);
    {
        size_t _i;
        jsize cnt = (*env)->GetArrayLength (env, ${java_name});
        size_t max_size = ${field_length};
        if (max_size != 0 && cnt > max_size) cnt = max_size;
        for (_i = 0; _i < cnt; _i++) {
            mp->${c_name}[_i] = clib_host_to_net_u32(${java_name}ArrayElements[_i]);
        }
    }
    (*env)->ReleaseIntArrayElements (env, ${java_name}, ${java_name}ArrayElements, 0);
    """)

vl_api_ip4_fib_counter_t_array_struct_setter_template = Template("""
    // vl_api_ip4_fib_counter_t_array_field_setter_template FIXME""")

vl_api_ip6_fib_counter_t_array_struct_setter_template = Template("""
    // vl_api_ip6_fib_counter_t_array_field_setter_template FIXME""")

struct_setter_templates = {'u8': u8_struct_setter_template,
                          'u16': u32_struct_setter_template,
                          'u32': u32_struct_setter_template,
                          'i32': u32_struct_setter_template,
                          'u64': u64_struct_setter_template,
                          'u8[]': u8_array_struct_setter_template,
                          'u32[]': u32_array_struct_setter_template,
                          'vl_api_ip4_fib_counter_t[]': vl_api_ip4_fib_counter_t_array_struct_setter_template,
                          'vl_api_ip6_fib_counter_t[]': vl_api_ip6_fib_counter_t_array_struct_setter_template
                  }

jni_impl_template = Template("""
/**
 * JNI binding for sending ${c_name} vpe.api message.
 * Generated based on $inputfile preparsed data:
$api_data
 */
JNIEXPORT jint JNICALL Java_org_openvpp_jvpp_JVppImpl_${java_name}0
(JNIEnv * env, jclass clazz$args) {
    vppjni_main_t *jm = &vppjni_main;
    vl_api_${c_name}_t * mp;
    u32 my_context_id;
    int rv;
    rv = vppjni_sanity_check (jm);
    if (rv) return rv;
    my_context_id = vppjni_get_context_id (jm);
    $request_class
    $field_identifiers
    M(${c_name_uppercase}, ${c_name});
    mp->context = clib_host_to_net_u32 (my_context_id);
    $struct_setters
    S;
    return my_context_id;
}""")

def generate_jni_impl(func_list, inputfile):
    jni_impl = []
    for f in func_list:
        f_name = f['name']
        camel_case_function_name = util.underscore_to_camelcase(f_name)
        if is_manually_generated(f_name) or util.is_reply(camel_case_function_name) \
                or util.is_ignored(f_name) or util.is_notification(f_name):
            continue

        arguments = ''
        request_class = ''
        field_identifiers = ''
        struct_setters = ''
        f_name_uppercase = f_name.upper()

        if f['args']:
            arguments = ', jobject request'
            camel_case_function_name_upper = util.underscore_to_camelcase_upper(f_name)

            request_class = request_class_template.substitute(java_name_upper=camel_case_function_name_upper)

            # field identifiers
            for t in zip(f['types'], f['args']):
                jni_type = t[0]
                java_field_name = util.underscore_to_camelcase(t[1])
                jni_signature = util.jni_2_signature_mapping[jni_type]
                jni_getter = util.jni_field_accessors[jni_type]
                field_identifiers += request_field_identifier_template.substitute(
                        jni_type=jni_type,
                        java_name=java_field_name,
                        jni_signature=jni_signature,
                        jni_getter=jni_getter)

            # field setters
            for t in zip(f['c_types'], f['args'], f['lengths']):
                c_type = t[0]
                c_name = t[1]
                field_length = t[2]
                java_field_name = util.underscore_to_camelcase(c_name)

                struct_setter_template = struct_setter_templates[c_type]

                struct_setters += struct_setter_template.substitute(
                        c_name=c_name,
                        java_name=java_field_name,
                        field_length=field_length)

        jni_impl.append(jni_impl_template.substitute(
                inputfile=inputfile,
                api_data=util.api_message_to_javadoc(f),
                java_name=camel_case_function_name,
                c_name_uppercase=f_name_uppercase,
                c_name=f_name,
                request_class=request_class,
                field_identifiers=field_identifiers,
                struct_setters=struct_setters,
                args=arguments))

    return "\n".join(jni_impl)


dto_field_id_template = Template("""
    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");""")

default_dto_field_setter_template = Template("""
    (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, mp->${c_name});
""")

u32_dto_field_setter_template = Template("""
    (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, clib_net_to_host_u32(mp->${c_name}));
""")

u64_dto_field_setter_template = Template("""
    (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, clib_net_to_host_u64(mp->${c_name}));
""")

u8_array_dto_field_setter_template = Template("""
    jbyteArray ${java_name} = (*env)->NewByteArray(env, ${field_length});
    (*env)->SetByteArrayRegion(env, ${java_name}, 0, ${field_length}, (const jbyte*)mp->${c_name});
    (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
""")

# For each u64 array we get its elements. Then we convert values to host byte order.
# All changes to  jint* buffer are written to jlongArray (isCopy is set to NULL)
u64_array_dto_field_setter_template = Template("""
    {
        jlongArray ${java_name} = (*env)->NewLongArray(env, ${field_length});
        jlong * ${java_name}ArrayElements = (*env)->GetLongArrayElements(env, ${java_name}, NULL);
        unsigned int _i;
        for (_i = 0; _i < ${field_length}; _i++) {
            ${java_name}ArrayElements[_i] = clib_net_to_host_u64(mp->${c_name}[_i]);
        }
        (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
    }
""")

dto_field_setter_templates = {'u8': default_dto_field_setter_template,
                      'u16': u32_dto_field_setter_template,
                      'u32': u32_dto_field_setter_template,
                      'i32': u32_dto_field_setter_template,
                      'u64': u64_dto_field_setter_template,
                      'f64': default_dto_field_setter_template,
                      'u64[]': u64_array_dto_field_setter_template,
                      'u8[]': u8_array_dto_field_setter_template
                      }

msg_handler_template = Template("""
/**
 * Handler for ${handler_name} vpe.api message.
 * Generated based on $inputfile preparsed data:
$api_data
 */
static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp)
{
    vppjni_main_t * jm = &vppjni_main;
    JNIEnv *env = jm->jenv;

    jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V");
    jmethodID callbackMethod = (*env)->GetMethodID(env, jm->callbackClass, "on${dto_name}", "(Lorg/openvpp/jvpp/dto/${dto_name};)V");

    jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor);
    $dto_setters
    (*env)->CallVoidMethod(env, jm->callback, callbackMethod, dto);
}""")

def generate_msg_handlers(func_list, inputfile):
    handlers = []
    for f in func_list:
        handler_name = f['name']
        dto_name = util.underscore_to_camelcase_upper(handler_name)
        ref_name = util.underscore_to_camelcase(handler_name)

        if is_manually_generated(handler_name) or not util.is_reply(dto_name) or util.is_ignored(handler_name) or util.is_notification(handler_name):
            # TODO handle notifications
            continue

        dto_setters = ''
        # dto setters
        for t in zip(f['c_types'], f['types'], f['args'], f['lengths']):
            c_type = t[0]
            jni_type = t[1]
            c_name = t[2]
            field_length = t[3]

            java_field_name = util.underscore_to_camelcase(c_name)
            jni_signature = util.jni_2_signature_mapping[jni_type]
            jni_setter = util.jni_field_accessors[jni_type]

            dto_setters += dto_field_id_template.substitute(
                    java_name=java_field_name,
                    class_ref_name=ref_name,
                    jni_signature=jni_signature)

            dto_setter_template = dto_field_setter_templates[c_type]

            dto_setters += dto_setter_template.substitute(
                    java_name=java_field_name,
                    jni_signature=jni_signature,
                    c_name=c_name,
                    jni_setter=jni_setter,
                    field_length=field_length)

        handlers.append(msg_handler_template.substitute(
                inputfile=inputfile,
                api_data=util.api_message_to_javadoc(f),
                handler_name=handler_name,
                dto_name=dto_name,
                class_ref_name=ref_name,
                dto_setters=dto_setters))

    return "\n".join(handlers)


handler_registration_template = Template("""_(${upercase_name}, ${name}) \\
""")


def generate_handler_registration(func_list):
    handler_registration = ["#define foreach_vpe_api_msg \\\n"]
    for f in func_list:
        name = f['name']
        camelcase_name = util.underscore_to_camelcase(f['name'])

        if not util.is_reply(camelcase_name) or util.is_ignored(name) or util.is_notification(name):
            # TODO handle notifications
            continue

        handler_registration.append(handler_registration_template.substitute(
                name=name,
                upercase_name=name.upper()))

    return "".join(handler_registration)


jvpp_c_template = Template("""/**
 * This file contains JNI bindings for jvpp Java API.
 * It was generated by jvpp_c_gen.py based on $inputfile
 * (python representation of vpe.api generated by vppapigen).
 */

// JAVA class reference cache
$class_cache

// JNI bindings
$jni_implementations

// Message handlers
$msg_handlers

// Registration of message handlers in vlib
$handler_registration
""")

def generate_jvpp(func_list, inputfile):
    """ Generates jvpp C file """
    print "Generating jvpp C"

    class_cache = generate_class_cache(func_list)
    jni_impl = generate_jni_impl(func_list, inputfile)
    msg_handlers = generate_msg_handlers(func_list, inputfile)
    handler_registration = generate_handler_registration(func_list)

    jvpp_c_file = open("jvpp_gen.h", 'w')
    jvpp_c_file.write(jvpp_c_template.substitute(
            inputfile=inputfile,
            class_cache=class_cache,
            jni_implementations=jni_impl,
            msg_handlers=msg_handlers,
            handler_registration=handler_registration))
    jvpp_c_file.flush()
    jvpp_c_file.close()