When DataFrame are joined using only some of the levels of a MultiIndex, the extra levels will be dropped from the resulting join. To preserve those levels, use DataFrame.reset_index() on those level names to move those levels to columns prior to the join.
In [113]: right = pd.DataFrame({"v2": [100 * i for i inrange(1, 7)]}, index=rightindex)
In [114]: right Out[114]: v2 abc xy a x 100 y 200 b x 300 y 400 c x 500 y 600
In [115]: left.join(right, on=["abc", "xy"], how="inner") Out[115]: v1 v2 abc xy num a x 10100 21100 y 12200 23200 b x 14300 25300 y 16400 27400 c x 18500 29500 y 110600 211600
''' abc xy num v1 v2 0 a x 1 0 100 1 a x 2 1 100 2 a y 1 2 200 3 a y 2 3 200 4 b x 1 4 300 5 b x 2 5 300 6 b y 1 6 400 7 b y 2 7 400 8 c x 1 8 500 9 c x 2 9 500 10 c y 1 10 600 11 c y 2 11 600 '''