Renaming a network interface with systemd-networkd on Ubuntu 18.04

On a Ubuntu system where I’m creating a VXLAN Proof of Concept with CloudStack I wanted to rename the interface enp5s0 to cloudbr0.

I found many documentation on the internet on how to do this with *.link files, but I was missing the golden tip, which was you need to re-generate your initramfs.

/etc/systemd/network/50-cloudbr0.link

[Match]
MACAddress=00:25:90:4b:81:54

[Link]
Name=cloudbr0

After you create this file, re-generate your initramfs:

update-initramfs -c -k all

You can now use cloudbr0 in *.network files to use it like any other network interface.

In my case this is how my interfaces look like:

1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
6: cloudbr0:  mtu 9000 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:25:90:4b:81:54 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.11/24 brd 192.168.0.255 scope global cloudbr0
       valid_lft forever preferred_lft forever
    inet6 2a00:f10:114:0:225:90ff:fe4b:8154/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591993sec preferred_lft 604793sec
    inet6 fe80::225:90ff:fe4b:8154/64 scope link 
       valid_lft forever preferred_lft forever
8: cloudbr1:  mtu 1450 qdisc noqueue state UP group default qlen 1000
    link/ether 86:fa:b6:31:6e:c1 brd ff:ff:ff:ff:ff:ff
    inet 172.16.0.11/24 brd 172.16.0.255 scope global cloudbr1
       valid_lft forever preferred_lft forever
    inet6 fe80::84fa:b6ff:fe31:6ec1/64 scope link 
       valid_lft forever preferred_lft forever
9: vxlan100:  mtu 1450 qdisc noqueue master cloudbr1 state UNKNOWN group default qlen 1000
    link/ether 56:df:29:8d:db:83 brd ff:ff:ff:ff:ff:ff

Replacing Xeon headlight bulb on Tesla Model S (2013)

Headlight bulb replacement

On my Tesla Model S (September 2013) the left headlight bulb failed and it had to be replaced. As I like to do such things myself I started to figure out how to do so.

On YouTube I found a great video to do so (you’ll find it below), but I didn’t know which bulbs I needed to order.

I called Tesla and they told me my European Model S from 2013 uses Osram Xenon D8S 25W bulbs. I searched for a local dealer and ordered them. The total was EUR 150,00 for two bulbs.

Order two

You should always replace both bulbs at the same time. As both bulbs have been on for the same amount of time they usually fail within a reasonable time from each other.

Replacing them

It took me about 90 minutes to replace the bulbs. You need to remove the front wheel to access the bulbs. I also had to replace my summer for winter tires, so I could do two jobs at once.

Video

I can try to explain everything, but there is a great video on YouTube about this:

VXLAN with VyOS and Ubuntu 18.04

VXLAN

Virtual Extensible LAN uses encapsulation technique to encapsulate OSI layer 2 Ethernet frames within layer 4 UDP datagrams. More on this can be found on the link provided.

For a Ceph and CloudStack environment I needed to set up a Proof-of-Concept using VXLAN and some refurbished hardware. The main purpose of this PoC is to verify that VXLAN works with CloudStack, Ceph and Ubuntu 18.04

VyOS

VyOS is an open source network operating system based on Debian Linux. It supports VXLAN, so using this we were able to test VXLAN in this setup.

In production a other VXLAN capable router would be used, but for a PoC VyOS works just fine running on a regular server.

Configuration

The VyOS router is connected to ‘the internet’ with one NIC and the other NIC is connected to a switch.

Using static routes a IPv4 subnet (/24) and a IPv6 subnet (/48) are routed towards the VyOS router. These are then splitted and send to multiple VLANs.

As it took me a while to configure VXLAN under VyOS

I’m only posting that configuration.

interfaces {
    ethernet eth0 {
        address 31.25.96.130/30
        address 2a00:f10:100:1d::2/64
        duplex auto
        hw-id 00:25:90:80:ed:fe
        smp-affinity auto
        speed auto
    }
    ethernet eth5 {
        duplex auto
        hw-id a0:36:9f:0d:ab:be
        mtu 9000
        smp-affinity auto
        speed auto
        vif 300 {
            address 192.168.0.1/24
            description VXLAN
            mtu 9000
        }
    vxlan vxlan1000 {
        address 10.0.0.1/23
        address 2a00:f10:114:1000::1/64
        group 239.0.3.232
        ip {
            enable-arp-accept
            enable-arp-announce
        }
        ipv6 {
            dup-addr-detect-transmits 1
            router-advert {
                cur-hop-limit 64
                link-mtu 1500
                managed-flag false
                max-interval 600
                name-server 2a00:f10:ff04:153::53
                name-server 2a00:f10:ff04:253::53
                other-config-flag false
                prefix 2a00:f10:114:1000::/64 {
                    autonomous-flag true
                    on-link-flag true
                    valid-lifetime 2592000
                }
                reachable-time 0
                retrans-timer 0
                send-advert true
            }
        }
        link eth5.300
        mtu 1500
        vni 1000
    }
    vxlan vxlan2000 {
        address 109.72.91.1/26
        address 2a00:f10:114:2000::1/64
        group 239.0.7.208
        ipv6 {
            dup-addr-detect-transmits 1
            router-advert {
                cur-hop-limit 64
                link-mtu 1500
                managed-flag false
                max-interval 600
                name-server 2a00:f10:ff04:153::53
                name-server 2a00:f10:ff04:253::53
                other-config-flag false
                prefix 2a00:f10:114:2000::/64 {
                    autonomous-flag true
                    on-link-flag true
                    valid-lifetime 2592000
                }
                reachable-time 0
                retrans-timer 0
                send-advert true
            }
        }
        link eth5.300
        mtu 1500
        vni 2000
    }
}

VLAN 300 on eth5 is used to route VNI 1000 and 2000 in their own multicast groups.

The MTU of eth5 is set to 9000 so that the encapsulated traffic of VXLAN can still be 1500 bytes.

Ubuntu 18.04

To test if VXLAN was actually working on the Ubuntu 18.04 host I made a very simple script:

ip link add vxlan1000 type vxlan id 1000 dstport 4789 group 239.0.3.232 dev vlan300 ttl 5
ip link set up dev vxlan1000
ip addr add 10.0.0.11/23 dev vxlan1000
ip addr add 2a00:f10:114:1000::101/64 dev vxlan1000

That works! I can ping 10.0.0.11 and 2a00:f10:114:1000::1 from my Ubuntu 18.04 machine!

Testing with ConfigDrive and cloud-init

cloud-init

cloud-init is a very easy way to bootstrap/configure Virtual Machines running in a cloud environment. It can read it’s metadata from various data sources and configure for public SSH keys or create users for example.

Most large clouds support cloud-init to quickly deploy new Instances.

Config Drive

Config Drive is a data source which reads a local ‘CD-Rom’ device which contains the metadata for the Virtual Machine. This allows for auto configuration of Virtual Machines without them requiring network. My main use case for Config Drive is CloudStack which has support for Config Drive since version 4.11.

I wanted to test with Config Drive outside CloudStack to test some functionality.

meta_data.json

On my laptop I spun up a Ubuntu 18.04 Virtual Machine with cloud-init installed and I attached a ISO which I created.

/home/wido/Desktop/
             cloud-init/
               openstack/
                 latest/
                   meta_data.json

In meta_data.json I put:

{
  "hostname": "ubuntu-test",
  "name": "ubuntu-test",
  "uuid": "0109a241-6fd9-46b6-955a-cd52ad168ee7",
  "public_keys": [
      "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAwPKBJDJdlvOKIfilr0VSkF9i3viwLtO8GyCpxL/8TxrGKnEg19LPLN3lwKWbbTqBZgRmbrR3bgQfM4ffPoTCxSPv44eZCF8jMPv8PxpC0yVaTcqW4Q7woD7pjdIuGVImrmEls0U8rS3uGQDx7LhFphkAh+blfUtobqzyHvqcbtVEh+drESn8AXrKd1MZfGg6OB8Xrfdr6d959uHBHFJ8pOxxppYbInxKREPb3XmZzmoNQUmqFRN/VNVTreRHAxDcPM8pEPuNmr3Vp+vDVvfpA58yr2rZ21ASB4LlNOOSEx7vnLd6uH9rsqAJOtr0ZEE29fU609i4rd6Zda2HTGQO+Q== wido@wido-laptop",
      "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC0HJ4gm7QCUZjeXh/GmCzUamABgtvOZ4GcDOA1mSGoXqGHYkG8qPa5OaURx5yqROmclTtgwkaHxjY4in6Bi7DGxlSyFQuEOKBwNCcZOrGFiRbazeh3c5jEXgoCtDynrqBjuivWTaxzl46WVglxGSy2LAaps9sAhDU+8Xfm9wBYYtjv3CDAHEe9rN3UxgGEO5K+yhoFzcI9vZl4Q9hvMZRRBBFq3vOFoLr9pyyxVUNiH9oQLubeiqDwnQqaGn48WMX9eO9KiH32eSnzlgJg8viFyonVRJq2dxJMEcrfolQhD9GR1BXNeVGDNJYuo9FX1/NeZZYJTUAwZ4jP008CfaBAnuKBfdZTkjNZlXxMQOY29VAetHI/urvh4NK17o4g4gcBpmLV+13saBIHTLXmA8ADkGZgWE6yIYEah04nBHr28vmZJBX4vpQYz9VWiZcJeEsGah/oaPkFMNQB7mXE+plz46YJ1JtozBpntir7A4cHJX3qZNz/JwOU7yAc4K3EP6fGirSvIDtQ/SJs9Gp51Wbv32E3/5ouemsGnh/ziOc8eIUJMTFWqpEG9qVi/tVJiryuvRcha6+0cZmnWnknBztzRO3Oh3JgPwECdNA/X0acXlzFlRbkQpAJx9+ADESauNaIvfQ3l+kZx3m/1eAAZpAI2ZrnvQMUa40XYksiwncM4Q== wido@wido-desktop"
  ]
}

Using mkisofs I was able to create a ISO and attached it to the VM:

mkisofs -iso-level 3 -V 'CONFIG-2' -o /var/lib/libvirt/images/cloud-init.iso /home/wido/Desktop/cloud-init

The ISO was attached to the Virtual Machine and after reboot I could log in with the user ubuntu over SSH using my pubkey!

Placement Groups with Ceph Luminous stay in activating state

Placement Groups stuck in activating

When migrating from FileStore with BlueStore with Ceph Luminuous you might run into the problem that certain Placement Groups stay stuck in the activating state.

44    activating+undersized+degraded+remapped

PG Overdose

This is a side-effect of the new PG overdose protection in Ceph Luminous.

Too many PGs on your OSDs can cause serious performance or availability problems.

You can see the amount of Placement Groups per OSD using this command:

$ ceph osd df

Increase Max PG per OSD

The default value is a maximum of 200 PGs per OSD and you should stay below that! However, if you are hit by PGs in the activating state you can set this configuration value:

[global]
mon_max_pg_per_osd = 500

Then restart the OSDs and MONs which are serving the affected by this.

Usually you shouldn’t run into this, but if this hits you in the middle of a migration or upgrade this might save you.

Building a Lithium battery for my electric scooter

Disclaimer

I am not a battery expert nor am I claiming to be one! I got all my information from the internet and I think designed and build my battery correctly.

Always use common sense and do not blindly trust the information I (or anybody else) am/is putting on the internet.

Novox C20

In 2010 I bought a Novox C20 25km/h electric scooter. Novox was (they are gone) Dutch manufacturer of electric scooters. It seems that they (partially) bought the scooters in China and rebranded them to their brand Novox. It seems that many parts of it were actually from the Chinese manufacturer Tysong.

I previously wrote a dutch post and a english post about this scooter.

The model I have is the 25km/h version equipped with a 2.5kW electric motor with 4 12V 40Ah batteries.

Realistically this scooter had a range of about 35 ~ 40km before running out of juice.

25km/h

In the Netherlands we have two types of scooters. We call them snorfiets (25km/h, blue license plate) or bromfiets (45km/h, yellow plate). My scooter is a snorfiets which means I do not have to wear a helmet.

The primary purpose for the scooter is going to the beach in summer, use it when it’s nice weather and when I want to go somewhere which is a bit to far to go by bicycle.

4x 12V 40Ah

Originally the scooter had 4 12V 40Ah batteries which (in theory) is roughly 1.9kWh of capacity. These were lead acid batteries and each weight 12kg, so the total weight of the batteries was nearly 50kg.

Over the years the range and performance declined until the batteries died completely. After reading about DIY batteries using 18650 Lithium batteries I decided to build my own!

Designing the battery

After I decided to design and build my own battery I went out to gather information. I watched endless hours of videos on YouTube and I also bought a book:

With the information from YouTube, that book and other sources I designed a 14S15P battery.

14S15P

A 14S15P configuration means 14 Cells in Series and 15 Cells Parallel. The total amount of cells would be: 210 (14*15).

The nominal voltage of the battery would be 50.4V (3.6*14) and the maximum voltage 58.8V (4.2*14).

The existing controller in the scooter would not be able to handle that voltage so I would have to swap the controller. That’s something I’ll explain in a upcoming post.

On AliExpress I searched for 18650 battery cell holders for a 14S5P pack and ordered three sets. Combining them would give me a 14S15P battery pack which would physically fit in my scooter.

Samsung INR18650-35E

During my search for the correct cell I selected the Samsung INR18650-35E cell. This cell has a capacity of 3.500mAh and 210 of them would sum up to a total capacity of 2646Wh (3.6 * 3500 * 210 / 1000) or 2.6kWh for my scooter.

Each cell can deliver 10A of current so 15 of them in parallel would be able to provide 150A of current. With a nominal voltage of 50V that would be 7500W or 7.5kW.

As the motor in my scooter is rated for 2.5kW I would only need 50A of current. 50A or 2.5kW was my design target. Anything above it would be a bonus.

After searching I found the Dutch shop Nkon which has a good reputation and was able to deliver the INR18650-35E for a good price. So I ordered 220 cells (10 spare).

Next to the cells I ordered 15m of 0.15mm Nickel strip to use when spotwelding all the cells together.

Battery Management System

The next important thing was a Battery Management System (BMS). A BMS makes sure the cells stay in a healthy state by doing various things:

  • Protecting them against under -or overvoltage
  • Protecting against a high charge or discharge current
  • Making sure the cells are balanced

There are good and bad BMS out there. At first I thought I’d buy one on AliExpress but after reading on the dutch forum of Tweakers in the topic about electric scooters I purchased a TinyBMS s516 from Energus Power Solutions. It’s not the cheapest, but it does do its job: Protecting my 210 cells!

In addition to the BMS I ordered:

  • USB programming cable
  • Temperature sensors
  • Bluetooth module
  • Balancing Wires

Spotwelding

To connect all the cells together you need to use spotwelding as soldering would damage the cells badly due to the heat.

At first I bought a Sunko 709A spotwelder on AliExpress. Short story: A waste of money!

After searching a bit more I found a Arduino based spotwelder made by Malectrics in Germany.

This spotwelder works great with a Bosch 12V 44A 440A car battery. I made a short video and put it on YouTube.

In addition a picture of the spotwelder connected to the battery.

I never used a spotwelder before nor did I build a battery. I started with building a small 3S5P 12V battery to test my welding skills.

Safety First! Remember to wear gloves and eye protection when working with a spotwelder and/or batteries. Also make sure there are no loose tools or wires on your workplace and again, use common sense!

I am now using this battery as a DIY powerbank with a 12V and USB output.

Building it

After I got some practice on building the small battery I just started working on the big battery. Cutting nickel to length and I started to build the blocks.

This took a lot of time, probably over 40 hours as I slowly build my first big battery and still needed to learn.

I ended up stacking two strips of 0.15mm nickel on top of each other to handle my 50A target current.

You can also see the balancing wires of the BMS go to the positive terminal of each series. The manual of the BMS has clear instructions on how to wire them.

From the Nickel I went to 8AWG wire which I connected to a SB50 connector. Using that connector I would connect the battery Plus and Minus to the BMS and the BMS to the controller. The SB50 connector is rated for 50A and thus meets my requirements. I got the connectors from a local supplier.

Using a tape, lexan and some screw the end result (without BMS) looked like this:

And with my TinyBMS connected to it:

The tiny red wires are the balancing wires used by the BMS to monitor the cells in series and balance charge them when needed.

BMS settings

Using a Windows tool you can monitor and configure the BMS using a USB cable (which you have to buy).

There are various settings you can change, but the main values I set were:

  • Low voltage cut-off: 3.25V
  • High voltage cut-off: 4.15V
  • Maximum discharge currect: 75A

Although the cells can range from 3.2V to 4.2V I decided to take a 0.05V safety margin to increase the lifetime of the cells. I might lower the maximum voltage per cell to 4.1V in the future, but that’s something I still have to decide.

Using the BMS tool you can see the voltage of each individual series of cells and see them change while charging the battery.

Charging and Charger

To charge this battery I also needed a new charger as the old charger was not suited for Lithium and the voltage was too low.

I bought a 58.8V/5A charger on AliExpress which I’m using for now. It does seem to do the job just fine for now. The BMS isn’t complaining (yet).

Using XT90 connectors I connect my charger to the BMS although the scooter has a XLR input. But internally I’m connecting all those wires using XT90 connectors.

I got my XT90 connectors at Hobbyking as they also have a local warehouse in the Netherlands and have a good reputation.

Weight

The four old batteries had a combined weight of nearly 50kg where the new battery weighs slightly less than 12kg with everything connected.

That’s a weight reduction of 38kg! That will benefit acceleration, handling and range positively.

End result

The end result is a 2,6kWh battery consisting out of 210 cells connected in a 14S15P configuration with a nominal/maximum voltag of 50.4V/58.8V.

My initial calculations and tests tell me that when driving ~30km/h (Yes, I did some tuning!) the motor draws about 20A. That’s about 1000W at 50V.

Since the battery is 2600Wh I should be able to drive for roughly 2 hours and 30 minutes at 30km/h before the battery is empty.

30km for 2.5 hours would mean a range of 75km which is more then enough for my driving habits with this scooter.

With the original batteries I had a range of roughly 40km, but I never tried if it actually got that far. I think I improved the range with nearly 75% with this new battery.

In this picture you see the battery installed in the scooter and hooked up to the controller. A post about the controller and connecting it all will follow later.

Costs

The total costs for the cells, BMS, wire and connectors is about EUR 940,00 which can be broken down into:

  • Cells: EUR 650,00
  • BMS: EUR 200,00
  • Wires: EUR 40,00
  • Connectors: EUR 50,00
  • Time: 50 hours!

For me it was a cool project and something I could learn a lot from.

BIG Thanks

There are a few people and sources I have to thank big-time for their information on the internet.

  • jehugarcia on YouTube
  • User flippy.nl on Tweakers.net
  • All the other people sharing information!

I wouldn’t have been able to do this without those people. Thanks!

Performance

The battery is currently installed in the scooter and I’m still in the process of fine-tuning the controller.

My initial test drives tell me that the performance improved a lot! Acceleration to 30km/h is a lot quicker and I feel the motor has a lot more torque than it had before. This is mainly due to the battery being able to provide the current required to power the motor. The weight reduction can also be felt clearly and also contributes to the improved acceleration.

One of the cool things is that the scooter now has regenerative breaking thanks to the new controller.

A post regarding the controller and connecting it all will follow. Stay tuned!

Quick overview of Ceph version running on OSDs

When checking a Ceph cluster it’s useful to know which versions you OSDs in the cluster are running.

There is a very simple on-line command to do this:

ceph osd metadata|jq '.[].ceph_version'|sort|uniq -c

Running this on a cluster which is currently being upgraded to Jewel to Luminous it shows:

     10 "ceph version 10.2.6 (656b5b63ed7c43bd014bcafd81b001959d5f089f)"
   1670 "ceph version 10.2.7 (50e863e0f4bc8f4b9e31156de690d765af245185)"
    426 "ceph version 10.2.9 (2ee413f77150c0f375ff6f10edd6c8f9c7d060d0)"
     66 "ceph version 12.2.1 (3e7492b9ada8bdc9a5cd0feafd42fbca27f9c38e) luminous (stable)"

So 66 OSDs are running Luminous and 2106 OSDs are running Jewel.

Starting with Luminous there is also this command:

ceph features

This shows us all daemon and client versions in the cluster:

{
    "mon": {
        "group": {
            "features": "0x1ffddff8eea4fffb",
            "release": "luminous",
            "num": 5
        }
    },
    "osd": {
        "group": {
            "features": "0x7fddff8ee84bffb",
            "release": "jewel",
            "num": 426
        },
        "group": {
            "features": "0x1ffddff8eea4fffb",
            "release": "luminous",
            "num": 66
        }
    },
    "client": {
        "group": {
            "features": "0x7fddff8ee84bffb",
            "release": "jewel",
            "num": 357
        },
        "group": {
            "features": "0x1ffddff8eea4fffb",
            "release": "luminous",
            "num": 7
        }
    }
}

Getting connection URL from (Flask) SQLAlchemy

While working with Flask‘s SQLAlchemy extension I ran into the case where I wanted to obtain the host I was connected to combined with the username and password.

As I didn’t want to parse the URL I provided earlier to SQLAlchemy (and I didn’t have it available at that part of the code) I looked for a method to obtain it from SQLAlchemy.

It turns out this is not (or poorly) documented, but the Engine object has a attribute called url.

>>> db.engine.url
mysql+mysqlconnector://sqlmanager:***@localhost/sqlmanager?charset=utf8
>>>

This is actually a sqlalchemy.engine.url.URL object which contains attributes with the connection information:

>>> url = db.engine.url
>>> url.host
'localhost'
>>> url.username
'sqlmanager'
>>> url.password
'supersecretpassword'
>>> url.database
'sqlmanager'
>>> 

This was the information I needed and allowed me to use this information elsewhere.

4 years of Tesla Model S ownership

4 years

Last year I wrote a blogpost about 3 years of Model S ownership. Well, it’s a year later, so it’s time to write a post about 4 years of ownership 🙂

Do I still like my Model S? Best car I’ve ever owned!

The numbers

Some numbers about last year:

  • Last year my Model S had driven 141.466km, and another year later the counter is at 187.058km. A total of 45.592km in one year.
  • My average energy consumption still hovers somewhere around 200Wh/km.
  • A full charge (100%) yields 380km and a 90% charge 341km.

Roadtrips

The Model S is still my only car and I still take roadtrips with it. This year I took a few roadtrips:

  • 1.400km roadtrip to the UK and back (SuperChargers at the channel tunnel, yay!)
  • 3.500km to Milan/Rome for a summer vacation.
  • 2.000km roadtrip to Munich for Oktoberfest.

Pictures

A few pictures how my Model S still looks like after 4 years. Personally I can’t find any real signs of wear on the exterior nor interior.

Using a Destination Charger in Tuscany, Italy

Tesla charging stations (22kW) at our new office

Break-in in Rome

While parked in Rome people broke in to the car and stole clothing. And punctured the rear wheels…

Accident in Milan

On the way back from Rome a small truck changed lane and hit us. Nobody got hurt, just some damage to the car.

Conclusion

After 4 years my Model S is still doing just fine! No wear and tear, no severe battery degradation or major failures. The car just works!

I’ll probably keep this Model S until it turns 5 next year and buy a new one. Will I buy a Tesla again? Yes, no doubt!

Using the new dashboard in ceph-mgr

The upcoming Ceph Luminous (12.2.0) release features the new ceph-mgr daemon which has a few default plugins. One of these plugins is a dashboard to give you a graphical overview of your cluster.

Enabling Module

To enable the dashboard you have to enable the module in your /etc/ceph/ceph.conf on all machines running the ceph-mgr daemon. These are usually your Monitors.

Add this to the configuration:

[mgr]
mgr_modules = dashboard

Don’t restart your ceph-mgr daemon yet. More configuration changes have to be made first.

Setting server address and port

A server address and optionally a port have to be configured as a config-key.

By setting the value to :: the dashboard will be available on all IPv4 and IPv6 addresses on port 7000 (default):

ceph config-key put mgr/dashboard/server_addr ::

Restart daemons

Now restart all ceph-mgr daemons on your hosts:

systemctl restart ceph-mgr@

Accessing the dashboard

The default port is 7000, so now go to the IP-Address of the active ceph-mgr and open the see the dashboard.

You can find the active ceph-mgr in the ceph status:

root@alpha:~# ceph -s
  cluster:
    id:     30d838cd-955f-42e5-bddb-5609e1c880f8
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum alpha,bravo,charlie
    mgr: charlie(active), standbys: alpha, bravo
    osd: 3 osds: 3 up, 3 in
 
  data:
    pools:   1 pools, 64 pgs
    objects: 0 objects, 0 bytes
    usage:   3173 MB used, 27243 MB / 30416 MB avail
    pgs:     64 active+clean
 
root@alpha:~#

In this case charlie is the active mgr which in my case has IPv6 Address 2001:db8::102.

Point your browser to: http://[2001:db8::102]:7000 and you will see the dashboard.